Compare commits

..

44 Commits

Author SHA1 Message Date
Samuel Attard
a80f7fc81f build: enable JS semicolons (#22787) 2020-03-23 09:18:46 -07:00
trop[bot]
83e7b00e7c fix: prevent crash in ListValue v8 converter when conversion fails (#22757) 2020-03-19 14:02:15 -07:00
trop[bot]
eabb872345 test: no need to loadURL in menu test (#22767)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2020-03-19 16:34:27 +09:00
trop[bot]
b12090a129 fix: remove bad usages of for-in and guard against it (#22729)
* fix: remove bad usages of for-in and guard against it

* Apply suggestions from code review

Co-Authored-By: Samuel Maddock <samuel.maddock@gmail.com>

* Apply suggestions from code review

Co-Authored-By: Jeremy Apthorp <jeremya@chromium.org>

* Update remote.js

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
Co-authored-by: Samuel Attard <sattard@slack-corp.com>
Co-authored-by: Samuel Maddock <samuel.maddock@gmail.com>
Co-authored-by: Jeremy Apthorp <jeremya@chromium.org>
2020-03-18 09:37:57 +09:00
trop[bot]
0bf12e4e76 chore: don't delete nightly tag after draft (#22625)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-03-10 17:04:43 +09:00
trop[bot]
411b9f2ae7 build: upload sentry src bundles to symbol S3 bucket (#22618)
Co-authored-by: Samuel Attard <sattard@slack-corp.com>
2020-03-09 16:24:50 -07:00
Shelley Vohr
a632f1f1b7 fix: prevent potential modal window close segfault (#22540) 2020-03-04 22:24:34 -08:00
Cheng Zhao
2543517443 fix: destroy node platform after destroying wrappers (#22536)
Co-authored-by: Cheng Zhao <zcbenz@electronjs.org>
2020-03-05 13:07:17 +09:00
Cheng Zhao
5736df41c8 fix: properly forward properties to webview (#22512)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-03-05 10:06:53 +09:00
trop[bot]
fbbe8afc3e fix: bail early if no printers on the network (#22517)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-03-04 16:19:16 +09:00
loc
6fdd055642 fix: port CL that fixes ARIA tree impl for macOS (#22424) 2020-03-02 18:25:58 +00:00
Electron Bot
0f082ecd5c Bump v7.1.14 2020-02-27 16:07:16 -08:00
Jeremy Apthorp
f29a600fd8 fix: OOB access in ReadableStream::Close (#22435)
* fix: move ReadableStream requests onto the stack before iteration

* fix: Streams: Convert state DCHECKs to CHECKs

* fix patches
2020-02-27 15:53:01 -08:00
Samuel Attard
1bde50636f fix: backport v8 patch for type inference issue (#22428)
* fix: backport v8 patch for type inference issue

* Update .patches
2020-02-27 13:49:12 -08:00
Jeremy Apthorp
7d739a3608 fix: backport fix for icu crash (#22420) 2020-02-27 12:50:18 -08:00
trop[bot]
8d3f0f47c1 fix: pass safeDialogs preference properly (#22376)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-02-26 22:04:24 +00:00
trop[bot]
dfd338d8bf test: disable clipboard tests for WOA (#22389)
Co-authored-by: Cheng Zhao <zcbenz@github.com>
2020-02-26 16:45:50 +09:00
Jeremy Apthorp
dd242f4b28 fix: emit will-navigate for sandboxed contents (#22188) (#22329)
* fix: emit will-navigate for sandboxed contents (#22188)

* Update atom_sandboxed_renderer_client.cc

* Update atom_sandboxed_renderer_client.cc

* Update api-browser-window-spec.ts
2020-02-26 10:37:43 +09:00
Robo
4c61f62149 fix: crash in seccomp-bpf sandbox with glibc 2.31 (#22338) 2020-02-24 21:13:01 +00:00
trop[bot]
fd654afae9 docs: clean up protocol docs (#22307)
* docs: clean up protocol docs

* Fix capitalization

Co-authored-by: Mark Lee <malept@users.noreply.github.com>
2020-02-24 12:28:20 +09:00
Cheng Zhao
cadddfec23 fix: disable remote layer APIs in MAS build (7-1-x) (#22299)
* fix: add patch to disable remote layer APIs

* fix: use --disable-gpu-memory-buffer-compositor-resources for MAS build
2020-02-24 12:09:25 +09:00
John Kleinschmidt
0e8aa5c252 ci: update to CircleCI v2 config and APIs (#22310)
* ci: Update to CircleCI v2 config and APIs

This reverts commit e6807b387c.

* build: update release build endpoint from /jobs to /job (#21232)


(cherry picked from commit 41f1569c46)

* chore: workflows and pipeline state were split in the circle API (#21441)


(cherry picked from commit ec0edb757a)

Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-02-21 15:03:33 -05:00
Electron Bot
edac24c9be Bump v7.1.13 2020-02-20 09:38:02 -08:00
Electron Bot
0251f5aa93 Revert "Bump v7.1.13"
This reverts commit cccd3778f8.
2020-02-19 15:31:15 -08:00
Electron Bot
cccd3778f8 Bump v7.1.13 2020-02-19 15:28:25 -08:00
trop[bot]
680f3432ae build: only strip binaries on linux (#22288)
Co-authored-by: John Kleinschmidt <jkleinsc@github.com>
2020-02-19 18:25:59 -05:00
John Kleinschmidt
420fb46121 Revert "Bump v7.1.13"
This reverts commit d7247bc77d.
2020-02-19 11:19:33 -05:00
trop[bot]
6ef605eaa2 doc: remove accidental deprecation (#22266)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2020-02-19 13:44:26 +09:00
Electron Bot
d7247bc77d Bump v7.1.13 2020-02-18 12:21:15 -08:00
trop[bot]
5e6e118595 build: preserve timestamps when stripping files (#22094) (#22261)
* build: preserve timestamps when stripping files

Resolves an issue where the binaries in mksnapshot.zip were not getting stripped.

Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>
(cherry picked from commit 5e49aafe55)

Co-authored-by: John Kleinschmidt <jkleinsc@github.com>
2020-02-18 15:19:43 -05:00
trop[bot]
b1f19970e7 fix: don't include breakpad_symbols dir in dsym.zip (#22220)
Co-authored-by: John Kleinschmidt <jkleinsc@github.com>
2020-02-18 12:23:01 -05:00
Shelley Vohr
c30ecc38d0 fix: crash on custom printing margins (#22164) (#22187) 2020-02-17 09:44:53 +09:00
trop[bot]
14d9b8a8b4 fix: no-arg console.log is undefined (#22173)
Co-authored-by: Shelley Vohr <codebytere@github.com>
2020-02-14 15:32:47 +09:00
Shelley Vohr
f0d872947d chore: default to sys printer and prevent bad default crash (#22012) 2020-02-12 17:45:53 +00:00
Cheng Zhao
40a212e132 fix: make webRequest work with WebSocket (7-1-x) (#22141)
* fix: make webRequest work with WebSocket

* test: install modules for spec-main
2020-02-12 15:14:57 +09:00
Shelley Vohr
cb9b3b29af fix: keep references to active menus created by api Menu (#22151)
Without this such menus would be destroyed by js garbage collector even
when they are still displayed.

Co-authored-by: CezaryKulakowski <50166166+CezaryKulakowski@users.noreply.github.com>
2020-02-12 09:00:03 +09:00
trop[bot]
da65c881e2 ci: strip mksnapshot binaries on Linux (#22156)
Related to #21086.

Co-authored-by: Alexey Kuzmin <alex.s.kuzmin@gmail.com>
2020-02-11 15:45:08 -05:00
Electron Bot
aa5e51dbe0 Bump v7.1.12 2020-02-10 11:08:32 -08:00
trop[bot]
b5d38bfff0 fix: flash plugin (#22109)
* fix: flash plugin

Fixes https://github.com/electron/electron/issues/20744

* cleanup

* fix linting issue

Co-authored-by: t57ser <seve@live.at>
2020-02-10 10:49:43 +09:00
trop[bot]
a3054443f4 fix: use a WeakPtr so we do not UAF the store in FunctionLifetimeMonitor (#22112)
Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com>
2020-02-10 10:47:43 +09:00
Jeremy Apthorp
1f631ca337 ci: fix ELECTRON_OUT_DIR (#21994) (#22020) 2020-02-05 10:31:03 +09:00
Jeremy Apthorp
e7576019e9 fix: use legacy base::Value serializers for ipc (7-1-x) (#21922) 2020-01-31 11:38:55 -08:00
trop[bot]
77448c75c6 fix: return path from netLog.stopLogging (#21988)
Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>
2020-01-31 11:21:52 -08:00
Robo
538ebabf7e fix: crash with icu when input locale is invalid (#21969)
* fix: crash with icu when input locale is invalid

Backports https://github.com/unicode-org/icu/pull/632

* spec: add regression tests
2020-01-31 14:35:54 +09:00
363 changed files with 15140 additions and 12791 deletions

View File

@@ -1,3 +1,46 @@
version: 2.1
parameters:
upload-to-s3:
type: string
default: '1'
run-lint:
type: boolean
default: true
run-build-linux:
type: boolean
default: true
run-build-mac:
type: boolean
default: true
run-linux-x64-publish:
type: boolean
default: false
run-linux-ia32-publish:
type: boolean
default: false
run-linux-arm-publish:
type: boolean
default: false
run-linux-arm64-publish:
type: boolean
default: false
run-osx-publish:
type: boolean
default: false
run-mas-publish:
type: boolean
default: false
# The config expects the following environment variables to be set:
# - "SLACK_WEBHOOK" Slack hook URL to send notifications.
#
@@ -88,22 +131,31 @@ env-enable-sccache: &env-enable-sccache
env-send-slack-notifications: &env-send-slack-notifications
NOTIFY_SLACK: true
env-global: &env-global
ELECTRON_OUT_DIR: Default
env-linux-medium: &env-linux-medium
<<: *env-global
NUMBER_OF_NINJA_PROCESSES: 3
env-linux-2xlarge: &env-linux-2xlarge
<<: *env-global
NUMBER_OF_NINJA_PROCESSES: 34
env-linux-2xlarge-release: &env-linux-2xlarge-release
<<: *env-global
NUMBER_OF_NINJA_PROCESSES: 16
env-machine-mac: &env-machine-mac
<<: *env-global
NUMBER_OF_NINJA_PROCESSES: 6
env-mac-large: &env-mac-large
<<: *env-global
NUMBER_OF_NINJA_PROCESSES: 18
env-mac-large-release: &env-mac-large-release
<<: *env-global
NUMBER_OF_NINJA_PROCESSES: 8
env-disable-crash-reporter-tests: &env-disable-crash-reporter-tests
@@ -346,7 +398,9 @@ step-electron-chromedriver-build: &step-electron-chromedriver-build
command: |
cd src
ninja -C out/Default chrome/test/chromedriver -j $NUMBER_OF_NINJA_PROCESSES
electron/script/strip-binaries.py --target-cpu="$TARGET_ARCH" --file $PWD/out/Default/chromedriver
if [ "`uname`" == "Linux" ]; then
electron/script/strip-binaries.py --target-cpu="$TARGET_ARCH" --file $PWD/out/Default/chromedriver
fi
ninja -C out/Default electron:electron_chromedriver_zip
step-electron-chromedriver-store: &step-electron-chromedriver-store
@@ -466,6 +520,7 @@ step-mksnapshot-build: &step-mksnapshot-build
name: mksnapshot build
command: |
cd src
ninja -C out/Default electron:electron_mksnapshot -j $NUMBER_OF_NINJA_PROCESSES
if [ "`uname`" != "Darwin" ]; then
if [ "$TARGET_ARCH" == "arm" ]; then
electron/script/strip-binaries.py --file $PWD/out/Default/clang_x86_v8_arm/mksnapshot
@@ -473,6 +528,7 @@ step-mksnapshot-build: &step-mksnapshot-build
electron/script/strip-binaries.py --file $PWD/out/Default/clang_x64_v8_arm64/mksnapshot
else
electron/script/strip-binaries.py --file $PWD/out/Default/mksnapshot
electron/script/strip-binaries.py --file $PWD/out/Default/v8_context_snapshot_generator
fi
fi
ninja -C out/Default electron:electron_mksnapshot_zip -j $NUMBER_OF_NINJA_PROCESSES
@@ -489,6 +545,8 @@ step-maybe-generate-breakpad-symbols: &step-maybe-generate-breakpad-symbols
if [ "$GENERATE_SYMBOLS" == "true" ]; then
cd src
ninja -C out/Default electron:electron_symbols
cd out/Default/breakpad_symbols
find . -name \*.sym -print0 | xargs -0 npx @sentry/cli@1.51.1 difutil bundle-sources
fi
step-maybe-zip-symbols: &step-maybe-zip-symbols
@@ -661,7 +719,7 @@ steps-lint: &steps-lint
chromium_revision="$(grep -A1 chromium_version src/electron/DEPS | tr -d '\n' | cut -d\' -f4)"
gn_version="$(curl -sL "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/DEPS?format=TEXT" | base64 -d | grep gn_version | head -n1 | cut -d\' -f4)"
cipd ensure -ensure-file - -root . <<-CIPD
cipd ensure -ensure-file - -root . \<<-CIPD
\$ServiceURL https://chrome-infra-packages.appspot.com/
@Subdir src/buildtools/linux64
gn/gn/linux-amd64 $gn_version
@@ -961,7 +1019,6 @@ steps-tests: &steps-tests
ELECTRON_DISABLE_SECURITY_WARNINGS: 1
command: |
cd src
export ELECTRON_OUT_DIR=Default
(cd electron && node script/yarn test -- --ci --enable-logging)
- run:
name: Check test results existence
@@ -994,7 +1051,6 @@ steps-test-nan: &steps-test-nan
name: Run Nan Tests
command: |
cd src
export ELECTRON_OUT_DIR=Default
node electron/script/nan-spec-runner.js
steps-test-node: &steps-test-node
@@ -1009,7 +1065,6 @@ steps-test-node: &steps-test-node
name: Run Node Tests
command: |
cd src
export ELECTRON_OUT_DIR=Default
node electron/script/node-spec-runner.js junit
- store_test_results:
path: src/junit
@@ -1018,7 +1073,6 @@ chromium-upgrade-branches: &chromium-upgrade-branches
/chromium\-upgrade\/[0-9]+/
# List of all jobs.
version: 2
jobs:
# Layer 0: Lint. Standalone.
lint:
@@ -1138,6 +1192,8 @@ jobs:
<<: *env-linux-2xlarge-release
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
<<: *env-release-build
<<: *env-enable-sccache
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
<<: *steps-electron-build-for-publish
linux-ia32-debug:
@@ -1188,6 +1244,8 @@ jobs:
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
<<: *env-ia32
<<: *env-release-build
<<: *env-enable-sccache
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
<<: *steps-electron-build-for-publish
linux-arm-debug:
@@ -1238,7 +1296,9 @@ jobs:
<<: *env-linux-2xlarge-release
<<: *env-arm
<<: *env-release-build
<<: *env-enable-sccache
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
<<: *steps-electron-build-for-publish
linux-arm64-debug:
@@ -1305,7 +1365,9 @@ jobs:
<<: *env-linux-2xlarge-release
<<: *env-arm64
<<: *env-release-build
<<: *env-enable-sccache
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm64=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True'
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
<<: *steps-electron-build-for-publish
osx-testing:
@@ -1363,7 +1425,9 @@ jobs:
environment:
<<: *env-mac-large-release
<<: *env-release-build
<<: *env-enable-sccache
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
<<: *steps-electron-build-for-publish
mas-testing:
@@ -1427,7 +1491,9 @@ jobs:
<<: *env-mac-large-release
<<: *env-mas
<<: *env-release-build
<<: *env-enable-sccache
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_boto=True --custom-var=checkout_requests=True'
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
<<: *steps-electron-build-for-publish
# Layer 3: Tests.
@@ -1683,11 +1749,56 @@ jobs:
workflows:
version: 2
# The publish workflows below each contain one job so that they are
# compatible with how sudowoodo works today. If these workflows are
# changed to have multiple jobs, then scripts/release/ci-release-build.js
# will need to be updated and there will most likely need to be changes to
# sudowoodo
publish-x64-linux:
when: << pipeline.parameters.run-linux-x64-publish >>
jobs:
- linux-x64-publish:
context: release-env
publish-ia32-linux:
when: << pipeline.parameters.run-linux-ia32-publish >>
jobs:
- linux-ia32-publish:
context: release-env
publish-arm-linux:
when: << pipeline.parameters.run-linux-arm-publish >>
jobs:
- linux-arm-publish:
context: release-env
publish-arm64-linux:
when: << pipeline.parameters.run-linux-arm64-publish >>
jobs:
- linux-arm64-publish:
context: release-env
publish-osx:
when: << pipeline.parameters.run-osx-publish >>
jobs:
- osx-publish:
context: release-env
publish-mas:
when: << pipeline.parameters.run-mas-publish >>
jobs:
- mas-publish:
context: release-env
lint:
when: << pipeline.parameters.run-lint >>
jobs:
- lint
build-linux:
when: << pipeline.parameters.run-build-linux >>
jobs:
- linux-checkout-fast
- linux-checkout-and-save-cache
@@ -1754,6 +1865,7 @@ workflows:
- linux-checkout-fast
build-mac:
when: << pipeline.parameters.run-build-mac >>
jobs:
- mac-checkout-fast
- mac-checkout-and-save-cache
@@ -1808,11 +1920,11 @@ workflows:
- master
- *chromium-upgrade-branches
jobs:
- linux-checkout
- linux-checkout-fast
- linux-x64-release:
requires:
- linux-checkout
- linux-checkout-fast
- linux-x64-release-tests:
requires:
- linux-x64-release
@@ -1824,7 +1936,7 @@ workflows:
- linux-x64-release
- linux-x64-chromedriver:
requires:
- linux-checkout
- linux-checkout-fast
- linux-x64-release-summary:
requires:
- linux-x64-release
@@ -1834,7 +1946,7 @@ workflows:
- linux-ia32-release:
requires:
- linux-checkout
- linux-checkout-fast
- linux-ia32-release-tests:
requires:
- linux-ia32-release
@@ -1846,7 +1958,7 @@ workflows:
- linux-ia32-release
- linux-ia32-chromedriver:
requires:
- linux-checkout
- linux-checkout-fast
- linux-ia32-release-summary:
requires:
- linux-ia32-release
@@ -1856,10 +1968,10 @@ workflows:
- linux-arm-release:
requires:
- linux-checkout
- linux-checkout-fast
- linux-arm-chromedriver:
requires:
- linux-checkout
- linux-checkout-fast
- linux-arm-release-summary:
requires:
- linux-arm-release
@@ -1868,10 +1980,10 @@ workflows:
- linux-arm64-release:
requires:
- linux-checkout
- linux-checkout-fast
- linux-arm64-chromedriver:
requires:
- linux-checkout
- linux-checkout-fast
- linux-arm64-release-summary:
requires:
- linux-arm64-release
@@ -1887,11 +1999,11 @@ workflows:
- master
- *chromium-upgrade-branches
jobs:
- mac-checkout
- mac-checkout-fast
- osx-release:
requires:
- mac-checkout
- mac-checkout-fast
- osx-release-tests:
requires:
- osx-release
@@ -1903,7 +2015,7 @@ workflows:
- osx-release
- osx-chromedriver:
requires:
- mac-checkout
- mac-checkout-fast
- osx-release-summary:
requires:
- osx-release
@@ -1913,7 +2025,7 @@ workflows:
- mas-release:
requires:
- mac-checkout
- mac-checkout-fast
- mas-release-tests:
requires:
- mas-release
@@ -1925,7 +2037,7 @@ workflows:
- mas-release
- mas-chromedriver:
requires:
- mac-checkout
- mac-checkout-fast
- mas-release-summary:
requires:
- mas-release

View File

@@ -6,9 +6,11 @@
"browser": true
},
"rules": {
"semi": ["error", "always"],
"no-var": "error",
"no-unused-vars": 0,
"no-global-assign": 0,
"guard-for-in": 2,
"@typescript-eslint/no-unused-vars": ["error", {
"vars": "all",
"args": "after-used",

View File

@@ -1289,12 +1289,18 @@ dist_zip("electron_chromedriver_zip") {
]
}
mksnapshot_deps = [
":licenses",
"//tools/v8_context_snapshot:v8_context_snapshot_generator",
"//v8:mksnapshot($v8_snapshot_toolchain)",
]
group("electron_mksnapshot") {
public_deps = mksnapshot_deps
}
dist_zip("electron_mksnapshot_zip") {
data_deps = [
"//v8:mksnapshot($v8_snapshot_toolchain)",
"//tools/v8_context_snapshot:v8_context_snapshot_generator",
":licenses",
]
data_deps = mksnapshot_deps
outputs = [
"$root_build_dir/mksnapshot.zip",
]

View File

@@ -1 +1 @@
7.1.11
7.1.14

View File

@@ -1,48 +1,48 @@
import { app, dialog, BrowserWindow, shell, ipcMain } from 'electron'
import * as path from 'path'
import { app, dialog, BrowserWindow, shell, ipcMain } from 'electron';
import * as path from 'path';
let mainWindow: BrowserWindow | null = null
let mainWindow: BrowserWindow | null = null;
// Quit when all windows are closed.
app.on('window-all-closed', () => {
app.quit()
})
app.quit();
});
function decorateURL (url: string) {
// safely add `?utm_source=default_app
const parsedUrl = new URL(url)
parsedUrl.searchParams.append('utm_source', 'default_app')
return parsedUrl.toString()
const parsedUrl = new URL(url);
parsedUrl.searchParams.append('utm_source', 'default_app');
return parsedUrl.toString();
}
// Find the shortest path to the electron binary
const absoluteElectronPath = process.execPath
const relativeElectronPath = path.relative(process.cwd(), absoluteElectronPath)
const absoluteElectronPath = process.execPath;
const relativeElectronPath = path.relative(process.cwd(), absoluteElectronPath);
const electronPath = absoluteElectronPath.length < relativeElectronPath.length
? absoluteElectronPath
: relativeElectronPath
: relativeElectronPath;
const indexPath = path.resolve(app.getAppPath(), 'index.html')
const indexPath = path.resolve(app.getAppPath(), 'index.html');
function isTrustedSender (webContents: Electron.WebContents) {
if (webContents !== (mainWindow && mainWindow.webContents)) {
return false
return false;
}
const parsedUrl = new URL(webContents.getURL())
const parsedUrl = new URL(webContents.getURL());
const urlPath = process.platform === 'win32'
// Strip the prefixed "/" that occurs on windows
? path.resolve(parsedUrl.pathname.substr(1))
: parsedUrl.pathname
return parsedUrl.protocol === 'file:' && urlPath === indexPath
: parsedUrl.pathname;
return parsedUrl.protocol === 'file:' && urlPath === indexPath;
}
ipcMain.handle('bootstrap', (event) => {
return isTrustedSender(event.sender) ? electronPath : null
})
return isTrustedSender(event.sender) ? electronPath : null;
});
async function createWindow () {
await app.whenReady()
await app.whenReady();
const options: Electron.BrowserWindowConstructorOptions = {
width: 900,
@@ -57,46 +57,46 @@ async function createWindow () {
},
useContentSize: true,
show: false
}
};
if (process.platform === 'linux') {
options.icon = path.join(__dirname, 'icon.png')
options.icon = path.join(__dirname, 'icon.png');
}
mainWindow = new BrowserWindow(options)
mainWindow.on('ready-to-show', () => mainWindow!.show())
mainWindow = new BrowserWindow(options);
mainWindow.on('ready-to-show', () => mainWindow!.show());
mainWindow.webContents.on('new-window', (event, url) => {
event.preventDefault()
shell.openExternal(decorateURL(url))
})
event.preventDefault();
shell.openExternal(decorateURL(url));
});
mainWindow.webContents.session.setPermissionRequestHandler((webContents, permission, done) => {
const parsedUrl = new URL(webContents.getURL())
const parsedUrl = new URL(webContents.getURL());
const options: Electron.MessageBoxOptions = {
title: 'Permission Request',
message: `Allow '${parsedUrl.origin}' to access '${permission}'?`,
buttons: ['OK', 'Cancel'],
cancelId: 1
}
};
dialog.showMessageBox(mainWindow!, options).then(({ response }) => {
done(response === 0)
})
})
done(response === 0);
});
});
return mainWindow
return mainWindow;
}
export const loadURL = async (appUrl: string) => {
mainWindow = await createWindow()
mainWindow.loadURL(appUrl)
mainWindow.focus()
}
mainWindow = await createWindow();
mainWindow.loadURL(appUrl);
mainWindow.focus();
};
export const loadFile = async (appPath: string) => {
mainWindow = await createWindow()
mainWindow.loadFile(appPath)
mainWindow.focus()
}
mainWindow = await createWindow();
mainWindow.loadFile(appPath);
mainWindow.focus();
};

View File

@@ -1,8 +1,8 @@
import { app, dialog } from 'electron'
import { app, dialog } from 'electron';
import * as fs from 'fs'
import * as path from 'path'
import * as url from 'url'
import * as fs from 'fs';
import * as path from 'path';
import * as url from 'url';
type DefaultAppOptions = {
file: null | string;
@@ -14,10 +14,10 @@ type DefaultAppOptions = {
modules: string[];
}
const Module = require('module')
const Module = require('module');
// Parse command line options.
const argv = process.argv.slice(1)
const argv = process.argv.slice(1);
const option: DefaultAppOptions = {
file: null,
@@ -27,50 +27,50 @@ const option: DefaultAppOptions = {
interactive: false,
abi: false,
modules: []
}
};
let nextArgIsRequire = false
let nextArgIsRequire = false;
for (const arg of argv) {
if (nextArgIsRequire) {
option.modules.push(arg)
nextArgIsRequire = false
continue
option.modules.push(arg);
nextArgIsRequire = false;
continue;
} else if (arg === '--version' || arg === '-v') {
option.version = true
break
option.version = true;
break;
} else if (arg.match(/^--app=/)) {
option.file = arg.split('=')[1]
break
option.file = arg.split('=')[1];
break;
} else if (arg === '--interactive' || arg === '-i' || arg === '-repl') {
option.interactive = true
option.interactive = true;
} else if (arg === '--test-type=webdriver') {
option.webdriver = true
option.webdriver = true;
} else if (arg === '--require' || arg === '-r') {
nextArgIsRequire = true
continue
nextArgIsRequire = true;
continue;
} else if (arg === '--abi' || arg === '-a') {
option.abi = true
continue
option.abi = true;
continue;
} else if (arg === '--no-help') {
option.noHelp = true
continue
option.noHelp = true;
continue;
} else if (arg[0] === '-') {
continue
continue;
} else {
option.file = arg
break
option.file = arg;
break;
}
}
if (nextArgIsRequire) {
console.error('Invalid Usage: --require [file]\n\n"file" is required')
process.exit(1)
console.error('Invalid Usage: --require [file]\n\n"file" is required');
process.exit(1);
}
// Set up preload modules
if (option.modules.length > 0) {
Module._preloadModules(option.modules)
Module._preloadModules(option.modules);
}
function loadApplicationPackage (packagePath: string) {
@@ -79,102 +79,102 @@ function loadApplicationPackage (packagePath: string) {
configurable: false,
enumerable: true,
value: true
})
});
try {
// Override app name and version.
packagePath = path.resolve(packagePath)
const packageJsonPath = path.join(packagePath, 'package.json')
let appPath
packagePath = path.resolve(packagePath);
const packageJsonPath = path.join(packagePath, 'package.json');
let appPath;
if (fs.existsSync(packageJsonPath)) {
let packageJson
let packageJson;
try {
packageJson = require(packageJsonPath)
packageJson = require(packageJsonPath);
} catch (e) {
showErrorMessage(`Unable to parse ${packageJsonPath}\n\n${e.message}`)
return
showErrorMessage(`Unable to parse ${packageJsonPath}\n\n${e.message}`);
return;
}
if (packageJson.version) {
app.setVersion(packageJson.version)
app.setVersion(packageJson.version);
}
if (packageJson.productName) {
app.name = packageJson.productName
app.name = packageJson.productName;
} else if (packageJson.name) {
app.name = packageJson.name
app.name = packageJson.name;
}
appPath = packagePath
appPath = packagePath;
}
try {
const filePath = Module._resolveFilename(packagePath, module, true)
app._setDefaultAppPaths(appPath || path.dirname(filePath))
const filePath = Module._resolveFilename(packagePath, module, true);
app._setDefaultAppPaths(appPath || path.dirname(filePath));
} catch (e) {
showErrorMessage(`Unable to find Electron app at ${packagePath}\n\n${e.message}`)
return
showErrorMessage(`Unable to find Electron app at ${packagePath}\n\n${e.message}`);
return;
}
// Run the app.
Module._load(packagePath, module, true)
Module._load(packagePath, module, true);
} catch (e) {
console.error('App threw an error during load')
console.error(e.stack || e)
throw e
console.error('App threw an error during load');
console.error(e.stack || e);
throw e;
}
}
function showErrorMessage (message: string) {
app.focus()
dialog.showErrorBox('Error launching app', message)
process.exit(1)
app.focus();
dialog.showErrorBox('Error launching app', message);
process.exit(1);
}
async function loadApplicationByURL (appUrl: string) {
const { loadURL } = await import('./default_app')
loadURL(appUrl)
const { loadURL } = await import('./default_app');
loadURL(appUrl);
}
async function loadApplicationByFile (appPath: string) {
const { loadFile } = await import('./default_app')
loadFile(appPath)
const { loadFile } = await import('./default_app');
loadFile(appPath);
}
function startRepl () {
if (process.platform === 'win32') {
console.error('Electron REPL not currently supported on Windows')
process.exit(1)
console.error('Electron REPL not currently supported on Windows');
process.exit(1);
}
// prevent quitting
app.on('window-all-closed', () => {})
app.on('window-all-closed', () => {});
const repl = require('repl')
const repl = require('repl');
repl.start('> ').on('exit', () => {
process.exit(0)
})
process.exit(0);
});
}
// Start the specified app if there is one specified in command line, otherwise
// start the default app.
if (option.file && !option.webdriver) {
const file = option.file
const protocol = url.parse(file).protocol
const extension = path.extname(file)
const file = option.file;
const protocol = url.parse(file).protocol;
const extension = path.extname(file);
if (protocol === 'http:' || protocol === 'https:' || protocol === 'file:' || protocol === 'chrome:') {
loadApplicationByURL(file)
loadApplicationByURL(file);
} else if (extension === '.html' || extension === '.htm') {
loadApplicationByFile(path.resolve(file))
loadApplicationByFile(path.resolve(file));
} else {
loadApplicationPackage(file)
loadApplicationPackage(file);
}
} else if (option.version) {
console.log('v' + process.versions.electron)
process.exit(0)
console.log('v' + process.versions.electron);
process.exit(0);
} else if (option.abi) {
console.log(process.versions.modules)
process.exit(0)
console.log(process.versions.modules);
process.exit(0);
} else if (option.interactive) {
startRepl()
startRepl();
} else {
if (!option.noHelp) {
const welcomeMessage = `
@@ -192,10 +192,10 @@ Options:
-i, --interactive Open a REPL to the main process.
-r, --require Module to preload (option can be repeated).
-v, --version Print the version.
-a, --abi Print the Node ABI version.`
-a, --abi Print the Node ABI version.`;
console.log(welcomeMessage)
console.log(welcomeMessage);
}
loadApplicationByFile('index.html')
loadApplicationByFile('index.html');
}

View File

@@ -1,53 +1,53 @@
import { ipcRenderer, contextBridge } from 'electron'
import { ipcRenderer, contextBridge } from 'electron';
async function getOcticonSvg (name: string) {
try {
const response = await fetch(`octicon/${name}.svg`)
const div = document.createElement('div')
div.innerHTML = await response.text()
return div
const response = await fetch(`octicon/${name}.svg`);
const div = document.createElement('div');
div.innerHTML = await response.text();
return div;
} catch {
return null
return null;
}
}
async function loadSVG (element: HTMLSpanElement) {
for (const cssClass of element.classList) {
if (cssClass.startsWith('octicon-')) {
const icon = await getOcticonSvg(cssClass.substr(8))
const icon = await getOcticonSvg(cssClass.substr(8));
if (icon) {
for (const elemClass of element.classList) {
icon.classList.add(elemClass)
icon.classList.add(elemClass);
}
element.before(icon)
element.remove()
break
element.before(icon);
element.remove();
break;
}
}
}
}
async function initialize () {
const electronPath = await ipcRenderer.invoke('bootstrap')
const electronPath = await ipcRenderer.invoke('bootstrap');
function replaceText (selector: string, text: string) {
const element = document.querySelector<HTMLElement>(selector)
const element = document.querySelector<HTMLElement>(selector);
if (element) {
element.innerText = text
element.innerText = text;
}
}
replaceText('.electron-version', `Electron v${process.versions.electron}`)
replaceText('.chrome-version', `Chromium v${process.versions.chrome}`)
replaceText('.node-version', `Node v${process.versions.node}`)
replaceText('.v8-version', `v8 v${process.versions.v8}`)
replaceText('.command-example', `${electronPath} path-to-app`)
replaceText('.electron-version', `Electron v${process.versions.electron}`);
replaceText('.chrome-version', `Chromium v${process.versions.chrome}`);
replaceText('.node-version', `Node v${process.versions.node}`);
replaceText('.v8-version', `v8 v${process.versions.v8}`);
replaceText('.command-example', `${electronPath} path-to-app`);
for (const element of document.querySelectorAll<HTMLSpanElement>('.octicon')) {
loadSVG(element)
loadSVG(element);
}
}
contextBridge.exposeInMainWorld('electronDefaultApp', {
initialize
})
});

View File

@@ -711,34 +711,34 @@ Clears the recent documents list.
### `app.setAsDefaultProtocolClient(protocol[, path, args])`
* `protocol` String - The name of your protocol, without `://`. If you want your
app to handle `electron://` links, call this method with `electron` as the
parameter.
* `path` String (optional) _Windows_ - Defaults to `process.execPath`
* `args` String[] (optional) _Windows_ - Defaults to an empty array
* `protocol` String - The name of your protocol, without `://`. For example,
if you want your app to handle `electron://` links, call this method with
`electron` as the parameter.
* `path` String (optional) _Windows_ - The path to the Electron executable.
Defaults to `process.execPath`
* `args` String[] (optional) _Windows_ - Arguments passed to the executable.
Defaults to an empty array
Returns `Boolean` - Whether the call succeeded.
This method sets the current executable as the default handler for a protocol
(aka URI scheme). It allows you to integrate your app deeper into the operating
system. Once registered, all links with `your-protocol://` will be opened with
the current executable. The whole link, including protocol, will be passed to
your application as a parameter.
On Windows, you can provide optional parameters path, the path to your executable,
and args, an array of arguments to be passed to your executable when it launches.
Sets the current executable as the default handler for a protocol (aka URI
scheme). It allows you to integrate your app deeper into the operating system.
Once registered, all links with `your-protocol://` will be opened with the
current executable. The whole link, including protocol, will be passed to your
application as a parameter.
**Note:** On macOS, you can only register protocols that have been added to
your app's `info.plist`, which can not be modified at runtime. You can however
change the file with a simple text editor or script during build time.
Please refer to [Apple's documentation][CFBundleURLTypes] for details.
your app's `info.plist`, which cannot be modified at runtime. However, you can
change the file during build time via [Electron Forge][electron-forge],
[Electron Packager][electron-packager], or by editing `info.plist` with a text
editor. Please refer to [Apple's documentation][CFBundleURLTypes] for details.
**Note:** In a Windows Store environment (when packaged as an `appx`) this API
will return `true` for all calls but the registry key it sets won't be accessible
by other applications. In order to register your Windows Store application
as a default protocol handler you must [declare the protocol in your manifest](https://docs.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap-protocol).
The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally.
The API uses the Windows Registry and `LSSetDefaultHandlerForURLScheme` internally.
### `app.removeAsDefaultProtocolClient(protocol[, path, args])` _macOS_ _Windows_
@@ -757,10 +757,8 @@ protocol (aka URI scheme). If so, it will remove the app as the default handler.
* `path` String (optional) _Windows_ - Defaults to `process.execPath`
* `args` String[] (optional) _Windows_ - Defaults to an empty array
Returns `Boolean`
This method checks if the current executable is the default handler for a protocol
(aka URI scheme). If so, it will return true. Otherwise, it will return false.
Returns `Boolean` - Whether the current executable is the default handler for a
protocol (aka URI scheme).
**Note:** On macOS, you can use this method to check if the app has been
registered as the default protocol handler for a protocol. You can also verify
@@ -768,7 +766,7 @@ this by checking `~/Library/Preferences/com.apple.LaunchServices.plist` on the
macOS machine. Please refer to
[Apple's documentation][LSCopyDefaultHandlerForURLScheme] for details.
The API uses the Windows Registry and LSCopyDefaultHandlerForURLScheme internally.
The API uses the Windows Registry and `LSCopyDefaultHandlerForURLScheme` internally.
### `app.setUserTasks(tasks)` _Windows_
@@ -1309,6 +1307,8 @@ A `Boolean` property that returns `true` if the app is packaged, `false` otherw
[dock-menu]:https://developer.apple.com/macos/human-interface-guidelines/menus/dock-menus/
[tasks]:https://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks
[app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx
[electron-forge]: https://www.electronforge.io/
[electron-packager]: https://github.com/electron/electron-packager
[CFBundleURLTypes]: https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-102207-TPXREF115
[LSCopyDefaultHandlerForURLScheme]: https://developer.apple.com/library/mac/documentation/Carbon/Reference/LaunchServicesReference/#//apple_ref/c/func/LSCopyDefaultHandlerForURLScheme
[handoff]: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html

View File

@@ -402,8 +402,6 @@ Returns `Boolean` - whether or not this device has the ability to use Touch ID.
**NOTE:** This API will return `false` on macOS systems older than Sierra 10.12.2.
**[Deprecated](modernization/property-updates.md)**
### `systemPreferences.promptTouchID(reason)` _macOS_
* `reason` String - The reason you are asking for Touch ID authentication

View File

@@ -1244,7 +1244,7 @@ Returns [`PrinterInfo[]`](structures/printer-info.md)
* `silent` Boolean (optional) - Don't ask user for print settings. Default is `false`.
* `printBackground` Boolean (optional) - Prints the background color and image of
the web page. Default is `false`.
* `deviceName` String (optional) - Set the printer device name to use. Default is `''`.
* `deviceName` String (optional) - Set the printer device name to use. Must be the system-defined name and not the 'friendly' name, e.g 'Brother_QL_820NWB' and not 'Brother QL-820NWB'.
* `color` Boolean (optional) - Set whether the printed web page will be in color or grayscale. Default is `true`.
* `margins` Object (optional)
* `marginType` String (optional) - Can be `default`, `none`, `printableArea`, or `custom`. If `custom` is chosen, you will also need to specify `top`, `bottom`, `left`, and `right`.
@@ -1264,7 +1264,7 @@ Returns [`PrinterInfo[]`](structures/printer-info.md)
* `vertical` Number (optional) - The vertical dpi.
* `callback` Function (optional)
* `success` Boolean - Indicates success of the print call.
* `failureReason` String - Called back if the print fails; can be `cancelled` or `failed`.
* `failureReason` String - Error description called back if the print fails.
Prints window's web page. When `silent` is set to `true`, Electron will pick
the system's default printer if `deviceName` is empty and the default settings for printing.

View File

@@ -146,6 +146,7 @@ response are visible by the time this listener is fired.
* `timestamp` Double
* `statusLine` String
* `statusCode` Integer
* `requestHeaders` Record<string, string>
* `responseHeaders` Record<string, string[]> (optional)
* `callback` Function
* `headersReceivedResponse` Object
@@ -228,6 +229,7 @@ redirect is about to occur.
* `fromCache` Boolean
* `statusCode` Integer
* `statusLine` String
* `error` String
The `listener` will be called with `listener(details)` when a request is
completed.

View File

@@ -221,6 +221,8 @@ filenames = {
"shell/browser/net/cert_verifier_client.h",
"shell/browser/net/proxying_url_loader_factory.cc",
"shell/browser/net/proxying_url_loader_factory.h",
"shell/browser/net/proxying_websocket.cc",
"shell/browser/net/proxying_websocket.h",
"shell/browser/net/network_context_service_factory.cc",
"shell/browser/net/network_context_service_factory.h",
"shell/browser/net/network_context_service.cc",
@@ -233,6 +235,7 @@ filenames = {
"shell/browser/net/system_network_context_manager.h",
"shell/browser/net/url_pipe_loader.cc",
"shell/browser/net/url_pipe_loader.h",
"shell/browser/net/web_request_api_interface.h",
"shell/browser/notifications/linux/libnotify_notification.cc",
"shell/browser/notifications/linux/libnotify_notification.h",
"shell/browser/notifications/linux/notification_presenter_linux.cc",

View File

@@ -1,21 +1,21 @@
import * as fs from 'fs'
import * as path from 'path'
import * as fs from 'fs';
import * as path from 'path';
import { deprecate, Menu } from 'electron'
import { EventEmitter } from 'events'
import { deprecate, Menu } from 'electron';
import { EventEmitter } from 'events';
const bindings = process.electronBinding('app')
const commandLine = process.electronBinding('command_line')
const { app, App } = bindings
const bindings = process.electronBinding('app');
const commandLine = process.electronBinding('command_line');
const { app, App } = bindings;
// Only one app object permitted.
export default app
export default app;
let dockMenu: Electron.Menu | null = null
let dockMenu: Electron.Menu | null = null;
// App is an EventEmitter.
Object.setPrototypeOf(App.prototype, EventEmitter.prototype)
EventEmitter.call(app as any)
Object.setPrototypeOf(App.prototype, EventEmitter.prototype);
EventEmitter.call(app as any);
Object.assign(app, {
commandLine: {
@@ -24,99 +24,99 @@ Object.assign(app, {
appendSwitch: (theSwitch: string, value?: string) => commandLine.appendSwitch(String(theSwitch), typeof value === 'undefined' ? value : String(value)),
appendArgument: (arg: string) => commandLine.appendArgument(String(arg))
} as Electron.CommandLine
})
});
// we define this here because it'd be overly complicated to
// do in native land
Object.defineProperty(app, 'applicationMenu', {
get () {
return Menu.getApplicationMenu()
return Menu.getApplicationMenu();
},
set (menu: Electron.Menu | null) {
return Menu.setApplicationMenu(menu)
return Menu.setApplicationMenu(menu);
}
})
});
App.prototype.isPackaged = (() => {
const execFile = path.basename(process.execPath).toLowerCase()
const execFile = path.basename(process.execPath).toLowerCase();
if (process.platform === 'win32') {
return execFile !== 'electron.exe'
return execFile !== 'electron.exe';
}
return execFile !== 'electron'
})()
return execFile !== 'electron';
})();
app._setDefaultAppPaths = (packagePath) => {
// Set the user path according to application's name.
app.setPath('userData', path.join(app.getPath('appData'), app.name!))
app.setPath('userCache', path.join(app.getPath('cache'), app.name!))
app.setAppPath(packagePath)
app.setPath('userData', path.join(app.getPath('appData'), app.name!));
app.setPath('userCache', path.join(app.getPath('cache'), app.name!));
app.setAppPath(packagePath);
// Add support for --user-data-dir=
if (app.commandLine.hasSwitch('user-data-dir')) {
const userDataDir = app.commandLine.getSwitchValue('user-data-dir')
if (path.isAbsolute(userDataDir)) app.setPath('userData', userDataDir)
const userDataDir = app.commandLine.getSwitchValue('user-data-dir');
if (path.isAbsolute(userDataDir)) app.setPath('userData', userDataDir);
}
}
};
if (process.platform === 'darwin') {
const setDockMenu = app.dock!.setMenu
const setDockMenu = app.dock!.setMenu;
app.dock!.setMenu = (menu) => {
dockMenu = menu
setDockMenu(menu)
}
app.dock!.getMenu = () => dockMenu
dockMenu = menu;
setDockMenu(menu);
};
app.dock!.getMenu = () => dockMenu;
}
if (process.platform === 'linux') {
const patternVmRSS = /^VmRSS:\s*(\d+) kB$/m
const patternVmHWM = /^VmHWM:\s*(\d+) kB$/m
const patternVmRSS = /^VmRSS:\s*(\d+) kB$/m;
const patternVmHWM = /^VmHWM:\s*(\d+) kB$/m;
const getStatus = (pid: number) => {
try {
return fs.readFileSync(`/proc/${pid}/status`, 'utf8')
return fs.readFileSync(`/proc/${pid}/status`, 'utf8');
} catch {
return ''
return '';
}
}
};
const getEntry = (file: string, pattern: RegExp) => {
const match = file.match(pattern)
return match ? parseInt(match[1], 10) : 0
}
const match = file.match(pattern);
return match ? parseInt(match[1], 10) : 0;
};
const getProcessMemoryInfo = (pid: number) => {
const file = getStatus(pid)
const file = getStatus(pid);
return {
workingSetSize: getEntry(file, patternVmRSS),
peakWorkingSetSize: getEntry(file, patternVmHWM)
}
}
};
};
const nativeFn = app.getAppMetrics
const nativeFn = app.getAppMetrics;
app.getAppMetrics = () => {
const metrics = nativeFn.call(app)
const metrics = nativeFn.call(app);
for (const metric of metrics) {
metric.memory = getProcessMemoryInfo(metric.pid)
metric.memory = getProcessMemoryInfo(metric.pid);
}
return metrics
}
return metrics;
};
}
// Routes the events to webContents.
const events = ['certificate-error', 'select-client-certificate']
const events = ['certificate-error', 'select-client-certificate'];
for (const name of events) {
app.on(name as 'certificate-error', (event, webContents, ...args: any[]) => {
webContents.emit(name, event, ...args)
})
webContents.emit(name, event, ...args);
});
}
// Property Deprecations
deprecate.fnToProperty(App.prototype, 'accessibilitySupportEnabled', '_isAccessibilitySupportEnabled', '_setAccessibilitySupportEnabled')
deprecate.fnToProperty(App.prototype, 'badgeCount', '_getBadgeCount', '_setBadgeCount')
deprecate.fnToProperty(App.prototype, 'name', '_getName', '_setName')
deprecate.fnToProperty(App.prototype, 'accessibilitySupportEnabled', '_isAccessibilitySupportEnabled', '_setAccessibilitySupportEnabled');
deprecate.fnToProperty(App.prototype, 'badgeCount', '_getBadgeCount', '_setBadgeCount');
deprecate.fnToProperty(App.prototype, 'name', '_getName', '_setName');
// Wrappers for native classes.
const { DownloadItem } = process.electronBinding('download_item')
Object.setPrototypeOf(DownloadItem.prototype, EventEmitter.prototype)
const { DownloadItem } = process.electronBinding('download_item');
Object.setPrototypeOf(DownloadItem.prototype, EventEmitter.prototype);

View File

@@ -1,7 +1,7 @@
'use strict'
'use strict';
if (process.platform === 'win32') {
module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-win')
module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-win');
} else {
module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-native')
module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-native');
}

View File

@@ -1,10 +1,10 @@
'use strict'
'use strict';
const EventEmitter = require('events').EventEmitter
const { autoUpdater, AutoUpdater } = process.electronBinding('auto_updater')
const EventEmitter = require('events').EventEmitter;
const { autoUpdater, AutoUpdater } = process.electronBinding('auto_updater');
// AutoUpdater is an EventEmitter.
Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype)
EventEmitter.call(autoUpdater)
Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype);
EventEmitter.call(autoUpdater);
module.exports = autoUpdater
module.exports = autoUpdater;

View File

@@ -1,74 +1,74 @@
'use strict'
'use strict';
const { app } = require('electron')
const { EventEmitter } = require('events')
const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win')
const { app } = require('electron');
const { EventEmitter } = require('events');
const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win');
class AutoUpdater extends EventEmitter {
quitAndInstall () {
if (!this.updateAvailable) {
return this.emitError('No update available, can\'t quit and install')
return this.emitError('No update available, can\'t quit and install');
}
squirrelUpdate.processStart()
app.quit()
squirrelUpdate.processStart();
app.quit();
}
getFeedURL () {
return this.updateURL
return this.updateURL;
}
setFeedURL (options) {
let updateURL
let updateURL;
if (typeof options === 'object') {
if (typeof options.url === 'string') {
updateURL = options.url
updateURL = options.url;
} else {
throw new Error('Expected options object to contain a \'url\' string property in setFeedUrl call')
throw new Error('Expected options object to contain a \'url\' string property in setFeedUrl call');
}
} else if (typeof options === 'string') {
updateURL = options
updateURL = options;
} else {
throw new Error('Expected an options object with a \'url\' property to be provided')
throw new Error('Expected an options object with a \'url\' property to be provided');
}
this.updateURL = updateURL
this.updateURL = updateURL;
}
checkForUpdates () {
if (!this.updateURL) {
return this.emitError('Update URL is not set')
return this.emitError('Update URL is not set');
}
if (!squirrelUpdate.supported()) {
return this.emitError('Can not find Squirrel')
return this.emitError('Can not find Squirrel');
}
this.emit('checking-for-update')
this.emit('checking-for-update');
squirrelUpdate.checkForUpdate(this.updateURL, (error, update) => {
if (error != null) {
return this.emitError(error)
return this.emitError(error);
}
if (update == null) {
return this.emit('update-not-available')
return this.emit('update-not-available');
}
this.updateAvailable = true
this.emit('update-available')
this.updateAvailable = true;
this.emit('update-available');
squirrelUpdate.update(this.updateURL, (error) => {
if (error != null) {
return this.emitError(error)
return this.emitError(error);
}
const { releaseNotes, version } = update
const { releaseNotes, version } = update;
// Date is not available on Windows, so fake it.
const date = new Date()
const date = new Date();
this.emit('update-downloaded', {}, releaseNotes, version, date, this.updateURL, () => {
this.quitAndInstall()
})
})
})
this.quitAndInstall();
});
});
});
}
// Private: Emit both error object and message, this is to keep compatibility
// with Old APIs.
emitError (message) {
this.emit('error', new Error(message), message)
this.emit('error', new Error(message), message);
}
}
module.exports = new AutoUpdater()
module.exports = new AutoUpdater();

View File

@@ -1,24 +1,24 @@
'use strict'
'use strict';
const fs = require('fs')
const path = require('path')
const spawn = require('child_process').spawn
const fs = require('fs');
const path = require('path');
const spawn = require('child_process').spawn;
// i.e. my-app/app-0.1.13/
const appFolder = path.dirname(process.execPath)
const appFolder = path.dirname(process.execPath);
// i.e. my-app/Update.exe
const updateExe = path.resolve(appFolder, '..', 'Update.exe')
const exeName = path.basename(process.execPath)
let spawnedArgs = []
let spawnedProcess
const updateExe = path.resolve(appFolder, '..', 'Update.exe');
const exeName = path.basename(process.execPath);
let spawnedArgs = [];
let spawnedProcess;
const isSameArgs = (args) => args.length === spawnedArgs.length && args.every((e, i) => e === spawnedArgs[i])
const isSameArgs = (args) => args.length === spawnedArgs.length && args.every((e, i) => e === spawnedArgs[i]);
// Spawn a command and invoke the callback when it completes with an error
// and the output from standard out.
const spawnUpdate = function (args, detached, callback) {
let error, errorEmitted, stderr, stdout
let error, errorEmitted, stderr, stdout;
try {
// Ensure we don't spawn multiple squirrel processes
@@ -28,92 +28,92 @@ const spawnUpdate = function (args, detached, callback) {
if (spawnedProcess && !isSameArgs(args)) {
// Disabled for backwards compatibility:
// eslint-disable-next-line standard/no-callback-literal
return callback(`AutoUpdater process with arguments ${args} is already running`)
return callback(`AutoUpdater process with arguments ${args} is already running`);
} else if (!spawnedProcess) {
spawnedProcess = spawn(updateExe, args, {
detached: detached,
windowsHide: true
})
spawnedArgs = args || []
});
spawnedArgs = args || [];
}
} catch (error1) {
error = error1
error = error1;
// Shouldn't happen, but still guard it.
process.nextTick(function () {
return callback(error)
})
return
return callback(error);
});
return;
}
stdout = ''
stderr = ''
stdout = '';
stderr = '';
spawnedProcess.stdout.on('data', (data) => { stdout += data })
spawnedProcess.stderr.on('data', (data) => { stderr += data })
spawnedProcess.stdout.on('data', (data) => { stdout += data; });
spawnedProcess.stderr.on('data', (data) => { stderr += data; });
errorEmitted = false
errorEmitted = false;
spawnedProcess.on('error', (error) => {
errorEmitted = true
callback(error)
})
errorEmitted = true;
callback(error);
});
return spawnedProcess.on('exit', function (code, signal) {
spawnedProcess = undefined
spawnedArgs = []
spawnedProcess = undefined;
spawnedArgs = [];
// We may have already emitted an error.
if (errorEmitted) {
return
return;
}
// Process terminated with error.
if (code !== 0) {
// Disabled for backwards compatibility:
// eslint-disable-next-line standard/no-callback-literal
return callback(`Command failed: ${signal != null ? signal : code}\n${stderr}`)
return callback(`Command failed: ${signal != null ? signal : code}\n${stderr}`);
}
// Success.
callback(null, stdout)
})
}
callback(null, stdout);
});
};
// Start an instance of the installed app.
exports.processStart = function () {
return spawnUpdate(['--processStartAndWait', exeName], true, function () {})
}
return spawnUpdate(['--processStartAndWait', exeName], true, function () {});
};
// Download the releases specified by the URL and write new results to stdout.
exports.checkForUpdate = function (updateURL, callback) {
return spawnUpdate(['--checkForUpdate', updateURL], false, function (error, stdout) {
let ref, ref1, update
let ref, ref1, update;
if (error != null) {
return callback(error)
return callback(error);
}
try {
// Last line of output is the JSON details about the releases
const json = stdout.trim().split('\n').pop()
update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : void 0 : void 0 : void 0
const json = stdout.trim().split('\n').pop();
update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : void 0 : void 0 : void 0;
} catch {
// Disabled for backwards compatibility:
// eslint-disable-next-line standard/no-callback-literal
return callback(`Invalid result:\n${stdout}`)
return callback(`Invalid result:\n${stdout}`);
}
return callback(null, update)
})
}
return callback(null, update);
});
};
// Update the application to the latest remote version specified by URL.
exports.update = function (updateURL, callback) {
return spawnUpdate(['--update', updateURL], false, callback)
}
return spawnUpdate(['--update', updateURL], false, callback);
};
// Is the Update.exe installed with the current application?
exports.supported = function () {
try {
fs.accessSync(updateExe, fs.R_OK)
return true
fs.accessSync(updateExe, fs.R_OK);
return true;
} catch {
return false
return false;
}
}
};

View File

@@ -1,16 +1,16 @@
'use strict'
'use strict';
const { EventEmitter } = require('events')
const { BrowserView } = process.electronBinding('browser_view')
const { EventEmitter } = require('events');
const { BrowserView } = process.electronBinding('browser_view');
Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype)
Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype);
BrowserView.fromWebContents = (webContents) => {
for (const view of BrowserView.getAllViews()) {
if (view.webContents.equal(webContents)) return view
if (view.webContents.equal(webContents)) return view;
}
return null
}
return null;
};
module.exports = BrowserView
module.exports = BrowserView;

View File

@@ -1,49 +1,49 @@
'use strict'
'use strict';
const electron = require('electron')
const { WebContentsView, TopLevelWindow, deprecate } = electron
const { BrowserWindow } = process.electronBinding('window')
const electron = require('electron');
const { WebContentsView, TopLevelWindow, deprecate } = electron;
const { BrowserWindow } = process.electronBinding('window');
Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype)
Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype);
BrowserWindow.prototype._init = function () {
// Call parent class's _init.
TopLevelWindow.prototype._init.call(this)
TopLevelWindow.prototype._init.call(this);
// Avoid recursive require.
const { app } = electron
const { app } = electron;
// Create WebContentsView.
this.setContentView(new WebContentsView(this.webContents))
this.setContentView(new WebContentsView(this.webContents));
const nativeSetBounds = this.setBounds
const nativeSetBounds = this.setBounds;
this.setBounds = (bounds, ...opts) => {
bounds = {
...this.getBounds(),
...bounds
}
nativeSetBounds.call(this, bounds, ...opts)
}
};
nativeSetBounds.call(this, bounds, ...opts);
};
// window.resizeTo(...)
// window.moveTo(...)
this.webContents.on('move', (event, size) => {
this.setBounds(size)
})
this.setBounds(size);
});
// Hide the auto-hide menu when webContents is focused.
this.webContents.on('activate', () => {
if (process.platform !== 'darwin' && this.autoHideMenuBar && this.isMenuBarVisible()) {
this.setMenuBarVisibility(false)
this.setMenuBarVisibility(false);
}
})
});
// Change window title to page title.
this.webContents.on('page-title-updated', (event, title, ...args) => {
// Route the event to BrowserWindow.
this.emit('page-title-updated', event, title, ...args)
if (!this.isDestroyed() && !event.defaultPrevented) this.setTitle(title)
})
this.emit('page-title-updated', event, title, ...args);
if (!this.isDestroyed() && !event.defaultPrevented) this.setTitle(title);
});
// Sometimes the webContents doesn't get focus when window is shown, so we
// have to force focusing on webContents in this case. The safest way is to
@@ -54,142 +54,142 @@ BrowserWindow.prototype._init = function () {
// Finder, we still do it on all platforms in case of other bugs we don't
// know.
this.webContents.once('load-url', function () {
this.focus()
})
this.focus();
});
// Redirect focus/blur event to app instance too.
this.on('blur', (event) => {
app.emit('browser-window-blur', event, this)
})
app.emit('browser-window-blur', event, this);
});
this.on('focus', (event) => {
app.emit('browser-window-focus', event, this)
})
app.emit('browser-window-focus', event, this);
});
// Subscribe to visibilityState changes and pass to renderer process.
let isVisible = this.isVisible() && !this.isMinimized()
let isVisible = this.isVisible() && !this.isMinimized();
const visibilityChanged = () => {
const newState = this.isVisible() && !this.isMinimized()
const newState = this.isVisible() && !this.isMinimized();
if (isVisible !== newState) {
isVisible = newState
const visibilityState = isVisible ? 'visible' : 'hidden'
this.webContents.emit('-window-visibility-change', visibilityState)
isVisible = newState;
const visibilityState = isVisible ? 'visible' : 'hidden';
this.webContents.emit('-window-visibility-change', visibilityState);
}
}
};
const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore']
const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore'];
for (const event of visibilityEvents) {
this.on(event, visibilityChanged)
this.on(event, visibilityChanged);
}
// Notify the creation of the window.
const event = process.electronBinding('event').createEmpty()
app.emit('browser-window-created', event, this)
const event = process.electronBinding('event').createEmpty();
app.emit('browser-window-created', event, this);
Object.defineProperty(this, 'devToolsWebContents', {
enumerable: true,
configurable: false,
get () {
return this.webContents.devToolsWebContents
return this.webContents.devToolsWebContents;
}
})
}
});
};
const isBrowserWindow = (win) => {
return win && win.constructor.name === 'BrowserWindow'
}
return win && win.constructor.name === 'BrowserWindow';
};
BrowserWindow.fromId = (id) => {
const win = TopLevelWindow.fromId(id)
return isBrowserWindow(win) ? win : null
}
const win = TopLevelWindow.fromId(id);
return isBrowserWindow(win) ? win : null;
};
BrowserWindow.getAllWindows = () => {
return TopLevelWindow.getAllWindows().filter(isBrowserWindow)
}
return TopLevelWindow.getAllWindows().filter(isBrowserWindow);
};
BrowserWindow.getFocusedWindow = () => {
for (const window of BrowserWindow.getAllWindows()) {
if (window.isFocused() || window.isDevToolsFocused()) return window
if (window.isFocused() || window.isDevToolsFocused()) return window;
}
return null
}
return null;
};
BrowserWindow.fromWebContents = (webContents) => {
for (const window of BrowserWindow.getAllWindows()) {
if (window.webContents && window.webContents.equal(webContents)) return window
if (window.webContents && window.webContents.equal(webContents)) return window;
}
}
};
BrowserWindow.fromBrowserView = (browserView) => {
for (const window of BrowserWindow.getAllWindows()) {
if (window.getBrowserView() === browserView) return window
if (window.getBrowserView() === browserView) return window;
}
return null
}
return null;
};
// Helpers.
Object.assign(BrowserWindow.prototype, {
loadURL (...args) {
return this.webContents.loadURL(...args)
return this.webContents.loadURL(...args);
},
getURL (...args) {
return this.webContents.getURL()
return this.webContents.getURL();
},
loadFile (...args) {
return this.webContents.loadFile(...args)
return this.webContents.loadFile(...args);
},
reload (...args) {
return this.webContents.reload(...args)
return this.webContents.reload(...args);
},
send (...args) {
return this.webContents.send(...args)
return this.webContents.send(...args);
},
openDevTools (...args) {
return this.webContents.openDevTools(...args)
return this.webContents.openDevTools(...args);
},
closeDevTools () {
return this.webContents.closeDevTools()
return this.webContents.closeDevTools();
},
isDevToolsOpened () {
return this.webContents.isDevToolsOpened()
return this.webContents.isDevToolsOpened();
},
isDevToolsFocused () {
return this.webContents.isDevToolsFocused()
return this.webContents.isDevToolsFocused();
},
toggleDevTools () {
return this.webContents.toggleDevTools()
return this.webContents.toggleDevTools();
},
inspectElement (...args) {
return this.webContents.inspectElement(...args)
return this.webContents.inspectElement(...args);
},
inspectSharedWorker () {
return this.webContents.inspectSharedWorker()
return this.webContents.inspectSharedWorker();
},
inspectServiceWorker () {
return this.webContents.inspectServiceWorker()
return this.webContents.inspectServiceWorker();
},
showDefinitionForSelection () {
return this.webContents.showDefinitionForSelection()
return this.webContents.showDefinitionForSelection();
},
capturePage (...args) {
return this.webContents.capturePage(...args)
return this.webContents.capturePage(...args);
},
setTouchBar (touchBar) {
electron.TouchBar._setOnWindow(touchBar, this)
electron.TouchBar._setOnWindow(touchBar, this);
},
setBackgroundThrottling (allowed) {
this.webContents.setBackgroundThrottling(allowed)
this.webContents.setBackgroundThrottling(allowed);
}
})
});
// Deprecations
deprecate.fnToProperty(BrowserWindow.prototype, 'autoHideMenuBar', '_isMenuBarAutoHide', '_setAutoHideMenuBar')
deprecate.fnToProperty(BrowserWindow.prototype, 'minimizable', '_isMinimizable', '_setMinimizable')
deprecate.fnToProperty(BrowserWindow.prototype, 'maximizable', '_isMaximizable', '_setMaximizable')
deprecate.fnToProperty(BrowserWindow.prototype, 'resizable', '_isResizable', '_setResizable')
deprecate.fnToProperty(BrowserWindow.prototype, 'fullScreenable', '_isFullScreenable', '_setFullScreenable')
deprecate.fnToProperty(BrowserWindow.prototype, 'closable', '_isClosable', '_setClosable')
deprecate.fnToProperty(BrowserWindow.prototype, 'movable', '_isMovable', '_setMovable')
deprecate.fnToProperty(BrowserWindow.prototype, 'autoHideMenuBar', '_isMenuBarAutoHide', '_setAutoHideMenuBar');
deprecate.fnToProperty(BrowserWindow.prototype, 'minimizable', '_isMinimizable', '_setMinimizable');
deprecate.fnToProperty(BrowserWindow.prototype, 'maximizable', '_isMaximizable', '_setMaximizable');
deprecate.fnToProperty(BrowserWindow.prototype, 'resizable', '_isResizable', '_setResizable');
deprecate.fnToProperty(BrowserWindow.prototype, 'fullScreenable', '_isFullScreenable', '_setFullScreenable');
deprecate.fnToProperty(BrowserWindow.prototype, 'closable', '_isClosable', '_setClosable');
deprecate.fnToProperty(BrowserWindow.prototype, 'movable', '_isMovable', '_setMovable');
module.exports = BrowserWindow
module.exports = BrowserWindow;

View File

@@ -1,2 +1,2 @@
'use strict'
module.exports = process.electronBinding('content_tracing')
'use strict';
module.exports = process.electronBinding('content_tracing');

View File

@@ -1,12 +1,12 @@
'use strict'
'use strict';
const CrashReporter = require('@electron/internal/common/crash-reporter')
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init')
const CrashReporter = require('@electron/internal/common/crash-reporter');
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init');
class CrashReporterMain extends CrashReporter {
init (options) {
return crashReporterInit(options)
return crashReporterInit(options);
}
}
module.exports = new CrashReporterMain()
module.exports = new CrashReporterMain();

View File

@@ -1,8 +1,8 @@
'use strict'
'use strict';
const { app, BrowserWindow, deprecate } = require('electron')
const binding = process.electronBinding('dialog')
const v8Util = process.electronBinding('v8_util')
const { app, BrowserWindow, deprecate } = require('electron');
const binding = process.electronBinding('dialog');
const v8Util = process.electronBinding('v8_util');
const fileDialogProperties = {
openFile: 1 << 0,
@@ -13,15 +13,15 @@ const fileDialogProperties = {
promptToCreate: 1 << 5,
noResolveAliases: 1 << 6,
treatPackageAsDirectory: 1 << 7
}
};
const normalizeAccessKey = (text) => {
if (typeof text !== 'string') return text
if (typeof text !== 'string') return text;
// macOS does not have access keys so remove single ampersands
// and replace double ampersands with a single ampersand
if (process.platform === 'darwin') {
return text.replace(/&(&?)/g, '$1')
return text.replace(/&(&?)/g, '$1');
}
// Linux uses a single underscore as an access key prefix so escape
@@ -30,29 +30,29 @@ const normalizeAccessKey = (text) => {
// a single underscore
if (process.platform === 'linux') {
return text.replace(/_/g, '__').replace(/&(.?)/g, (match, after) => {
if (after === '&') return after
return `_${after}`
})
if (after === '&') return after;
return `_${after}`;
});
}
return text
}
return text;
};
const checkAppInitialized = function () {
if (!app.isReady()) {
throw new Error('dialog module can only be used after app is ready')
throw new Error('dialog module can only be used after app is ready');
}
}
};
const saveDialog = (sync, window, options) => {
checkAppInitialized()
checkAppInitialized();
if (window && window.constructor !== BrowserWindow) {
options = window
window = null
options = window;
window = null;
}
if (options == null) options = { title: 'Save' }
if (options == null) options = { title: 'Save' };
const {
buttonLabel = '',
@@ -63,31 +63,31 @@ const saveDialog = (sync, window, options) => {
securityScopedBookmarks = false,
nameFieldLabel = '',
showsTagField = true
} = options
} = options;
if (typeof title !== 'string') throw new TypeError('Title must be a string')
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string')
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string')
if (typeof message !== 'string') throw new TypeError('Message must be a string')
if (typeof nameFieldLabel !== 'string') throw new TypeError('Name field label must be a string')
if (typeof title !== 'string') throw new TypeError('Title must be a string');
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string');
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string');
if (typeof message !== 'string') throw new TypeError('Message must be a string');
if (typeof nameFieldLabel !== 'string') throw new TypeError('Name field label must be a string');
const settings = { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window }
return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings)
}
const settings = { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window };
return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings);
};
const openDialog = (sync, window, options) => {
checkAppInitialized()
checkAppInitialized();
if (window && window.constructor !== BrowserWindow) {
options = window
window = null
options = window;
window = null;
}
if (options == null) {
options = {
title: 'Open',
properties: ['openFile']
}
};
}
const {
@@ -98,40 +98,40 @@ const openDialog = (sync, window, options) => {
title = '',
message = '',
securityScopedBookmarks = false
} = options
} = options;
if (!Array.isArray(properties)) throw new TypeError('Properties must be an array')
if (!Array.isArray(properties)) throw new TypeError('Properties must be an array');
let dialogProperties = 0
let dialogProperties = 0;
for (const prop in fileDialogProperties) {
if (properties.includes(prop)) {
dialogProperties |= fileDialogProperties[prop]
dialogProperties |= fileDialogProperties[prop];
}
}
if (typeof title !== 'string') throw new TypeError('Title must be a string')
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string')
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string')
if (typeof message !== 'string') throw new TypeError('Message must be a string')
if (typeof title !== 'string') throw new TypeError('Title must be a string');
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string');
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string');
if (typeof message !== 'string') throw new TypeError('Message must be a string');
const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window }
settings.properties = dialogProperties
const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window };
settings.properties = dialogProperties;
return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings)
}
return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings);
};
const messageBox = (sync, window, options) => {
checkAppInitialized()
checkAppInitialized();
if (window && window.constructor !== BrowserWindow) {
options = window
window = null
options = window;
window = null;
}
if (options == null) options = { type: 'none' }
if (options == null) options = { type: 'none' };
const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question']
const messageBoxOptions = { noLink: 1 << 0 }
const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'];
const messageBoxOptions = { noLink: 1 << 0 };
let {
buttons = [],
@@ -145,32 +145,32 @@ const messageBox = (sync, window, options) => {
message = '',
title = '',
type = 'none'
} = options
} = options;
const messageBoxType = messageBoxTypes.indexOf(type)
if (messageBoxType === -1) throw new TypeError('Invalid message box type')
if (!Array.isArray(buttons)) throw new TypeError('Buttons must be an array')
if (options.normalizeAccessKeys) buttons = buttons.map(normalizeAccessKey)
if (typeof title !== 'string') throw new TypeError('Title must be a string')
if (typeof noLink !== 'boolean') throw new TypeError('noLink must be a boolean')
if (typeof message !== 'string') throw new TypeError('Message must be a string')
if (typeof detail !== 'string') throw new TypeError('Detail must be a string')
if (typeof checkboxLabel !== 'string') throw new TypeError('checkboxLabel must be a string')
const messageBoxType = messageBoxTypes.indexOf(type);
if (messageBoxType === -1) throw new TypeError('Invalid message box type');
if (!Array.isArray(buttons)) throw new TypeError('Buttons must be an array');
if (options.normalizeAccessKeys) buttons = buttons.map(normalizeAccessKey);
if (typeof title !== 'string') throw new TypeError('Title must be a string');
if (typeof noLink !== 'boolean') throw new TypeError('noLink must be a boolean');
if (typeof message !== 'string') throw new TypeError('Message must be a string');
if (typeof detail !== 'string') throw new TypeError('Detail must be a string');
if (typeof checkboxLabel !== 'string') throw new TypeError('checkboxLabel must be a string');
checkboxChecked = !!checkboxChecked
checkboxChecked = !!checkboxChecked;
if (checkboxChecked && !checkboxLabel) {
throw new Error('checkboxChecked requires that checkboxLabel also be passed')
throw new Error('checkboxChecked requires that checkboxLabel also be passed');
}
// Choose a default button to get selected when dialog is cancelled.
if (cancelId == null) {
// If the defaultId is set to 0, ensure the cancel button is a different index (1)
cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0
cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0;
for (let i = 0; i < buttons.length; i++) {
const text = buttons[i].toLowerCase()
const text = buttons[i].toLowerCase();
if (text === 'cancel' || text === 'no') {
cancelId = i
break
cancelId = i;
break;
}
}
}
@@ -188,57 +188,57 @@ const messageBox = (sync, window, options) => {
checkboxLabel,
checkboxChecked,
icon
}
};
if (sync) {
return binding.showMessageBoxSync(settings)
return binding.showMessageBoxSync(settings);
} else {
return binding.showMessageBox(settings)
return binding.showMessageBox(settings);
}
}
};
module.exports = {
showOpenDialog: function (window, options) {
return openDialog(false, window, options)
return openDialog(false, window, options);
},
showOpenDialogSync: function (window, options) {
return openDialog(true, window, options)
return openDialog(true, window, options);
},
showSaveDialog: function (window, options) {
return saveDialog(false, window, options)
return saveDialog(false, window, options);
},
showSaveDialogSync: function (window, options) {
return saveDialog(true, window, options)
return saveDialog(true, window, options);
},
showMessageBox: function (window, options) {
return messageBox(false, window, options)
return messageBox(false, window, options);
},
showMessageBoxSync: function (window, options) {
return messageBox(true, window, options)
return messageBox(true, window, options);
},
showErrorBox: function (...args) {
return binding.showErrorBox(...args)
return binding.showErrorBox(...args);
},
showCertificateTrustDialog: function (window, options) {
if (window && window.constructor !== BrowserWindow) options = window
if (window && window.constructor !== BrowserWindow) options = window;
if (options == null || typeof options !== 'object') {
throw new TypeError('options must be an object')
throw new TypeError('options must be an object');
}
const { certificate, message = '' } = options
const { certificate, message = '' } = options;
if (certificate == null || typeof certificate !== 'object') {
throw new TypeError('certificate must be an object')
throw new TypeError('certificate must be an object');
}
if (typeof message !== 'string') throw new TypeError('message must be a string')
if (typeof message !== 'string') throw new TypeError('message must be a string');
return binding.showCertificateTrustDialog(window, certificate, message)
return binding.showCertificateTrustDialog(window, certificate, message);
}
}
};

View File

@@ -1,15 +1,15 @@
'use strict'
'use strict';
const common = require('@electron/internal/common/api/exports/electron')
const common = require('@electron/internal/common/api/exports/electron');
// since browser module list is also used in renderer, keep it separate.
const moduleList = require('@electron/internal/browser/api/module-list')
const moduleList = require('@electron/internal/browser/api/module-list');
// Import common modules.
common.defineProperties(exports)
common.defineProperties(exports);
for (const module of moduleList) {
Object.defineProperty(exports, module.name, {
enumerable: !module.private,
get: common.handleESModule(module.loader)
})
});
}

View File

@@ -1,3 +1,3 @@
'use strict'
'use strict';
module.exports = process.electronBinding('global_shortcut').globalShortcut
module.exports = process.electronBinding('global_shortcut').globalShortcut;

View File

@@ -1,22 +1,22 @@
'use strict'
'use strict';
const { deprecate } = require('electron')
const { deprecate } = require('electron');
if (process.platform === 'darwin') {
const { EventEmitter } = require('events')
const { inAppPurchase, InAppPurchase } = process.electronBinding('in_app_purchase')
const { EventEmitter } = require('events');
const { inAppPurchase, InAppPurchase } = process.electronBinding('in_app_purchase');
// inAppPurchase is an EventEmitter.
Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype)
EventEmitter.call(inAppPurchase)
Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype);
EventEmitter.call(inAppPurchase);
module.exports = inAppPurchase
module.exports = inAppPurchase;
} else {
module.exports = {
purchaseProduct: (productID, quantity, callback) => {
throw new Error('The inAppPurchase module can only be used on macOS')
throw new Error('The inAppPurchase module can only be used on macOS');
},
canMakePayments: () => false,
getReceiptURL: () => ''
}
};
}

View File

@@ -1,40 +1,40 @@
import { EventEmitter } from 'events'
import { IpcMainInvokeEvent } from 'electron'
import { EventEmitter } from 'events';
import { IpcMainInvokeEvent } from 'electron';
class IpcMain extends EventEmitter {
private _invokeHandlers: Map<string, (e: IpcMainInvokeEvent, ...args: any[]) => void> = new Map();
handle: Electron.IpcMain['handle'] = (method, fn) => {
if (this._invokeHandlers.has(method)) {
throw new Error(`Attempted to register a second handler for '${method}'`)
throw new Error(`Attempted to register a second handler for '${method}'`);
}
if (typeof fn !== 'function') {
throw new Error(`Expected handler to be a function, but found type '${typeof fn}'`)
throw new Error(`Expected handler to be a function, but found type '${typeof fn}'`);
}
this._invokeHandlers.set(method, async (e, ...args) => {
try {
(e as any)._reply(await Promise.resolve(fn(e, ...args)))
(e as any)._reply(await Promise.resolve(fn(e, ...args)));
} catch (err) {
(e as any)._throw(err)
(e as any)._throw(err);
}
})
});
}
handleOnce: Electron.IpcMain['handleOnce'] = (method, fn) => {
this.handle(method, (e, ...args) => {
this.removeHandler(method)
return fn(e, ...args)
})
this.removeHandler(method);
return fn(e, ...args);
});
}
removeHandler (method: string) {
this._invokeHandlers.delete(method)
this._invokeHandlers.delete(method);
}
}
const ipcMain = new IpcMain()
const ipcMain = new IpcMain();
// Do not throw exception when channel name is "error".
ipcMain.on('error', () => {})
ipcMain.on('error', () => {});
export default ipcMain
export default ipcMain;

View File

@@ -1,15 +1,15 @@
'use strict'
'use strict';
const { app } = require('electron')
const { app } = require('electron');
const isMac = process.platform === 'darwin'
const isWindows = process.platform === 'win32'
const isLinux = process.platform === 'linux'
const isMac = process.platform === 'darwin';
const isWindows = process.platform === 'win32';
const isLinux = process.platform === 'linux';
const roles = {
about: {
get label () {
return isLinux ? 'About' : `About ${app.name}`
return isLinux ? 'About' : `About ${app.name}`;
}
},
close: {
@@ -38,7 +38,7 @@ const roles = {
accelerator: 'Shift+CmdOrCtrl+R',
nonNativeMacOSRole: true,
windowMethod: (window) => {
window.webContents.reloadIgnoringCache()
window.webContents.reloadIgnoringCache();
}
},
front: {
@@ -49,7 +49,7 @@ const roles = {
},
hide: {
get label () {
return `Hide ${app.name}`
return `Hide ${app.name}`;
},
accelerator: 'Command+H'
},
@@ -77,9 +77,9 @@ const roles = {
quit: {
get label () {
switch (process.platform) {
case 'darwin': return `Quit ${app.name}`
case 'win32': return 'Exit'
default: return 'Quit'
case 'darwin': return `Quit ${app.name}`;
case 'win32': return 'Exit';
default: return 'Quit';
}
},
accelerator: isWindows ? undefined : 'CommandOrControl+Q',
@@ -101,7 +101,7 @@ const roles = {
accelerator: 'CommandOrControl+0',
nonNativeMacOSRole: true,
webContentsMethod: (webContents) => {
webContents.zoomLevel = 0
webContents.zoomLevel = 0;
}
},
selectall: {
@@ -134,7 +134,7 @@ const roles = {
label: 'Toggle Full Screen',
accelerator: isMac ? 'Control+Command+F' : 'F11',
windowMethod: (window) => {
window.setFullScreen(!window.isFullScreen())
window.setFullScreen(!window.isFullScreen());
}
},
undo: {
@@ -156,7 +156,7 @@ const roles = {
accelerator: 'CommandOrControl+Plus',
nonNativeMacOSRole: true,
webContentsMethod: (webContents) => {
webContents.zoomLevel += 0.5
webContents.zoomLevel += 0.5;
}
},
zoomout: {
@@ -164,13 +164,13 @@ const roles = {
accelerator: 'CommandOrControl+-',
nonNativeMacOSRole: true,
webContentsMethod: (webContents) => {
webContents.zoomLevel -= 0.5
webContents.zoomLevel -= 0.5;
}
},
// App submenu should be used for Mac only
appmenu: {
get label () {
return app.name
return app.name;
},
submenu: [
{ role: 'about' },
@@ -249,71 +249,71 @@ const roles = {
])
]
}
}
};
exports.roleList = roles
exports.roleList = roles;
const canExecuteRole = (role) => {
if (!roles.hasOwnProperty(role)) return false
if (!isMac) return true
if (!roles.hasOwnProperty(role)) return false;
if (!isMac) return true;
// macOS handles all roles natively except for a few
return roles[role].nonNativeMacOSRole
}
return roles[role].nonNativeMacOSRole;
};
exports.getDefaultLabel = (role) => {
return roles.hasOwnProperty(role) ? roles[role].label : ''
}
return roles.hasOwnProperty(role) ? roles[role].label : '';
};
exports.getDefaultAccelerator = (role) => {
if (roles.hasOwnProperty(role)) return roles[role].accelerator
}
if (roles.hasOwnProperty(role)) return roles[role].accelerator;
};
exports.shouldRegisterAccelerator = (role) => {
const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined
return hasRoleRegister ? roles[role].registerAccelerator : true
}
const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined;
return hasRoleRegister ? roles[role].registerAccelerator : true;
};
exports.getDefaultSubmenu = (role) => {
if (!roles.hasOwnProperty(role)) return
if (!roles.hasOwnProperty(role)) return;
let { submenu } = roles[role]
let { submenu } = roles[role];
// remove null items from within the submenu
if (Array.isArray(submenu)) {
submenu = submenu.filter((item) => item != null)
submenu = submenu.filter((item) => item != null);
}
return submenu
}
return submenu;
};
exports.execute = (role, focusedWindow, focusedWebContents) => {
if (!canExecuteRole(role)) return false
if (!canExecuteRole(role)) return false;
const { appMethod, webContentsMethod, windowMethod } = roles[role]
const { appMethod, webContentsMethod, windowMethod } = roles[role];
if (appMethod) {
app[appMethod]()
return true
app[appMethod]();
return true;
}
if (windowMethod && focusedWindow != null) {
if (typeof windowMethod === 'function') {
windowMethod(focusedWindow)
windowMethod(focusedWindow);
} else {
focusedWindow[windowMethod]()
focusedWindow[windowMethod]();
}
return true
return true;
}
if (webContentsMethod && focusedWebContents != null) {
if (typeof webContentsMethod === 'function') {
webContentsMethod(focusedWebContents)
webContentsMethod(focusedWebContents);
} else {
focusedWebContents[webContentsMethod]()
focusedWebContents[webContentsMethod]();
}
return true
return true;
}
return false
}
return false;
};

View File

@@ -1,87 +1,87 @@
'use strict'
'use strict';
const roles = require('@electron/internal/browser/api/menu-item-roles')
const roles = require('@electron/internal/browser/api/menu-item-roles');
let nextCommandId = 0
let nextCommandId = 0;
const MenuItem = function (options) {
const { Menu } = require('electron')
const { Menu } = require('electron');
// Preserve extra fields specified by user
for (const key in options) {
if (!(key in this)) this[key] = options[key]
if (!(key in this)) this[key] = options[key];
}
if (typeof this.role === 'string' || this.role instanceof String) {
this.role = this.role.toLowerCase()
this.role = this.role.toLowerCase();
}
this.submenu = this.submenu || roles.getDefaultSubmenu(this.role)
this.submenu = this.submenu || roles.getDefaultSubmenu(this.role);
if (this.submenu != null && this.submenu.constructor !== Menu) {
this.submenu = Menu.buildFromTemplate(this.submenu)
this.submenu = Menu.buildFromTemplate(this.submenu);
}
if (this.type == null && this.submenu != null) {
this.type = 'submenu'
this.type = 'submenu';
}
if (this.type === 'submenu' && (this.submenu == null || this.submenu.constructor !== Menu)) {
throw new Error('Invalid submenu')
throw new Error('Invalid submenu');
}
this.overrideReadOnlyProperty('type', 'normal')
this.overrideReadOnlyProperty('role')
this.overrideReadOnlyProperty('accelerator')
this.overrideReadOnlyProperty('icon')
this.overrideReadOnlyProperty('submenu')
this.overrideReadOnlyProperty('type', 'normal');
this.overrideReadOnlyProperty('role');
this.overrideReadOnlyProperty('accelerator');
this.overrideReadOnlyProperty('icon');
this.overrideReadOnlyProperty('submenu');
this.overrideProperty('label', roles.getDefaultLabel(this.role))
this.overrideProperty('sublabel', '')
this.overrideProperty('toolTip', '')
this.overrideProperty('enabled', true)
this.overrideProperty('visible', true)
this.overrideProperty('checked', false)
this.overrideProperty('acceleratorWorksWhenHidden', true)
this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role))
this.overrideProperty('label', roles.getDefaultLabel(this.role));
this.overrideProperty('sublabel', '');
this.overrideProperty('toolTip', '');
this.overrideProperty('enabled', true);
this.overrideProperty('visible', true);
this.overrideProperty('checked', false);
this.overrideProperty('acceleratorWorksWhenHidden', true);
this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role));
if (!MenuItem.types.includes(this.type)) {
throw new Error(`Unknown menu item type: ${this.type}`)
throw new Error(`Unknown menu item type: ${this.type}`);
}
this.overrideReadOnlyProperty('commandId', ++nextCommandId)
this.overrideReadOnlyProperty('commandId', ++nextCommandId);
const click = options.click
const click = options.click;
this.click = (event, focusedWindow, focusedWebContents) => {
// Manually flip the checked flags when clicked.
if (this.type === 'checkbox' || this.type === 'radio') {
this.checked = !this.checked
this.checked = !this.checked;
}
if (!roles.execute(this.role, focusedWindow, focusedWebContents)) {
if (typeof click === 'function') {
click(this, focusedWindow, event)
click(this, focusedWindow, event);
} else if (typeof this.selector === 'string' && process.platform === 'darwin') {
Menu.sendActionToFirstResponder(this.selector)
Menu.sendActionToFirstResponder(this.selector);
}
}
}
}
};
};
MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio']
MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'];
MenuItem.prototype.getDefaultRoleAccelerator = function () {
return roles.getDefaultAccelerator(this.role)
}
return roles.getDefaultAccelerator(this.role);
};
MenuItem.prototype.overrideProperty = function (name, defaultValue = null) {
if (this[name] == null) {
this[name] = defaultValue
this[name] = defaultValue;
}
}
};
MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) {
this.overrideProperty(name, defaultValue)
this.overrideProperty(name, defaultValue);
Object.defineProperty(this, name, {
enumerable: true,
writable: false,
value: this[name]
})
}
});
};
module.exports = MenuItem
module.exports = MenuItem;

View File

@@ -1,41 +1,41 @@
'use strict'
'use strict';
function splitArray (arr, predicate) {
const result = arr.reduce((multi, item) => {
const current = multi[multi.length - 1]
const current = multi[multi.length - 1];
if (predicate(item)) {
if (current.length > 0) multi.push([])
if (current.length > 0) multi.push([]);
} else {
current.push(item)
current.push(item);
}
return multi
}, [[]])
return multi;
}, [[]]);
if (result[result.length - 1].length === 0) {
return result.slice(0, result.length - 1)
return result.slice(0, result.length - 1);
}
return result
return result;
}
function joinArrays (arrays, joinIDs) {
return arrays.reduce((joined, arr, i) => {
if (i > 0 && arr.length) {
if (joinIDs.length > 0) {
joined.push(joinIDs[0])
joinIDs.splice(0, 1)
joined.push(joinIDs[0]);
joinIDs.splice(0, 1);
} else {
joined.push({ type: 'separator' })
joined.push({ type: 'separator' });
}
}
return joined.concat(arr)
}, [])
return joined.concat(arr);
}, []);
}
function pushOntoMultiMap (map, key, value) {
if (!map.has(key)) {
map.set(key, [])
map.set(key, []);
}
map.get(key).push(value)
map.get(key).push(value);
}
function indexOfGroupContainingID (groups, id, ignoreGroup) {
@@ -45,102 +45,102 @@ function indexOfGroupContainingID (groups, id, ignoreGroup) {
candidateGroup.some(
candidateItem => candidateItem.id === id
)
)
);
}
// Sort nodes topologically using a depth-first approach. Encountered cycles
// are broken.
function sortTopologically (originalOrder, edgesById) {
const sorted = []
const marked = new Set()
const sorted = [];
const marked = new Set();
const visit = (mark) => {
if (marked.has(mark)) return
marked.add(mark)
const edges = edgesById.get(mark)
if (marked.has(mark)) return;
marked.add(mark);
const edges = edgesById.get(mark);
if (edges != null) {
edges.forEach(visit)
edges.forEach(visit);
}
sorted.push(mark)
}
sorted.push(mark);
};
originalOrder.forEach(visit)
return sorted
originalOrder.forEach(visit);
return sorted;
}
function attemptToMergeAGroup (groups) {
for (let i = 0; i < groups.length; i++) {
const group = groups[i]
const group = groups[i];
for (const item of group) {
const toIDs = [...(item.before || []), ...(item.after || [])]
const toIDs = [...(item.before || []), ...(item.after || [])];
for (const id of toIDs) {
const index = indexOfGroupContainingID(groups, id, group)
if (index === -1) continue
const mergeTarget = groups[index]
const index = indexOfGroupContainingID(groups, id, group);
if (index === -1) continue;
const mergeTarget = groups[index];
mergeTarget.push(...group)
groups.splice(i, 1)
return true
mergeTarget.push(...group);
groups.splice(i, 1);
return true;
}
}
}
return false
return false;
}
function mergeGroups (groups) {
let merged = true
let merged = true;
while (merged) {
merged = attemptToMergeAGroup(groups)
merged = attemptToMergeAGroup(groups);
}
return groups
return groups;
}
function sortItemsInGroup (group) {
const originalOrder = group.map((node, i) => i)
const edges = new Map()
const idToIndex = new Map(group.map((item, i) => [item.id, i]))
const originalOrder = group.map((node, i) => i);
const edges = new Map();
const idToIndex = new Map(group.map((item, i) => [item.id, i]));
group.forEach((item, i) => {
if (item.before) {
item.before.forEach(toID => {
const to = idToIndex.get(toID)
const to = idToIndex.get(toID);
if (to != null) {
pushOntoMultiMap(edges, to, i)
pushOntoMultiMap(edges, to, i);
}
})
});
}
if (item.after) {
item.after.forEach(toID => {
const to = idToIndex.get(toID)
const to = idToIndex.get(toID);
if (to != null) {
pushOntoMultiMap(edges, i, to)
pushOntoMultiMap(edges, i, to);
}
})
});
}
})
});
const sortedNodes = sortTopologically(originalOrder, edges)
return sortedNodes.map(i => group[i])
const sortedNodes = sortTopologically(originalOrder, edges);
return sortedNodes.map(i => group[i]);
}
function findEdgesInGroup (groups, i, edges) {
const group = groups[i]
const group = groups[i];
for (const item of group) {
if (item.beforeGroupContaining) {
for (const id of item.beforeGroupContaining) {
const to = indexOfGroupContainingID(groups, id, group)
const to = indexOfGroupContainingID(groups, id, group);
if (to !== -1) {
pushOntoMultiMap(edges, to, i)
return
pushOntoMultiMap(edges, to, i);
return;
}
}
}
if (item.afterGroupContaining) {
for (const id of item.afterGroupContaining) {
const to = indexOfGroupContainingID(groups, id, group)
const to = indexOfGroupContainingID(groups, id, group);
if (to !== -1) {
pushOntoMultiMap(edges, i, to)
return
pushOntoMultiMap(edges, i, to);
return;
}
}
}
@@ -148,29 +148,29 @@ function findEdgesInGroup (groups, i, edges) {
}
function sortGroups (groups) {
const originalOrder = groups.map((item, i) => i)
const edges = new Map()
const originalOrder = groups.map((item, i) => i);
const edges = new Map();
for (let i = 0; i < groups.length; i++) {
findEdgesInGroup(groups, i, edges)
findEdgesInGroup(groups, i, edges);
}
const sortedGroupIndexes = sortTopologically(originalOrder, edges)
return sortedGroupIndexes.map(i => groups[i])
const sortedGroupIndexes = sortTopologically(originalOrder, edges);
return sortedGroupIndexes.map(i => groups[i]);
}
function sortMenuItems (menuItems) {
const isSeparator = (item) => item.type === 'separator'
const separators = menuItems.filter(i => i.type === 'separator')
const isSeparator = (item) => item.type === 'separator';
const separators = menuItems.filter(i => i.type === 'separator');
// Split the items into their implicit groups based upon separators.
const groups = splitArray(menuItems, isSeparator)
const mergedGroups = mergeGroups(groups)
const mergedGroupsWithSortedItems = mergedGroups.map(sortItemsInGroup)
const sortedGroups = sortGroups(mergedGroupsWithSortedItems)
const groups = splitArray(menuItems, isSeparator);
const mergedGroups = mergeGroups(groups);
const mergedGroupsWithSortedItems = mergedGroups.map(sortItemsInGroup);
const sortedGroups = sortGroups(mergedGroupsWithSortedItems);
const joined = joinArrays(sortedGroups, separators)
return joined
const joined = joinArrays(sortedGroups, separators);
return joined;
}
module.exports = { sortMenuItems }
module.exports = { sortMenuItems };

View File

@@ -1,16 +1,16 @@
'use strict'
'use strict';
const { TopLevelWindow, MenuItem, webContents } = require('electron')
const { sortMenuItems } = require('@electron/internal/browser/api/menu-utils')
const EventEmitter = require('events').EventEmitter
const v8Util = process.electronBinding('v8_util')
const bindings = process.electronBinding('menu')
const { TopLevelWindow, MenuItem, webContents } = require('electron');
const { sortMenuItems } = require('@electron/internal/browser/api/menu-utils');
const EventEmitter = require('events').EventEmitter;
const v8Util = process.electronBinding('v8_util');
const bindings = process.electronBinding('menu');
const { Menu } = bindings
let applicationMenu = null
let groupIdIndex = 0
const { Menu } = bindings;
let applicationMenu = null;
let groupIdIndex = 0;
Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype)
Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype);
// Menu Delegate.
// This object should hold no reference to |Menu| to avoid cyclic reference.
@@ -20,172 +20,172 @@ const delegate = {
shouldCommandIdWorkWhenHidden: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].acceleratorWorksWhenHidden : undefined,
isCommandIdVisible: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].visible : undefined,
getAcceleratorForCommandId: (menu, id, useDefaultAccelerator) => {
const command = menu.commandsMap[id]
if (!command) return
if (command.accelerator != null) return command.accelerator
if (useDefaultAccelerator) return command.getDefaultRoleAccelerator()
const command = menu.commandsMap[id];
if (!command) return;
if (command.accelerator != null) return command.accelerator;
if (useDefaultAccelerator) return command.getDefaultRoleAccelerator();
},
shouldRegisterAcceleratorForCommandId: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].registerAccelerator : undefined,
executeCommand: (menu, event, id) => {
const command = menu.commandsMap[id]
if (!command) return
command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents())
const command = menu.commandsMap[id];
if (!command) return;
command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents());
},
menuWillShow: (menu) => {
// Ensure radio groups have at least one menu item selected
for (const id in menu.groupsMap) {
const found = menu.groupsMap[id].find(item => item.checked) || null
if (!found) v8Util.setHiddenValue(menu.groupsMap[id][0], 'checked', true)
for (const id of Object.keys(menu.groupsMap)) {
const found = menu.groupsMap[id].find(item => item.checked) || null;
if (!found) v8Util.setHiddenValue(menu.groupsMap[id][0], 'checked', true);
}
}
}
};
/* Instance Methods */
Menu.prototype._init = function () {
this.commandsMap = {}
this.groupsMap = {}
this.items = []
this.delegate = delegate
}
this.commandsMap = {};
this.groupsMap = {};
this.items = [];
this.delegate = delegate;
};
Menu.prototype.popup = function (options = {}) {
if (options == null || typeof options !== 'object') {
throw new TypeError('Options must be an object')
throw new TypeError('Options must be an object');
}
let { window, x, y, positioningItem, callback } = options
let { window, x, y, positioningItem, callback } = options;
// no callback passed
if (!callback || typeof callback !== 'function') callback = () => {}
if (!callback || typeof callback !== 'function') callback = () => {};
// set defaults
if (typeof x !== 'number') x = -1
if (typeof y !== 'number') y = -1
if (typeof positioningItem !== 'number') positioningItem = -1
if (typeof x !== 'number') x = -1;
if (typeof y !== 'number') y = -1;
if (typeof positioningItem !== 'number') positioningItem = -1;
// find which window to use
const wins = TopLevelWindow.getAllWindows()
const wins = TopLevelWindow.getAllWindows();
if (!wins || wins.indexOf(window) === -1) {
window = TopLevelWindow.getFocusedWindow()
window = TopLevelWindow.getFocusedWindow();
if (!window && wins && wins.length > 0) {
window = wins[0]
window = wins[0];
}
if (!window) {
throw new Error(`Cannot open Menu without a TopLevelWindow present`)
throw new Error(`Cannot open Menu without a TopLevelWindow present`);
}
}
this.popupAt(window, x, y, positioningItem, callback)
return { browserWindow: window, x, y, position: positioningItem }
}
this.popupAt(window, x, y, positioningItem, callback);
return { browserWindow: window, x, y, position: positioningItem };
};
Menu.prototype.closePopup = function (window) {
if (window instanceof TopLevelWindow) {
this.closePopupAt(window.id)
this.closePopupAt(window.id);
} else {
// Passing -1 (invalid) would make closePopupAt close the all menu runners
// belong to this menu.
this.closePopupAt(-1)
this.closePopupAt(-1);
}
}
};
Menu.prototype.getMenuItemById = function (id) {
const items = this.items
const items = this.items;
let found = items.find(item => item.id === id) || null
let found = items.find(item => item.id === id) || null;
for (let i = 0; !found && i < items.length; i++) {
if (items[i].submenu) {
found = items[i].submenu.getMenuItemById(id)
found = items[i].submenu.getMenuItemById(id);
}
}
return found
}
return found;
};
Menu.prototype.append = function (item) {
return this.insert(this.getItemCount(), item)
}
return this.insert(this.getItemCount(), item);
};
Menu.prototype.insert = function (pos, item) {
if ((item ? item.constructor : void 0) !== MenuItem) {
throw new TypeError('Invalid item')
throw new TypeError('Invalid item');
}
if (pos < 0) {
throw new RangeError(`Position ${pos} cannot be less than 0`)
throw new RangeError(`Position ${pos} cannot be less than 0`);
} else if (pos > this.getItemCount()) {
throw new RangeError(`Position ${pos} cannot be greater than the total MenuItem count`)
throw new RangeError(`Position ${pos} cannot be greater than the total MenuItem count`);
}
// insert item depending on its type
insertItemByType.call(this, item, pos)
insertItemByType.call(this, item, pos);
// set item properties
if (item.sublabel) this.setSublabel(pos, item.sublabel)
if (item.toolTip) this.setToolTip(pos, item.toolTip)
if (item.icon) this.setIcon(pos, item.icon)
if (item.role) this.setRole(pos, item.role)
if (item.sublabel) this.setSublabel(pos, item.sublabel);
if (item.toolTip) this.setToolTip(pos, item.toolTip);
if (item.icon) this.setIcon(pos, item.icon);
if (item.role) this.setRole(pos, item.role);
// Make menu accessable to items.
item.overrideReadOnlyProperty('menu', this)
item.overrideReadOnlyProperty('menu', this);
// Remember the items.
this.items.splice(pos, 0, item)
this.commandsMap[item.commandId] = item
}
this.items.splice(pos, 0, item);
this.commandsMap[item.commandId] = item;
};
Menu.prototype._callMenuWillShow = function () {
if (this.delegate) this.delegate.menuWillShow(this)
if (this.delegate) this.delegate.menuWillShow(this);
this.items.forEach(item => {
if (item.submenu) item.submenu._callMenuWillShow()
})
}
if (item.submenu) item.submenu._callMenuWillShow();
});
};
/* Static Methods */
Menu.getApplicationMenu = () => applicationMenu
Menu.getApplicationMenu = () => applicationMenu;
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder;
// set application menu with a preexisting menu
Menu.setApplicationMenu = function (menu) {
if (menu && menu.constructor !== Menu) {
throw new TypeError('Invalid menu')
throw new TypeError('Invalid menu');
}
applicationMenu = menu
v8Util.setHiddenValue(global, 'applicationMenuSet', true)
applicationMenu = menu;
v8Util.setHiddenValue(global, 'applicationMenuSet', true);
if (process.platform === 'darwin') {
if (!menu) return
menu._callMenuWillShow()
bindings.setApplicationMenu(menu)
if (!menu) return;
menu._callMenuWillShow();
bindings.setApplicationMenu(menu);
} else {
const windows = TopLevelWindow.getAllWindows()
return windows.map(w => w.setMenu(menu))
const windows = TopLevelWindow.getAllWindows();
return windows.map(w => w.setMenu(menu));
}
}
};
Menu.buildFromTemplate = function (template) {
if (!Array.isArray(template)) {
throw new TypeError('Invalid template for Menu: Menu template must be an array')
throw new TypeError('Invalid template for Menu: Menu template must be an array');
}
if (!areValidTemplateItems(template)) {
throw new TypeError('Invalid template for MenuItem: must have at least one of label, role or type')
throw new TypeError('Invalid template for MenuItem: must have at least one of label, role or type');
}
const filtered = removeExtraSeparators(template)
const sorted = sortTemplate(filtered)
const filtered = removeExtraSeparators(template);
const sorted = sortTemplate(filtered);
const menu = new Menu()
const menu = new Menu();
sorted.forEach(item => {
if (item instanceof MenuItem) {
menu.append(item)
menu.append(item);
} else {
menu.append(new MenuItem(item))
menu.append(new MenuItem(item));
}
})
});
return menu
}
return menu;
};
/* Helper Functions */
@@ -196,51 +196,50 @@ function areValidTemplateItems (template) {
typeof item === 'object' &&
(item.hasOwnProperty('label') ||
item.hasOwnProperty('role') ||
item.type === 'separator'))
item.type === 'separator'));
}
function sortTemplate (template) {
const sorted = sortMenuItems(template)
for (const id in sorted) {
const item = sorted[id]
const sorted = sortMenuItems(template);
for (const item of sorted) {
if (Array.isArray(item.submenu)) {
item.submenu = sortTemplate(item.submenu)
item.submenu = sortTemplate(item.submenu);
}
}
return sorted
return sorted;
}
// Search between separators to find a radio menu item and return its group id
function generateGroupId (items, pos) {
if (pos > 0) {
for (let idx = pos - 1; idx >= 0; idx--) {
if (items[idx].type === 'radio') return items[idx].groupId
if (items[idx].type === 'separator') break
if (items[idx].type === 'radio') return items[idx].groupId;
if (items[idx].type === 'separator') break;
}
} else if (pos < items.length) {
for (let idx = pos; idx <= items.length - 1; idx++) {
if (items[idx].type === 'radio') return items[idx].groupId
if (items[idx].type === 'separator') break
if (items[idx].type === 'radio') return items[idx].groupId;
if (items[idx].type === 'separator') break;
}
}
groupIdIndex += 1
return groupIdIndex
groupIdIndex += 1;
return groupIdIndex;
}
function removeExtraSeparators (items) {
// fold adjacent separators together
let ret = items.filter((e, idx, arr) => {
if (e.visible === false) return true
return e.type !== 'separator' || idx === 0 || arr[idx - 1].type !== 'separator'
})
if (e.visible === false) return true;
return e.type !== 'separator' || idx === 0 || arr[idx - 1].type !== 'separator';
});
// remove edge separators
ret = ret.filter((e, idx, arr) => {
if (e.visible === false) return true
return e.type !== 'separator' || (idx !== 0 && idx !== arr.length - 1)
})
if (e.visible === false) return true;
return e.type !== 'separator' || (idx !== 0 && idx !== arr.length - 1);
});
return ret
return ret;
}
function insertItemByType (item, pos) {
@@ -251,28 +250,28 @@ function insertItemByType (item, pos) {
submenu: () => this.insertSubMenu(pos, item.commandId, item.label, item.submenu),
radio: () => {
// Grouping radio menu items
item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos))
item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos));
if (this.groupsMap[item.groupId] == null) {
this.groupsMap[item.groupId] = []
this.groupsMap[item.groupId] = [];
}
this.groupsMap[item.groupId].push(item)
this.groupsMap[item.groupId].push(item);
// Setting a radio menu item should flip other items in the group.
v8Util.setHiddenValue(item, 'checked', item.checked)
v8Util.setHiddenValue(item, 'checked', item.checked);
Object.defineProperty(item, 'checked', {
enumerable: true,
get: () => v8Util.getHiddenValue(item, 'checked'),
set: () => {
this.groupsMap[item.groupId].forEach(other => {
if (other !== item) v8Util.setHiddenValue(other, 'checked', false)
})
v8Util.setHiddenValue(item, 'checked', true)
if (other !== item) v8Util.setHiddenValue(other, 'checked', false);
});
v8Util.setHiddenValue(item, 'checked', true);
}
})
this.insertRadioItem(pos, item.commandId, item.label, item.groupId)
});
this.insertRadioItem(pos, item.commandId, item.label, item.groupId);
}
}
types[item.type]()
};
types[item.type]();
}
module.exports = Menu
module.exports = Menu;

View File

@@ -1,11 +1,11 @@
'use strict'
'use strict';
// TODO: Figure out a way to not duplicate this information between here and module-list
// It is currently duplicated as module-list "require"s all the browser API file and the
// remote module in the renderer process depends on that file. As a result webpack
// includes all the browser API files in the renderer process as well and we want to avoid that
const features = process.electronBinding('features')
const features = process.electronBinding('features');
// Browser side modules, please sort alphabetically.
module.exports = [
@@ -37,7 +37,7 @@ module.exports = [
{ name: 'View' },
{ name: 'webContents' },
{ name: 'WebContentsView' }
]
];
if (features.isViewApiEnabled()) {
module.exports.push(
@@ -48,5 +48,5 @@ if (features.isViewApiEnabled()) {
{ name: 'MdTextButton' },
{ name: 'ResizeArea' },
{ name: 'TextField' }
)
);
}

View File

@@ -1,8 +1,8 @@
'use strict'
'use strict';
// TODO: Updating this file also required updating the module-keys file
const features = process.electronBinding('features')
const features = process.electronBinding('features');
// Browser side modules, please sort alphabetically.
module.exports = [
@@ -34,7 +34,7 @@ module.exports = [
{ name: 'View', loader: () => require('./view') },
{ name: 'webContents', loader: () => require('./web-contents') },
{ name: 'WebContentsView', loader: () => require('./web-contents-view') }
]
];
if (features.isViewApiEnabled()) {
module.exports.push(
@@ -45,5 +45,5 @@ if (features.isViewApiEnabled()) {
{ name: 'MdTextButton', loader: () => require('./views/md-text-button') },
{ name: 'ResizeArea', loader: () => require('./views/resize-area') },
{ name: 'TextField', loader: () => require('./views/text-field') }
)
);
}

View File

@@ -1,8 +1,8 @@
import { EventEmitter } from 'events'
import { EventEmitter } from 'events';
const { NativeTheme, nativeTheme } = process.electronBinding('native_theme')
const { NativeTheme, nativeTheme } = process.electronBinding('native_theme');
Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype)
EventEmitter.call(nativeTheme as any)
Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype);
EventEmitter.call(nativeTheme as any);
module.exports = nativeTheme
module.exports = nativeTheme;

View File

@@ -1,32 +1,32 @@
'use strict'
'use strict';
// TODO(deepak1556): Deprecate and remove standalone netLog module,
// it is now a property of session module.
const { app, session } = require('electron')
const { app, session } = require('electron');
// Fallback to default session.
Object.setPrototypeOf(module.exports, new Proxy({}, {
get (target, property) {
if (!app.isReady()) return
if (!app.isReady()) return;
const netLog = session.defaultSession.netLog
const netLog = session.defaultSession.netLog;
if (!Object.getPrototypeOf(netLog).hasOwnProperty(property)) return
if (!Object.getPrototypeOf(netLog).hasOwnProperty(property)) return;
// check for properties on the prototype chain that aren't functions
if (typeof netLog[property] !== 'function') return netLog[property]
if (typeof netLog[property] !== 'function') return netLog[property];
// Returning a native function directly would throw error.
return (...args) => netLog[property](...args)
return (...args) => netLog[property](...args);
},
ownKeys () {
if (!app.isReady()) return []
if (!app.isReady()) return [];
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog))
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog));
},
getOwnPropertyDescriptor (target) {
return { configurable: true, enumerable: true }
return { configurable: true, enumerable: true };
}
}))
}));

View File

@@ -1,16 +1,16 @@
'use strict'
'use strict';
const url = require('url')
const { EventEmitter } = require('events')
const { Readable, Writable } = require('stream')
const { app } = require('electron')
const { Session } = process.electronBinding('session')
const { net, Net, _isValidHeaderName, _isValidHeaderValue } = process.electronBinding('net')
const { URLLoader } = net
const url = require('url');
const { EventEmitter } = require('events');
const { Readable, Writable } = require('stream');
const { app } = require('electron');
const { Session } = process.electronBinding('session');
const { net, Net, _isValidHeaderName, _isValidHeaderValue } = process.electronBinding('net');
const { URLLoader } = net;
Object.setPrototypeOf(URLLoader.prototype, EventEmitter.prototype)
Object.setPrototypeOf(URLLoader.prototype, EventEmitter.prototype);
const kSupportedProtocols = new Set(['http:', 'https:'])
const kSupportedProtocols = new Set(['http:', 'https:']);
// set of headers that Node.js discards duplicates for
// see https://nodejs.org/api/http.html#http_message_headers
@@ -33,27 +33,27 @@ const discardableDuplicateHeaders = new Set([
'server',
'age',
'expires'
])
]);
class IncomingMessage extends Readable {
constructor (responseHead) {
super()
this._shouldPush = false
this._data = []
this._responseHead = responseHead
super();
this._shouldPush = false;
this._data = [];
this._responseHead = responseHead;
}
get statusCode () {
return this._responseHead.statusCode
return this._responseHead.statusCode;
}
get statusMessage () {
return this._responseHead.statusMessage
return this._responseHead.statusMessage;
}
get headers () {
const filteredHeaders = {}
const { rawHeaders } = this._responseHead
const filteredHeaders = {};
const { rawHeaders } = this._responseHead;
rawHeaders.forEach(header => {
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key) &&
discardableDuplicateHeaders.has(header.key)) {
@@ -63,145 +63,145 @@ class IncomingMessage extends Readable {
// keep set-cookie as an array per Node.js rules
// see https://nodejs.org/api/http.html#http_message_headers
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) {
filteredHeaders[header.key].push(header.value)
filteredHeaders[header.key].push(header.value);
} else {
filteredHeaders[header.key] = [header.value]
filteredHeaders[header.key] = [header.value];
}
} else {
// for non-cookie headers, the values are joined together with ', '
if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) {
filteredHeaders[header.key] += `, ${header.value}`
filteredHeaders[header.key] += `, ${header.value}`;
} else {
filteredHeaders[header.key] = header.value
filteredHeaders[header.key] = header.value;
}
}
}
})
return filteredHeaders
});
return filteredHeaders;
}
get httpVersion () {
return `${this.httpVersionMajor}.${this.httpVersionMinor}`
return `${this.httpVersionMajor}.${this.httpVersionMinor}`;
}
get httpVersionMajor () {
return this._responseHead.httpVersion.major
return this._responseHead.httpVersion.major;
}
get httpVersionMinor () {
return this._responseHead.httpVersion.minor
return this._responseHead.httpVersion.minor;
}
get rawTrailers () {
throw new Error('HTTP trailers are not supported')
throw new Error('HTTP trailers are not supported');
}
get trailers () {
throw new Error('HTTP trailers are not supported')
throw new Error('HTTP trailers are not supported');
}
_storeInternalData (chunk) {
this._data.push(chunk)
this._pushInternalData()
this._data.push(chunk);
this._pushInternalData();
}
_pushInternalData () {
while (this._shouldPush && this._data.length > 0) {
const chunk = this._data.shift()
this._shouldPush = this.push(chunk)
const chunk = this._data.shift();
this._shouldPush = this.push(chunk);
}
}
_read () {
this._shouldPush = true
this._pushInternalData()
this._shouldPush = true;
this._pushInternalData();
}
}
/** Writable stream that buffers up everything written to it. */
class SlurpStream extends Writable {
constructor () {
super()
this._data = Buffer.alloc(0)
super();
this._data = Buffer.alloc(0);
}
_write (chunk, encoding, callback) {
this._data = Buffer.concat([this._data, chunk])
callback()
this._data = Buffer.concat([this._data, chunk]);
callback();
}
data () { return this._data }
data () { return this._data; }
}
class ChunkedBodyStream extends Writable {
constructor (clientRequest) {
super()
this._clientRequest = clientRequest
super();
this._clientRequest = clientRequest;
}
_write (chunk, encoding, callback) {
if (this._downstream) {
this._downstream.write(chunk).then(callback, callback)
this._downstream.write(chunk).then(callback, callback);
} else {
// the contract of _write is that we won't be called again until we call
// the callback, so we're good to just save a single chunk.
this._pendingChunk = chunk
this._pendingCallback = callback
this._pendingChunk = chunk;
this._pendingCallback = callback;
// The first write to a chunked body stream begins the request.
this._clientRequest._startRequest()
this._clientRequest._startRequest();
}
}
_final (callback) {
this._downstream.done()
callback()
this._downstream.done();
callback();
}
startReading (pipe) {
if (this._downstream) {
throw new Error('two startReading calls???')
throw new Error('two startReading calls???');
}
this._downstream = pipe
this._downstream = pipe;
if (this._pendingChunk) {
const doneWriting = (maybeError) => {
const cb = this._pendingCallback
delete this._pendingCallback
delete this._pendingChunk
cb(maybeError)
}
this._downstream.write(this._pendingChunk).then(doneWriting, doneWriting)
const cb = this._pendingCallback;
delete this._pendingCallback;
delete this._pendingChunk;
cb(maybeError);
};
this._downstream.write(this._pendingChunk).then(doneWriting, doneWriting);
}
}
}
function parseOptions (options) {
if (typeof options === 'string') {
options = url.parse(options)
options = url.parse(options);
} else {
options = { ...options }
options = { ...options };
}
const method = (options.method || 'GET').toUpperCase()
let urlStr = options.url
const method = (options.method || 'GET').toUpperCase();
let urlStr = options.url;
if (!urlStr) {
const urlObj = {}
const protocol = options.protocol || 'http:'
const urlObj = {};
const protocol = options.protocol || 'http:';
if (!kSupportedProtocols.has(protocol)) {
throw new Error('Protocol "' + protocol + '" not supported')
throw new Error('Protocol "' + protocol + '" not supported');
}
urlObj.protocol = protocol
urlObj.protocol = protocol;
if (options.host) {
urlObj.host = options.host
urlObj.host = options.host;
} else {
if (options.hostname) {
urlObj.hostname = options.hostname
urlObj.hostname = options.hostname;
} else {
urlObj.hostname = 'localhost'
urlObj.hostname = 'localhost';
}
if (options.port) {
urlObj.port = options.port
urlObj.port = options.port;
}
}
@@ -212,22 +212,22 @@ function parseOptions (options) {
// well, and b) possibly too restrictive for real-world usage. That's
// why it only scans for spaces because those are guaranteed to create
// an invalid request.
throw new TypeError('Request path contains unescaped characters')
throw new TypeError('Request path contains unescaped characters');
}
const pathObj = url.parse(options.path || '/')
urlObj.pathname = pathObj.pathname
urlObj.search = pathObj.search
urlObj.hash = pathObj.hash
urlStr = url.format(urlObj)
const pathObj = url.parse(options.path || '/');
urlObj.pathname = pathObj.pathname;
urlObj.search = pathObj.search;
urlObj.hash = pathObj.hash;
urlStr = url.format(urlObj);
}
const redirectPolicy = options.redirect || 'follow'
const redirectPolicy = options.redirect || 'follow';
if (!['follow', 'error', 'manual'].includes(redirectPolicy)) {
throw new Error('redirect mode should be one of follow, error or manual')
throw new Error('redirect mode should be one of follow, error or manual');
}
if (options.headers != null && typeof options.headers !== 'object') {
throw new TypeError('headers must be an object')
throw new TypeError('headers must be an object');
}
const urlLoaderOptions = {
@@ -235,180 +235,180 @@ function parseOptions (options) {
url: urlStr,
redirectPolicy,
extraHeaders: options.headers || {}
}
};
for (const [name, value] of Object.entries(urlLoaderOptions.extraHeaders)) {
if (!_isValidHeaderName(name)) {
throw new Error(`Invalid header name: '${name}'`)
throw new Error(`Invalid header name: '${name}'`);
}
if (!_isValidHeaderValue(value.toString())) {
throw new Error(`Invalid value for header '${name}': '${value}'`)
throw new Error(`Invalid value for header '${name}': '${value}'`);
}
}
if (options.session) {
if (options.session instanceof Session) {
urlLoaderOptions.session = options.session
urlLoaderOptions.session = options.session;
} else {
throw new TypeError('`session` should be an instance of the Session class')
throw new TypeError('`session` should be an instance of the Session class');
}
} else if (options.partition) {
if (typeof options.partition === 'string') {
urlLoaderOptions.partition = options.partition
urlLoaderOptions.partition = options.partition;
} else {
throw new TypeError('`partition` should be a string')
throw new TypeError('`partition` should be a string');
}
}
return urlLoaderOptions
return urlLoaderOptions;
}
class ClientRequest extends Writable {
constructor (options, callback) {
super({ autoDestroy: true })
super({ autoDestroy: true });
if (!app.isReady()) {
throw new Error('net module can only be used after app is ready')
throw new Error('net module can only be used after app is ready');
}
if (callback) {
this.once('response', callback)
this.once('response', callback);
}
const { redirectPolicy, ...urlLoaderOptions } = parseOptions(options)
this._urlLoaderOptions = urlLoaderOptions
this._redirectPolicy = redirectPolicy
this._started = false
const { redirectPolicy, ...urlLoaderOptions } = parseOptions(options);
this._urlLoaderOptions = urlLoaderOptions;
this._redirectPolicy = redirectPolicy;
this._started = false;
}
set chunkedEncoding (value) {
if (this._started) {
throw new Error('chunkedEncoding can only be set before the request is started')
throw new Error('chunkedEncoding can only be set before the request is started');
}
if (typeof this._chunkedEncoding !== 'undefined') {
throw new Error('chunkedEncoding can only be set once')
throw new Error('chunkedEncoding can only be set once');
}
this._chunkedEncoding = !!value
this._chunkedEncoding = !!value;
if (this._chunkedEncoding) {
this._body = new ChunkedBodyStream(this)
this._body = new ChunkedBodyStream(this);
this._urlLoaderOptions.body = (pipe) => {
this._body.startReading(pipe)
}
this._body.startReading(pipe);
};
}
}
setHeader (name, value) {
if (typeof name !== 'string') {
throw new TypeError('`name` should be a string in setHeader(name, value)')
throw new TypeError('`name` should be a string in setHeader(name, value)');
}
if (value == null) {
throw new Error('`value` required in setHeader("' + name + '", value)')
throw new Error('`value` required in setHeader("' + name + '", value)');
}
if (this._started || this._firstWrite) {
throw new Error('Can\'t set headers after they are sent')
throw new Error('Can\'t set headers after they are sent');
}
if (!_isValidHeaderName(name)) {
throw new Error(`Invalid header name: '${name}'`)
throw new Error(`Invalid header name: '${name}'`);
}
if (!_isValidHeaderValue(value.toString())) {
throw new Error(`Invalid value for header '${name}': '${value}'`)
throw new Error(`Invalid value for header '${name}': '${value}'`);
}
const key = name.toLowerCase()
this._urlLoaderOptions.extraHeaders[key] = value
const key = name.toLowerCase();
this._urlLoaderOptions.extraHeaders[key] = value;
}
getHeader (name) {
if (name == null) {
throw new Error('`name` is required for getHeader(name)')
throw new Error('`name` is required for getHeader(name)');
}
const key = name.toLowerCase()
return this._urlLoaderOptions.extraHeaders[key]
const key = name.toLowerCase();
return this._urlLoaderOptions.extraHeaders[key];
}
removeHeader (name) {
if (name == null) {
throw new Error('`name` is required for removeHeader(name)')
throw new Error('`name` is required for removeHeader(name)');
}
if (this._started || this._firstWrite) {
throw new Error('Can\'t remove headers after they are sent')
throw new Error('Can\'t remove headers after they are sent');
}
const key = name.toLowerCase()
delete this._urlLoaderOptions.extraHeaders[key]
const key = name.toLowerCase();
delete this._urlLoaderOptions.extraHeaders[key];
}
_write (chunk, encoding, callback) {
this._firstWrite = true
this._firstWrite = true;
if (!this._body) {
this._body = new SlurpStream()
this._body = new SlurpStream();
this._body.on('finish', () => {
this._urlLoaderOptions.body = this._body.data()
this._startRequest()
})
this._urlLoaderOptions.body = this._body.data();
this._startRequest();
});
}
// TODO: is this the right way to forward to another stream?
this._body.write(chunk, encoding, callback)
this._body.write(chunk, encoding, callback);
}
_final (callback) {
if (this._body) {
// TODO: is this the right way to forward to another stream?
this._body.end(callback)
this._body.end(callback);
} else {
// end() called without a body, go ahead and start the request
this._startRequest()
callback()
this._startRequest();
callback();
}
}
_startRequest () {
this._started = true
this._started = true;
const stringifyValues = (obj) => {
const ret = {}
for (const k in obj) {
ret[k] = obj[k].toString()
const ret = {};
for (const k of Object.keys(obj)) {
ret[k] = obj[k].toString();
}
return ret
}
const opts = { ...this._urlLoaderOptions, extraHeaders: stringifyValues(this._urlLoaderOptions.extraHeaders) }
this._urlLoader = new URLLoader(opts)
return ret;
};
const opts = { ...this._urlLoaderOptions, extraHeaders: stringifyValues(this._urlLoaderOptions.extraHeaders) };
this._urlLoader = new URLLoader(opts);
this._urlLoader.on('response-started', (event, finalUrl, responseHead) => {
const response = this._response = new IncomingMessage(responseHead)
this.emit('response', response)
})
const response = this._response = new IncomingMessage(responseHead);
this.emit('response', response);
});
this._urlLoader.on('data', (event, data) => {
this._response._storeInternalData(Buffer.from(data))
})
this._response._storeInternalData(Buffer.from(data));
});
this._urlLoader.on('complete', () => {
if (this._response) { this._response._storeInternalData(null) }
})
if (this._response) { this._response._storeInternalData(null); }
});
this._urlLoader.on('error', (event, netErrorString) => {
const error = new Error(netErrorString)
if (this._response) this._response.destroy(error)
this._die(error)
})
const error = new Error(netErrorString);
if (this._response) this._response.destroy(error);
this._die(error);
});
this._urlLoader.on('login', (event, authInfo, callback) => {
const handled = this.emit('login', authInfo, callback)
const handled = this.emit('login', authInfo, callback);
if (!handled) {
// If there were no listeners, cancel the authentication request.
callback()
callback();
}
})
});
this._urlLoader.on('redirect', (event, redirectInfo, headers) => {
const { statusCode, newMethod, newUrl } = redirectInfo
const { statusCode, newMethod, newUrl } = redirectInfo;
if (this._redirectPolicy === 'error') {
this._die(new Error(`Attempted to redirect, but redirect policy was 'error'`))
this._die(new Error(`Attempted to redirect, but redirect policy was 'error'`));
} else if (this._redirectPolicy === 'manual') {
let _followRedirect = false
this._followRedirectCb = () => { _followRedirect = true }
let _followRedirect = false;
this._followRedirectCb = () => { _followRedirect = true; };
try {
this.emit('redirect', statusCode, newMethod, newUrl, headers)
this.emit('redirect', statusCode, newMethod, newUrl, headers);
} finally {
this._followRedirectCb = null
this._followRedirectCb = null;
if (!_followRedirect && !this._aborted) {
this._die(new Error('Redirect was cancelled'))
this._die(new Error('Redirect was cancelled'));
}
}
} else if (this._redirectPolicy === 'follow') {
@@ -416,61 +416,61 @@ class ClientRequest extends Writable {
// allowed but does nothing. (Perhaps it should throw an error
// though...? Since the redirect will happen regardless.)
try {
this._followRedirectCb = () => {}
this.emit('redirect', statusCode, newMethod, newUrl, headers)
this._followRedirectCb = () => {};
this.emit('redirect', statusCode, newMethod, newUrl, headers);
} finally {
this._followRedirectCb = null
this._followRedirectCb = null;
}
} else {
this._die(new Error(`Unexpected redirect policy '${this._redirectPolicy}'`))
this._die(new Error(`Unexpected redirect policy '${this._redirectPolicy}'`));
}
})
});
this._urlLoader.on('upload-progress', (event, position, total) => {
this._uploadProgress = { active: true, started: true, current: position, total }
this.emit('upload-progress', position, total) // Undocumented, for now
})
this._uploadProgress = { active: true, started: true, current: position, total };
this.emit('upload-progress', position, total); // Undocumented, for now
});
this._urlLoader.on('download-progress', (event, current) => {
if (this._response) {
this._response.emit('download-progress', current) // Undocumented, for now
this._response.emit('download-progress', current); // Undocumented, for now
}
})
});
}
followRedirect () {
if (this._followRedirectCb) {
this._followRedirectCb()
this._followRedirectCb();
} else {
throw new Error('followRedirect() called, but was not waiting for a redirect')
throw new Error('followRedirect() called, but was not waiting for a redirect');
}
}
abort () {
if (!this._aborted) {
process.nextTick(() => { this.emit('abort') })
process.nextTick(() => { this.emit('abort'); });
}
this._aborted = true
this._die()
this._aborted = true;
this._die();
}
_die (err) {
this.destroy(err)
this.destroy(err);
if (this._urlLoader) {
this._urlLoader.cancel()
if (this._response) this._response.destroy(err)
this._urlLoader.cancel();
if (this._response) this._response.destroy(err);
}
}
getUploadProgress () {
return this._uploadProgress ? { ...this._uploadProgress } : { active: false }
return this._uploadProgress ? { ...this._uploadProgress } : { active: false };
}
}
Net.prototype.request = function (options, callback) {
return new ClientRequest(options, callback)
}
return new ClientRequest(options, callback);
};
net.ClientRequest = ClientRequest
net.ClientRequest = ClientRequest;
module.exports = net
module.exports = net;

View File

@@ -1,10 +1,10 @@
'use strict'
'use strict';
const { EventEmitter } = require('events')
const { Notification, isSupported } = process.electronBinding('notification')
const { EventEmitter } = require('events');
const { Notification, isSupported } = process.electronBinding('notification');
Object.setPrototypeOf(Notification.prototype, EventEmitter.prototype)
Object.setPrototypeOf(Notification.prototype, EventEmitter.prototype);
Notification.isSupported = isSupported
Notification.isSupported = isSupported;
module.exports = Notification
module.exports = Notification;

View File

@@ -1,14 +1,14 @@
'use strict'
'use strict';
import { createLazyInstance } from '../utils'
import { createLazyInstance } from '../utils';
const { EventEmitter } = require('events')
const { createPowerMonitor, PowerMonitor } = process.electronBinding('power_monitor')
const { EventEmitter } = require('events');
const { createPowerMonitor, PowerMonitor } = process.electronBinding('power_monitor');
// PowerMonitor is an EventEmitter.
Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype)
Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype);
const powerMonitor = createLazyInstance(createPowerMonitor, PowerMonitor, true)
const powerMonitor = createLazyInstance(createPowerMonitor, PowerMonitor, true);
if (process.platform === 'linux') {
// In order to delay system shutdown when e.preventDefault() is invoked
@@ -18,21 +18,21 @@ if (process.platform === 'linux') {
//
// So here we watch for 'shutdown' listeners to be added or removed and
// set or unset our shutdown delay lock accordingly.
const { app } = require('electron')
const { app } = require('electron');
app.whenReady().then(() => {
powerMonitor.on('newListener', (event: string) => {
// whenever the listener count is incremented to one...
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
powerMonitor.blockShutdown()
powerMonitor.blockShutdown();
}
})
});
powerMonitor.on('removeListener', (event: string) => {
// whenever the listener count is decremented to zero...
if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) {
powerMonitor.unblockShutdown()
powerMonitor.unblockShutdown();
}
})
})
});
});
}
module.exports = powerMonitor
module.exports = powerMonitor;

View File

@@ -1,3 +1,3 @@
'use strict'
'use strict';
module.exports = process.electronBinding('power_save_blocker').powerSaveBlocker
module.exports = process.electronBinding('power_save_blocker').powerSaveBlocker;

View File

@@ -1,29 +1,29 @@
import { app, session } from 'electron'
import { app, session } from 'electron';
// Global protocol APIs.
const protocol = process.electronBinding('protocol')
const protocol = process.electronBinding('protocol');
// Fallback protocol APIs of default session.
Object.setPrototypeOf(protocol, new Proxy({}, {
get (_target, property) {
if (!app.isReady()) return
if (!app.isReady()) return;
const protocol = session.defaultSession!.protocol
if (!Object.getPrototypeOf(protocol).hasOwnProperty(property)) return
const protocol = session.defaultSession!.protocol;
if (!Object.getPrototypeOf(protocol).hasOwnProperty(property)) return;
// Returning a native function directly would throw error.
return (...args: any[]) => (protocol[property as keyof Electron.Protocol] as Function)(...args)
return (...args: any[]) => (protocol[property as keyof Electron.Protocol] as Function)(...args);
},
ownKeys () {
if (!app.isReady()) return []
if (!app.isReady()) return [];
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession!.protocol))
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession!.protocol));
},
getOwnPropertyDescriptor () {
return { configurable: true, enumerable: true }
return { configurable: true, enumerable: true };
}
}))
}));
export default protocol
export default protocol;

View File

@@ -1,10 +1,10 @@
'use strict'
'use strict';
import { createLazyInstance } from '../utils'
const { EventEmitter } = require('events')
const { Screen, createScreen } = process.electronBinding('screen')
import { createLazyInstance } from '../utils';
const { EventEmitter } = require('events');
const { Screen, createScreen } = process.electronBinding('screen');
// Screen is an EventEmitter.
Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype)
Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype);
module.exports = createLazyInstance(createScreen, Screen, true)
module.exports = createLazyInstance(createScreen, Screen, true);

View File

@@ -1,52 +1,53 @@
'use strict'
'use strict';
const { EventEmitter } = require('events')
const { app, deprecate } = require('electron')
const { fromPartition, Session, Cookies, NetLog, Protocol } = process.electronBinding('session')
const { EventEmitter } = require('events');
const { app, deprecate } = require('electron');
const { fromPartition, Session, Cookies, NetLog, Protocol } = process.electronBinding('session');
// Public API.
Object.defineProperties(exports, {
defaultSession: {
enumerable: true,
get () { return fromPartition('') }
get () { return fromPartition(''); }
},
fromPartition: {
enumerable: true,
value: fromPartition
}
})
});
Object.setPrototypeOf(Session.prototype, EventEmitter.prototype)
Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype)
Object.setPrototypeOf(Session.prototype, EventEmitter.prototype);
Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype);
Session.prototype._init = function () {
app.emit('session-created', this)
}
app.emit('session-created', this);
};
const _originalStartLogging = NetLog.prototype.startLogging
const _originalStartLogging = NetLog.prototype.startLogging;
NetLog.prototype.startLogging = function (path, ...args) {
this._currentlyLoggingPath = path
this._currentlyLoggingPath = path;
try {
return _originalStartLogging.call(this, path, ...args)
return _originalStartLogging.call(this, path, ...args);
} catch (e) {
this._currentlyLoggingPath = null
throw e
this._currentlyLoggingPath = null;
throw e;
}
}
};
const _originalStopLogging = NetLog.prototype.stopLogging
const _originalStopLogging = NetLog.prototype.stopLogging;
NetLog.prototype.stopLogging = function () {
this._currentlyLoggingPath = null
return _originalStopLogging.call(this)
}
const logPath = this._currentlyLoggingPath;
this._currentlyLoggingPath = null;
return _originalStopLogging.call(this).then(() => logPath);
};
const currentlyLoggingPathDeprecated = deprecate.warnOnce('currentlyLoggingPath')
const currentlyLoggingPathDeprecated = deprecate.warnOnce('currentlyLoggingPath');
Object.defineProperties(NetLog.prototype, {
currentlyLoggingPath: {
enumerable: true,
get () {
currentlyLoggingPathDeprecated()
return this._currentlyLoggingPath == null ? '' : this._currentlyLoggingPath
currentlyLoggingPathDeprecated();
return this._currentlyLoggingPath == null ? '' : this._currentlyLoggingPath;
}
}
})
});

View File

@@ -1,10 +1,10 @@
import { EventEmitter } from 'events'
import { deprecate } from 'electron'
const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences')
import { EventEmitter } from 'events';
import { deprecate } from 'electron';
const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences');
// SystemPreferences is an EventEmitter.
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype)
EventEmitter.call(systemPreferences)
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype);
EventEmitter.call(systemPreferences);
if ('appLevelAppearance' in systemPreferences) {
deprecate.fnToProperty(
@@ -12,7 +12,7 @@ if ('appLevelAppearance' in systemPreferences) {
'appLevelAppearance',
'_getAppLevelAppearance',
'_setAppLevelAppearance'
)
);
}
if ('effectiveAppearance' in systemPreferences) {
@@ -20,23 +20,23 @@ if ('effectiveAppearance' in systemPreferences) {
SystemPreferences.prototype,
'effectiveAppearance',
'_getEffectiveAppearance'
)
);
}
SystemPreferences.prototype.isDarkMode = deprecate.moveAPI(
SystemPreferences.prototype.isDarkMode,
'systemPreferences.isDarkMode()',
'nativeTheme.shouldUseDarkColors'
)
);
SystemPreferences.prototype.isInvertedColorScheme = deprecate.moveAPI(
SystemPreferences.prototype.isInvertedColorScheme,
'systemPreferences.isInvertedColorScheme()',
'nativeTheme.shouldUseInvertedColorScheme'
)
);
SystemPreferences.prototype.isHighContrastColorScheme = deprecate.moveAPI(
SystemPreferences.prototype.isHighContrastColorScheme,
'systemPreferences.isHighContrastColorScheme()',
'nativeTheme.shouldUseHighContrastColors'
)
);
module.exports = systemPreferences
module.exports = systemPreferences;

View File

@@ -1,24 +1,24 @@
'use strict'
'use strict';
const electron = require('electron')
const { EventEmitter } = require('events')
const { TopLevelWindow } = process.electronBinding('top_level_window')
const electron = require('electron');
const { EventEmitter } = require('events');
const { TopLevelWindow } = process.electronBinding('top_level_window');
Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype)
Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype);
TopLevelWindow.prototype._init = function () {
// Avoid recursive require.
const { app } = electron
const { app } = electron;
// Simulate the application menu on platforms other than macOS.
if (process.platform !== 'darwin') {
const menu = app.applicationMenu
if (menu) this.setMenu(menu)
const menu = app.applicationMenu;
if (menu) this.setMenu(menu);
}
}
};
TopLevelWindow.getFocusedWindow = () => {
return TopLevelWindow.getAllWindows().find((win) => win.isFocused())
}
return TopLevelWindow.getAllWindows().find((win) => win.isFocused());
};
module.exports = TopLevelWindow
module.exports = TopLevelWindow;

View File

@@ -1,337 +1,337 @@
'use strict'
'use strict';
const { EventEmitter } = require('events')
const { EventEmitter } = require('events');
let nextItemID = 1
let nextItemID = 1;
class TouchBar extends EventEmitter {
// Bind a touch bar to a window
static _setOnWindow (touchBar, window) {
if (window._touchBar != null) {
window._touchBar._removeFromWindow(window)
window._touchBar._removeFromWindow(window);
}
if (touchBar == null) {
window._setTouchBarItems([])
return
window._setTouchBarItems([]);
return;
}
if (Array.isArray(touchBar)) {
touchBar = new TouchBar(touchBar)
touchBar = new TouchBar(touchBar);
}
touchBar._addToWindow(window)
touchBar._addToWindow(window);
}
constructor (options) {
super()
super();
if (options == null) {
throw new Error('Must specify options object as first argument')
throw new Error('Must specify options object as first argument');
}
let { items, escapeItem } = options
let { items, escapeItem } = options;
if (!Array.isArray(items)) {
items = []
items = [];
}
this.changeListener = (item) => {
this.emit('change', item.id, item.type)
}
this.emit('change', item.id, item.type);
};
this.windowListeners = {}
this.items = {}
this.ordereredItems = []
this.escapeItem = escapeItem
this.windowListeners = {};
this.items = {};
this.ordereredItems = [];
this.escapeItem = escapeItem;
const registerItem = (item) => {
this.items[item.id] = item
item.on('change', this.changeListener)
this.items[item.id] = item;
item.on('change', this.changeListener);
if (item.child instanceof TouchBar) {
item.child.ordereredItems.forEach(registerItem)
item.child.ordereredItems.forEach(registerItem);
}
}
};
items.forEach((item) => {
if (!(item instanceof TouchBarItem)) {
throw new Error('Each item must be an instance of TouchBarItem')
throw new Error('Each item must be an instance of TouchBarItem');
}
this.ordereredItems.push(item)
registerItem(item)
})
this.ordereredItems.push(item);
registerItem(item);
});
}
set escapeItem (item) {
if (item != null && !(item instanceof TouchBarItem)) {
throw new Error('Escape item must be an instance of TouchBarItem')
throw new Error('Escape item must be an instance of TouchBarItem');
}
if (this.escapeItem != null) {
this.escapeItem.removeListener('change', this.changeListener)
this.escapeItem.removeListener('change', this.changeListener);
}
this._escapeItem = item
this._escapeItem = item;
if (this.escapeItem != null) {
this.escapeItem.on('change', this.changeListener)
this.escapeItem.on('change', this.changeListener);
}
this.emit('escape-item-change', item)
this.emit('escape-item-change', item);
}
get escapeItem () {
return this._escapeItem
return this._escapeItem;
}
_addToWindow (window) {
const { id } = window
const { id } = window;
// Already added to window
if (this.windowListeners.hasOwnProperty(id)) return
if (this.windowListeners.hasOwnProperty(id)) return;
window._touchBar = this
window._touchBar = this;
const changeListener = (itemID) => {
window._refreshTouchBarItem(itemID)
}
this.on('change', changeListener)
window._refreshTouchBarItem(itemID);
};
this.on('change', changeListener);
const escapeItemListener = (item) => {
window._setEscapeTouchBarItem(item != null ? item : {})
}
this.on('escape-item-change', escapeItemListener)
window._setEscapeTouchBarItem(item != null ? item : {});
};
this.on('escape-item-change', escapeItemListener);
const interactionListener = (event, itemID, details) => {
let item = this.items[itemID]
let item = this.items[itemID];
if (item == null && this.escapeItem != null && this.escapeItem.id === itemID) {
item = this.escapeItem
item = this.escapeItem;
}
if (item != null && item.onInteraction != null) {
item.onInteraction(details)
item.onInteraction(details);
}
}
window.on('-touch-bar-interaction', interactionListener)
};
window.on('-touch-bar-interaction', interactionListener);
const removeListeners = () => {
this.removeListener('change', changeListener)
this.removeListener('escape-item-change', escapeItemListener)
window.removeListener('-touch-bar-interaction', interactionListener)
window.removeListener('closed', removeListeners)
window._touchBar = null
delete this.windowListeners[id]
this.removeListener('change', changeListener);
this.removeListener('escape-item-change', escapeItemListener);
window.removeListener('-touch-bar-interaction', interactionListener);
window.removeListener('closed', removeListeners);
window._touchBar = null;
delete this.windowListeners[id];
const unregisterItems = (items) => {
for (const item of items) {
item.removeListener('change', this.changeListener)
item.removeListener('change', this.changeListener);
if (item.child instanceof TouchBar) {
unregisterItems(item.child.ordereredItems)
unregisterItems(item.child.ordereredItems);
}
}
}
unregisterItems(this.ordereredItems)
};
unregisterItems(this.ordereredItems);
if (this.escapeItem) {
this.escapeItem.removeListener('change', this.changeListener)
this.escapeItem.removeListener('change', this.changeListener);
}
}
window.once('closed', removeListeners)
this.windowListeners[id] = removeListeners
};
window.once('closed', removeListeners);
this.windowListeners[id] = removeListeners;
window._setTouchBarItems(this.ordereredItems)
escapeItemListener(this.escapeItem)
window._setTouchBarItems(this.ordereredItems);
escapeItemListener(this.escapeItem);
}
_removeFromWindow (window) {
const removeListeners = this.windowListeners[window.id]
if (removeListeners != null) removeListeners()
const removeListeners = this.windowListeners[window.id];
if (removeListeners != null) removeListeners();
}
}
class TouchBarItem extends EventEmitter {
constructor () {
super()
this._addImmutableProperty('id', `${nextItemID++}`)
this._parents = []
super();
this._addImmutableProperty('id', `${nextItemID++}`);
this._parents = [];
}
_addImmutableProperty (name, value) {
Object.defineProperty(this, name, {
get: function () {
return value
return value;
},
set: function () {
throw new Error(`Cannot override property ${name}`)
throw new Error(`Cannot override property ${name}`);
},
enumerable: true,
configurable: false
})
});
}
_addLiveProperty (name, initialValue) {
const privateName = `_${name}`
this[privateName] = initialValue
const privateName = `_${name}`;
this[privateName] = initialValue;
Object.defineProperty(this, name, {
get: function () {
return this[privateName]
return this[privateName];
},
set: function (value) {
this[privateName] = value
this.emit('change', this)
this[privateName] = value;
this.emit('change', this);
},
enumerable: true
})
});
}
_addParent (item) {
const existing = this._parents.some(test => test.id === item.id)
const existing = this._parents.some(test => test.id === item.id);
if (!existing) {
this._parents.push({
id: item.id,
type: item.type
})
});
}
}
}
TouchBar.TouchBarButton = class TouchBarButton extends TouchBarItem {
constructor (config) {
super()
if (config == null) config = {}
this._addImmutableProperty('type', 'button')
const { click, icon, iconPosition, label, backgroundColor } = config
this._addLiveProperty('label', label)
this._addLiveProperty('backgroundColor', backgroundColor)
this._addLiveProperty('icon', icon)
this._addLiveProperty('iconPosition', iconPosition)
super();
if (config == null) config = {};
this._addImmutableProperty('type', 'button');
const { click, icon, iconPosition, label, backgroundColor } = config;
this._addLiveProperty('label', label);
this._addLiveProperty('backgroundColor', backgroundColor);
this._addLiveProperty('icon', icon);
this._addLiveProperty('iconPosition', iconPosition);
if (typeof click === 'function') {
this._addImmutableProperty('onInteraction', () => {
config.click()
})
config.click();
});
}
}
}
};
TouchBar.TouchBarColorPicker = class TouchBarColorPicker extends TouchBarItem {
constructor (config) {
super()
if (config == null) config = {}
this._addImmutableProperty('type', 'colorpicker')
const { availableColors, change, selectedColor } = config
this._addLiveProperty('availableColors', availableColors)
this._addLiveProperty('selectedColor', selectedColor)
super();
if (config == null) config = {};
this._addImmutableProperty('type', 'colorpicker');
const { availableColors, change, selectedColor } = config;
this._addLiveProperty('availableColors', availableColors);
this._addLiveProperty('selectedColor', selectedColor);
if (typeof change === 'function') {
this._addImmutableProperty('onInteraction', (details) => {
this._selectedColor = details.color
change(details.color)
})
this._selectedColor = details.color;
change(details.color);
});
}
}
}
};
TouchBar.TouchBarGroup = class TouchBarGroup extends TouchBarItem {
constructor (config) {
super()
if (config == null) config = {}
this._addImmutableProperty('type', 'group')
const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items)
this._addLiveProperty('child', defaultChild)
this.child.ordereredItems.forEach((item) => item._addParent(this))
super();
if (config == null) config = {};
this._addImmutableProperty('type', 'group');
const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items);
this._addLiveProperty('child', defaultChild);
this.child.ordereredItems.forEach((item) => item._addParent(this));
}
}
};
TouchBar.TouchBarLabel = class TouchBarLabel extends TouchBarItem {
constructor (config) {
super()
if (config == null) config = {}
this._addImmutableProperty('type', 'label')
this._addLiveProperty('label', config.label)
this._addLiveProperty('textColor', config.textColor)
super();
if (config == null) config = {};
this._addImmutableProperty('type', 'label');
this._addLiveProperty('label', config.label);
this._addLiveProperty('textColor', config.textColor);
}
}
};
TouchBar.TouchBarPopover = class TouchBarPopover extends TouchBarItem {
constructor (config) {
super()
if (config == null) config = {}
this._addImmutableProperty('type', 'popover')
this._addLiveProperty('label', config.label)
this._addLiveProperty('icon', config.icon)
this._addLiveProperty('showCloseButton', config.showCloseButton)
const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items)
this._addLiveProperty('child', defaultChild)
this.child.ordereredItems.forEach((item) => item._addParent(this))
super();
if (config == null) config = {};
this._addImmutableProperty('type', 'popover');
this._addLiveProperty('label', config.label);
this._addLiveProperty('icon', config.icon);
this._addLiveProperty('showCloseButton', config.showCloseButton);
const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items);
this._addLiveProperty('child', defaultChild);
this.child.ordereredItems.forEach((item) => item._addParent(this));
}
}
};
TouchBar.TouchBarSlider = class TouchBarSlider extends TouchBarItem {
constructor (config) {
super()
if (config == null) config = {}
this._addImmutableProperty('type', 'slider')
const { change, label, minValue, maxValue, value } = config
this._addLiveProperty('label', label)
this._addLiveProperty('minValue', minValue)
this._addLiveProperty('maxValue', maxValue)
this._addLiveProperty('value', value)
super();
if (config == null) config = {};
this._addImmutableProperty('type', 'slider');
const { change, label, minValue, maxValue, value } = config;
this._addLiveProperty('label', label);
this._addLiveProperty('minValue', minValue);
this._addLiveProperty('maxValue', maxValue);
this._addLiveProperty('value', value);
if (typeof change === 'function') {
this._addImmutableProperty('onInteraction', (details) => {
this._value = details.value
change(details.value)
})
this._value = details.value;
change(details.value);
});
}
}
}
};
TouchBar.TouchBarSpacer = class TouchBarSpacer extends TouchBarItem {
constructor (config) {
super()
if (config == null) config = {}
this._addImmutableProperty('type', 'spacer')
this._addImmutableProperty('size', config.size)
super();
if (config == null) config = {};
this._addImmutableProperty('type', 'spacer');
this._addImmutableProperty('size', config.size);
}
}
};
TouchBar.TouchBarSegmentedControl = class TouchBarSegmentedControl extends TouchBarItem {
constructor (config) {
super()
if (config == null) config = {}
const { segmentStyle, segments, selectedIndex, change, mode } = config
this._addImmutableProperty('type', 'segmented_control')
this._addLiveProperty('segmentStyle', segmentStyle)
this._addLiveProperty('segments', segments || [])
this._addLiveProperty('selectedIndex', selectedIndex)
this._addLiveProperty('mode', mode)
super();
if (config == null) config = {};
const { segmentStyle, segments, selectedIndex, change, mode } = config;
this._addImmutableProperty('type', 'segmented_control');
this._addLiveProperty('segmentStyle', segmentStyle);
this._addLiveProperty('segments', segments || []);
this._addLiveProperty('selectedIndex', selectedIndex);
this._addLiveProperty('mode', mode);
if (typeof change === 'function') {
this._addImmutableProperty('onInteraction', (details) => {
this._selectedIndex = details.selectedIndex
change(details.selectedIndex, details.isSelected)
})
this._selectedIndex = details.selectedIndex;
change(details.selectedIndex, details.isSelected);
});
}
}
}
};
TouchBar.TouchBarScrubber = class TouchBarScrubber extends TouchBarItem {
constructor (config) {
super()
if (config == null) config = {}
const { items, selectedStyle, overlayStyle, showArrowButtons, continuous, mode } = config
let { select, highlight } = config
this._addImmutableProperty('type', 'scrubber')
this._addLiveProperty('items', items)
this._addLiveProperty('selectedStyle', selectedStyle || null)
this._addLiveProperty('overlayStyle', overlayStyle || null)
this._addLiveProperty('showArrowButtons', showArrowButtons || false)
this._addLiveProperty('mode', mode || 'free')
this._addLiveProperty('continuous', typeof continuous === 'undefined' ? true : continuous)
super();
if (config == null) config = {};
const { items, selectedStyle, overlayStyle, showArrowButtons, continuous, mode } = config;
let { select, highlight } = config;
this._addImmutableProperty('type', 'scrubber');
this._addLiveProperty('items', items);
this._addLiveProperty('selectedStyle', selectedStyle || null);
this._addLiveProperty('overlayStyle', overlayStyle || null);
this._addLiveProperty('showArrowButtons', showArrowButtons || false);
this._addLiveProperty('mode', mode || 'free');
this._addLiveProperty('continuous', typeof continuous === 'undefined' ? true : continuous);
if (typeof select === 'function' || typeof highlight === 'function') {
if (select == null) select = () => {}
if (highlight == null) highlight = () => {}
if (select == null) select = () => {};
if (highlight == null) highlight = () => {};
this._addImmutableProperty('onInteraction', (details) => {
if (details.type === 'select' && typeof select === 'function') {
select(details.selectedIndex)
select(details.selectedIndex);
} else if (details.type === 'highlight' && typeof highlight === 'function') {
highlight(details.highlightedIndex)
highlight(details.highlightedIndex);
}
})
});
}
}
}
};
module.exports = TouchBar
module.exports = TouchBar;

View File

@@ -1,9 +1,9 @@
'use strict'
'use strict';
const { EventEmitter } = require('events')
const { deprecate } = require('electron')
const { Tray } = process.electronBinding('tray')
const { EventEmitter } = require('events');
const { deprecate } = require('electron');
const { Tray } = process.electronBinding('tray');
Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype)
Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype);
module.exports = Tray
module.exports = Tray;

View File

@@ -1,11 +1,11 @@
'use strict'
'use strict';
const { EventEmitter } = require('events')
const { View } = process.electronBinding('view')
const { EventEmitter } = require('events');
const { View } = process.electronBinding('view');
Object.setPrototypeOf(View.prototype, EventEmitter.prototype)
Object.setPrototypeOf(View.prototype, EventEmitter.prototype);
View.prototype._init = function () {
}
};
module.exports = View
module.exports = View;

View File

@@ -1,15 +1,15 @@
'use strict'
'use strict';
const electron = require('electron')
const electron = require('electron');
const { LayoutManager } = electron
const { BoxLayout } = process.electronBinding('box_layout')
const { LayoutManager } = electron;
const { BoxLayout } = process.electronBinding('box_layout');
Object.setPrototypeOf(BoxLayout.prototype, LayoutManager.prototype)
Object.setPrototypeOf(BoxLayout.prototype, LayoutManager.prototype);
BoxLayout.prototype._init = function () {
// Call parent class's _init.
LayoutManager.prototype._init.call(this)
}
LayoutManager.prototype._init.call(this);
};
module.exports = BoxLayout
module.exports = BoxLayout;

View File

@@ -1,15 +1,15 @@
'use strict'
'use strict';
const electron = require('electron')
const electron = require('electron');
const { View } = electron
const { Button } = process.electronBinding('button')
const { View } = electron;
const { Button } = process.electronBinding('button');
Object.setPrototypeOf(Button.prototype, View.prototype)
Object.setPrototypeOf(Button.prototype, View.prototype);
Button.prototype._init = function () {
// Call parent class's _init.
View.prototype._init.call(this)
}
View.prototype._init.call(this);
};
module.exports = Button
module.exports = Button;

View File

@@ -1,15 +1,15 @@
'use strict'
'use strict';
const electron = require('electron')
const electron = require('electron');
const { Button } = electron
const { LabelButton } = process.electronBinding('label_button')
const { Button } = electron;
const { LabelButton } = process.electronBinding('label_button');
Object.setPrototypeOf(LabelButton.prototype, Button.prototype)
Object.setPrototypeOf(LabelButton.prototype, Button.prototype);
LabelButton.prototype._init = function () {
// Call parent class's _init.
Button.prototype._init.call(this)
}
Button.prototype._init.call(this);
};
module.exports = LabelButton
module.exports = LabelButton;

View File

@@ -1,8 +1,8 @@
'use strict'
'use strict';
const { LayoutManager } = process.electronBinding('layout_manager')
const { LayoutManager } = process.electronBinding('layout_manager');
LayoutManager.prototype._init = function () {
}
};
module.exports = LayoutManager
module.exports = LayoutManager;

View File

@@ -1,15 +1,15 @@
'use strict'
'use strict';
const electron = require('electron')
const electron = require('electron');
const { LabelButton } = electron
const { MdTextButton } = process.electronBinding('md_text_button')
const { LabelButton } = electron;
const { MdTextButton } = process.electronBinding('md_text_button');
Object.setPrototypeOf(MdTextButton.prototype, LabelButton.prototype)
Object.setPrototypeOf(MdTextButton.prototype, LabelButton.prototype);
MdTextButton.prototype._init = function () {
// Call parent class's _init.
LabelButton.prototype._init.call(this)
}
LabelButton.prototype._init.call(this);
};
module.exports = MdTextButton
module.exports = MdTextButton;

View File

@@ -1,15 +1,15 @@
'use strict'
'use strict';
const electron = require('electron')
const electron = require('electron');
const { View } = electron
const { ResizeArea } = process.electronBinding('resize_area')
const { View } = electron;
const { ResizeArea } = process.electronBinding('resize_area');
Object.setPrototypeOf(ResizeArea.prototype, View.prototype)
Object.setPrototypeOf(ResizeArea.prototype, View.prototype);
ResizeArea.prototype._init = function () {
// Call parent class's _init.
View.prototype._init.call(this)
}
View.prototype._init.call(this);
};
module.exports = ResizeArea
module.exports = ResizeArea;

View File

@@ -1,15 +1,15 @@
'use strict'
'use strict';
const electron = require('electron')
const electron = require('electron');
const { View } = electron
const { TextField } = process.electronBinding('text_field')
const { View } = electron;
const { TextField } = process.electronBinding('text_field');
Object.setPrototypeOf(TextField.prototype, View.prototype)
Object.setPrototypeOf(TextField.prototype, View.prototype);
TextField.prototype._init = function () {
// Call parent class's _init.
View.prototype._init.call(this)
}
View.prototype._init.call(this);
};
module.exports = TextField
module.exports = TextField;

View File

@@ -1,15 +1,15 @@
'use strict'
'use strict';
const electron = require('electron')
const electron = require('electron');
const { View } = electron
const { WebContentsView } = process.electronBinding('web_contents_view')
const { View } = electron;
const { WebContentsView } = process.electronBinding('web_contents_view');
Object.setPrototypeOf(WebContentsView.prototype, View.prototype)
Object.setPrototypeOf(WebContentsView.prototype, View.prototype);
WebContentsView.prototype._init = function () {
// Call parent class's _init.
View.prototype._init.call(this)
}
View.prototype._init.call(this);
};
module.exports = WebContentsView
module.exports = WebContentsView;

View File

@@ -1,27 +1,27 @@
'use strict'
'use strict';
const features = process.electronBinding('features')
const { EventEmitter } = require('events')
const electron = require('electron')
const path = require('path')
const url = require('url')
const { app, ipcMain, session, deprecate } = electron
const features = process.electronBinding('features');
const { EventEmitter } = require('events');
const electron = require('electron');
const path = require('path');
const url = require('url');
const { app, ipcMain, session, deprecate } = electron;
const { internalWindowOpen } = require('@electron/internal/browser/guest-window-manager')
const NavigationController = require('@electron/internal/browser/navigation-controller')
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal')
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
const errorUtils = require('@electron/internal/common/error-utils')
const { internalWindowOpen } = require('@electron/internal/browser/guest-window-manager');
const NavigationController = require('@electron/internal/browser/navigation-controller');
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
const errorUtils = require('@electron/internal/common/error-utils');
// session is not used here, the purpose is to make sure session is initalized
// before the webContents module.
// eslint-disable-next-line
session
let nextId = 0
let nextId = 0;
const getNextId = function () {
return ++nextId
}
return ++nextId;
};
// Stock page sizes
const PDFPageSizes = {
@@ -62,7 +62,7 @@ const PDFPageSizes = {
width_microns: 279400,
custom_display_name: 'Tabloid'
}
}
};
// Default printing setting
const defaultPrintingSetting = {
@@ -92,83 +92,83 @@ const defaultPrintingSetting = {
collate: true,
shouldPrintBackgrounds: false,
shouldPrintSelectionOnly: false
}
};
// JavaScript implementations of WebContents.
const binding = process.electronBinding('web_contents')
const { WebContents } = binding
const binding = process.electronBinding('web_contents');
const { WebContents } = binding;
Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype)
Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype)
Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype);
Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype);
// WebContents::send(channel, args..)
// WebContents::sendToAll(channel, args..)
WebContents.prototype.send = function (channel, ...args) {
if (typeof channel !== 'string') {
throw new Error('Missing required channel argument')
throw new Error('Missing required channel argument');
}
const internal = false
const sendToAll = false
const internal = false;
const sendToAll = false;
return this._send(internal, sendToAll, channel, args)
}
return this._send(internal, sendToAll, channel, args);
};
WebContents.prototype.sendToAll = function (channel, ...args) {
if (typeof channel !== 'string') {
throw new Error('Missing required channel argument')
throw new Error('Missing required channel argument');
}
const internal = false
const sendToAll = true
const internal = false;
const sendToAll = true;
return this._send(internal, sendToAll, channel, args)
}
return this._send(internal, sendToAll, channel, args);
};
WebContents.prototype._sendInternal = function (channel, ...args) {
if (typeof channel !== 'string') {
throw new Error('Missing required channel argument')
throw new Error('Missing required channel argument');
}
const internal = true
const sendToAll = false
const internal = true;
const sendToAll = false;
return this._send(internal, sendToAll, channel, args)
}
return this._send(internal, sendToAll, channel, args);
};
WebContents.prototype._sendInternalToAll = function (channel, ...args) {
if (typeof channel !== 'string') {
throw new Error('Missing required channel argument')
throw new Error('Missing required channel argument');
}
const internal = true
const sendToAll = true
const internal = true;
const sendToAll = true;
return this._send(internal, sendToAll, channel, args)
}
return this._send(internal, sendToAll, channel, args);
};
WebContents.prototype.sendToFrame = function (frameId, channel, ...args) {
if (typeof channel !== 'string') {
throw new Error('Missing required channel argument')
throw new Error('Missing required channel argument');
} else if (typeof frameId !== 'number') {
throw new Error('Missing required frameId argument')
throw new Error('Missing required frameId argument');
}
const internal = false
const sendToAll = false
const internal = false;
const sendToAll = false;
return this._sendToFrame(internal, sendToAll, frameId, channel, args)
}
return this._sendToFrame(internal, sendToAll, frameId, channel, args);
};
WebContents.prototype._sendToFrameInternal = function (frameId, channel, ...args) {
if (typeof channel !== 'string') {
throw new Error('Missing required channel argument')
throw new Error('Missing required channel argument');
} else if (typeof frameId !== 'number') {
throw new Error('Missing required frameId argument')
throw new Error('Missing required frameId argument');
}
const internal = true
const sendToAll = false
const internal = true;
const sendToAll = false;
return this._sendToFrame(internal, sendToAll, frameId, channel, args)
}
return this._sendToFrame(internal, sendToAll, frameId, channel, args);
};
// Following methods are mapped to webFrame.
const webFrameMethods = [
@@ -177,56 +177,56 @@ const webFrameMethods = [
'removeInsertedCSS',
'setLayoutZoomLevelLimits',
'setVisualZoomLevelLimits'
]
];
for (const method of webFrameMethods) {
WebContents.prototype[method] = function (...args) {
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, ...args)
}
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, ...args);
};
}
const executeJavaScript = (contents, code, hasUserGesture) => {
return ipcMainUtils.invokeInWebContents(contents, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScript', code, hasUserGesture)
}
return ipcMainUtils.invokeInWebContents(contents, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScript', code, hasUserGesture);
};
// Make sure WebContents::executeJavaScript would run the code only when the
// WebContents has been loaded.
WebContents.prototype.executeJavaScript = function (code, hasUserGesture) {
if (this.getURL() && !this.isLoadingMainFrame()) {
return executeJavaScript(this, code, hasUserGesture)
return executeJavaScript(this, code, hasUserGesture);
} else {
return new Promise((resolve, reject) => {
this.once('did-stop-loading', () => {
executeJavaScript(this, code, hasUserGesture).then(resolve, reject)
})
})
executeJavaScript(this, code, hasUserGesture).then(resolve, reject);
});
});
}
}
};
// Translate the options of printToPDF.
WebContents.prototype.printToPDF = function (options) {
const printingSetting = {
...defaultPrintingSetting,
requestID: getNextId()
}
};
if (options.landscape) {
printingSetting.landscape = options.landscape
printingSetting.landscape = options.landscape;
}
if (options.marginsType) {
printingSetting.marginsType = options.marginsType
printingSetting.marginsType = options.marginsType;
}
if (options.printSelectionOnly) {
printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly
printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly;
}
if (options.printBackground) {
printingSetting.shouldPrintBackgrounds = options.printBackground
printingSetting.shouldPrintBackgrounds = options.printBackground;
}
if (options.pageSize) {
const pageSize = options.pageSize
const pageSize = options.pageSize;
if (typeof pageSize === 'object') {
if (!pageSize.height || !pageSize.width) {
return Promise.reject(new Error('Must define height and width for pageSize'))
return Promise.reject(new Error('Must define height and width for pageSize'));
}
// Dimensions in Microns
// 1 meter = 10^6 microns
@@ -235,47 +235,47 @@ WebContents.prototype.printToPDF = function (options) {
custom_display_name: 'Custom',
height_microns: Math.ceil(pageSize.height),
width_microns: Math.ceil(pageSize.width)
}
};
} else if (PDFPageSizes[pageSize]) {
printingSetting.mediaSize = PDFPageSizes[pageSize]
printingSetting.mediaSize = PDFPageSizes[pageSize];
} else {
return Promise.reject(new Error(`Does not support pageSize with ${pageSize}`))
return Promise.reject(new Error(`Does not support pageSize with ${pageSize}`));
}
} else {
printingSetting.mediaSize = PDFPageSizes['A4']
printingSetting.mediaSize = PDFPageSizes['A4'];
}
// Chromium expects this in a 0-100 range number, not as float
printingSetting.scaleFactor *= 100
printingSetting.scaleFactor *= 100;
if (features.isPrintingEnabled()) {
return this._printToPDF(printingSetting)
return this._printToPDF(printingSetting);
} else {
return Promise.reject(new Error('Printing feature is disabled'))
return Promise.reject(new Error('Printing feature is disabled'));
}
}
};
WebContents.prototype.print = function (...args) {
if (features.isPrintingEnabled()) {
this._print(...args)
this._print(...args);
} else {
console.error('Error: Printing feature is disabled.')
console.error('Error: Printing feature is disabled.');
}
}
};
WebContents.prototype.getPrinters = function () {
if (features.isPrintingEnabled()) {
return this._getPrinters()
return this._getPrinters();
} else {
console.error('Error: Printing feature is disabled.')
return []
console.error('Error: Printing feature is disabled.');
return [];
}
}
};
WebContents.prototype.loadFile = function (filePath, options = {}) {
if (typeof filePath !== 'string') {
throw new Error('Must pass filePath as a string')
throw new Error('Must pass filePath as a string');
}
const { query, search, hash } = options
const { query, search, hash } = options;
return this.loadURL(url.format({
protocol: 'file',
@@ -284,98 +284,98 @@ WebContents.prototype.loadFile = function (filePath, options = {}) {
query,
search,
hash
}))
}
}));
};
const addReplyToEvent = (event) => {
event.reply = (...args) => {
event.sender.sendToFrame(event.frameId, ...args)
}
}
event.sender.sendToFrame(event.frameId, ...args);
};
};
const addReplyInternalToEvent = (event) => {
Object.defineProperty(event, '_replyInternal', {
configurable: false,
enumerable: false,
value: (...args) => {
event.sender._sendToFrameInternal(event.frameId, ...args)
event.sender._sendToFrameInternal(event.frameId, ...args);
}
})
}
});
};
const addReturnValueToEvent = (event) => {
Object.defineProperty(event, 'returnValue', {
set: (value) => event.sendReply([value]),
get: () => {}
})
}
});
};
// Add JavaScript wrappers for WebContents class.
WebContents.prototype._init = function () {
// The navigation controller.
NavigationController.call(this, this)
NavigationController.call(this, this);
// Every remote callback from renderer process would add a listener to the
// render-view-deleted event, so ignore the listeners warning.
this.setMaxListeners(0)
this.setMaxListeners(0);
// Dispatch IPC messages to the ipc module.
this.on('-ipc-message', function (event, internal, channel, args) {
if (internal) {
addReplyInternalToEvent(event)
ipcMainInternal.emit(channel, event, ...args)
addReplyInternalToEvent(event);
ipcMainInternal.emit(channel, event, ...args);
} else {
addReplyToEvent(event)
this.emit('ipc-message', event, channel, ...args)
ipcMain.emit(channel, event, ...args)
addReplyToEvent(event);
this.emit('ipc-message', event, channel, ...args);
ipcMain.emit(channel, event, ...args);
}
})
});
this.on('-ipc-invoke', function (event, channel, args) {
event._reply = (result) => event.sendReply({ result })
event._reply = (result) => event.sendReply({ result });
event._throw = (error) => {
console.error(`Error occurred in handler for '${channel}':`, error)
event.sendReply({ error: error.toString() })
}
console.error(`Error occurred in handler for '${channel}':`, error);
event.sendReply({ error: error.toString() });
};
if (ipcMain._invokeHandlers.has(channel)) {
ipcMain._invokeHandlers.get(channel)(event, ...args)
ipcMain._invokeHandlers.get(channel)(event, ...args);
} else {
event._throw(`No handler registered for '${channel}'`)
event._throw(`No handler registered for '${channel}'`);
}
})
});
this.on('-ipc-message-sync', function (event, internal, channel, args) {
addReturnValueToEvent(event)
addReturnValueToEvent(event);
if (internal) {
addReplyInternalToEvent(event)
ipcMainInternal.emit(channel, event, ...args)
addReplyInternalToEvent(event);
ipcMainInternal.emit(channel, event, ...args);
} else {
addReplyToEvent(event)
this.emit('ipc-message-sync', event, channel, ...args)
ipcMain.emit(channel, event, ...args)
addReplyToEvent(event);
this.emit('ipc-message-sync', event, channel, ...args);
ipcMain.emit(channel, event, ...args);
}
})
});
// Handle context menu action request from pepper plugin.
this.on('pepper-context-menu', function (event, params, callback) {
// Access Menu via electron.Menu to prevent circular require.
const menu = electron.Menu.buildFromTemplate(params.menu)
const menu = electron.Menu.buildFromTemplate(params.menu);
menu.popup({
window: event.sender.getOwnerBrowserWindow(),
x: params.x,
y: params.y,
callback
})
})
});
});
this.on('crashed', (event, ...args) => {
app.emit('renderer-process-crashed', event, this, ...args)
})
app.emit('renderer-process-crashed', event, this, ...args);
});
// The devtools requests the webContents to reload.
this.on('devtools-reload-page', function () {
this.reload()
})
this.reload();
});
// Handle window.open for BrowserWindow and BrowserView.
if (['browserView', 'window'].includes(this.getType())) {
@@ -387,9 +387,9 @@ WebContents.prototype._init = function () {
show: true,
width: 800,
height: 600
}
internalWindowOpen(event, url, referrer, frameName, disposition, options, additionalFeatures, postData)
})
};
internalWindowOpen(event, url, referrer, frameName, disposition, options, additionalFeatures, postData);
});
// Create a new browser window for the native implementation of
// "window.open", used in sandbox and nativeWindowOpen mode.
@@ -397,8 +397,8 @@ WebContents.prototype._init = function () {
userGesture, left, top, width, height, url, frameName) => {
if ((disposition !== 'foreground-tab' && disposition !== 'new-window' &&
disposition !== 'background-tab')) {
event.preventDefault()
return
event.preventDefault();
return;
}
const options = {
@@ -408,54 +408,54 @@ WebContents.prototype._init = function () {
width: width || 800,
height: height || 600,
webContents
}
const referrer = { url: '', policy: 'default' }
internalWindowOpen(event, url, referrer, frameName, disposition, options)
})
};
const referrer = { url: '', policy: 'default' };
internalWindowOpen(event, url, referrer, frameName, disposition, options);
});
}
this.on('login', (event, ...args) => {
app.emit('login', event, this, ...args)
})
app.emit('login', event, this, ...args);
});
const event = process.electronBinding('event').createEmpty()
app.emit('web-contents-created', event, this)
}
const event = process.electronBinding('event').createEmpty();
app.emit('web-contents-created', event, this);
};
// Deprecations
deprecate.fnToProperty(WebContents.prototype, 'audioMuted', '_isAudioMuted', '_setAudioMuted')
deprecate.fnToProperty(WebContents.prototype, 'userAgent', '_getUserAgent', '_setUserAgent')
deprecate.fnToProperty(WebContents.prototype, 'zoomLevel', '_getZoomLevel', '_setZoomLevel')
deprecate.fnToProperty(WebContents.prototype, 'zoomFactor', '_getZoomFactor', '_setZoomFactor')
deprecate.fnToProperty(WebContents.prototype, 'frameRate', '_getFrameRate', '_setFrameRate')
deprecate.fnToProperty(WebContents.prototype, 'audioMuted', '_isAudioMuted', '_setAudioMuted');
deprecate.fnToProperty(WebContents.prototype, 'userAgent', '_getUserAgent', '_setUserAgent');
deprecate.fnToProperty(WebContents.prototype, 'zoomLevel', '_getZoomLevel', '_setZoomLevel');
deprecate.fnToProperty(WebContents.prototype, 'zoomFactor', '_getZoomFactor', '_setZoomFactor');
deprecate.fnToProperty(WebContents.prototype, 'frameRate', '_getFrameRate', '_setFrameRate');
// JavaScript wrapper of Debugger.
const { Debugger } = process.electronBinding('debugger')
Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype)
const { Debugger } = process.electronBinding('debugger');
Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype);
// Public APIs.
module.exports = {
create (options = {}) {
return binding.create(options)
return binding.create(options);
},
fromId (id) {
return binding.fromId(id)
return binding.fromId(id);
},
getFocusedWebContents () {
let focused = null
let focused = null;
for (const contents of binding.getAllWebContents()) {
if (!contents.isFocused()) continue
if (focused == null) focused = contents
if (!contents.isFocused()) continue;
if (focused == null) focused = contents;
// Return webview web contents which may be embedded inside another
// web contents that is also reporting as focused
if (contents.getType() === 'webview') return contents
if (contents.getType() === 'webview') return contents;
}
return focused
return focused;
},
getAllWebContents () {
return binding.getAllWebContents()
return binding.getAllWebContents();
}
}
};

View File

@@ -1,57 +1,57 @@
'use strict'
'use strict';
const { app, webContents, BrowserWindow } = require('electron')
const { getAllWebContents } = process.electronBinding('web_contents')
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
const { app, webContents, BrowserWindow } = require('electron');
const { getAllWebContents } = process.electronBinding('web_contents');
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
const { Buffer } = require('buffer')
const fs = require('fs')
const path = require('path')
const url = require('url')
const util = require('util')
const { Buffer } = require('buffer');
const fs = require('fs');
const path = require('path');
const url = require('url');
const util = require('util');
// Mapping between extensionId(hostname) and manifest.
const manifestMap = {} // extensionId => manifest
const manifestNameMap = {} // name => manifest
const devToolsExtensionNames = new Set()
const manifestMap = {}; // extensionId => manifest
const manifestNameMap = {}; // name => manifest
const devToolsExtensionNames = new Set();
const generateExtensionIdFromName = function (name) {
return name.replace(/[\W_]+/g, '-').toLowerCase()
}
return name.replace(/[\W_]+/g, '-').toLowerCase();
};
const isWindowOrWebView = function (webContents) {
const type = webContents.getType()
return type === 'window' || type === 'webview'
}
const type = webContents.getType();
return type === 'window' || type === 'webview';
};
const isBackgroundPage = function (webContents) {
return webContents.getType() === 'backgroundPage'
}
return webContents.getType() === 'backgroundPage';
};
// Create or get manifest object from |srcDirectory|.
const getManifestFromPath = function (srcDirectory) {
let manifest
let manifestContent
let manifest;
let manifestContent;
try {
manifestContent = fs.readFileSync(path.join(srcDirectory, 'manifest.json'))
manifestContent = fs.readFileSync(path.join(srcDirectory, 'manifest.json'));
} catch (readError) {
console.warn(`Reading ${path.join(srcDirectory, 'manifest.json')} failed.`)
console.warn(readError.stack || readError)
throw readError
console.warn(`Reading ${path.join(srcDirectory, 'manifest.json')} failed.`);
console.warn(readError.stack || readError);
throw readError;
}
try {
manifest = JSON.parse(manifestContent)
manifest = JSON.parse(manifestContent);
} catch (parseError) {
console.warn(`Parsing ${path.join(srcDirectory, 'manifest.json')} failed.`)
console.warn(parseError.stack || parseError)
throw parseError
console.warn(`Parsing ${path.join(srcDirectory, 'manifest.json')} failed.`);
console.warn(parseError.stack || parseError);
throw parseError;
}
if (!manifestNameMap[manifest.name]) {
const extensionId = generateExtensionIdFromName(manifest.name)
manifestMap[extensionId] = manifestNameMap[manifest.name] = manifest
const extensionId = generateExtensionIdFromName(manifest.name);
manifestMap[extensionId] = manifestNameMap[manifest.name] = manifest;
Object.assign(manifest, {
srcDirectory: srcDirectory,
extensionId: extensionId,
@@ -63,31 +63,31 @@ const getManifestFromPath = function (srcDirectory) {
hostname: extensionId,
pathname: manifest.devtools_page
})
})
return manifest
});
return manifest;
} else if (manifest && manifest.name) {
console.warn(`Attempted to load extension "${manifest.name}" that has already been loaded.`)
return manifest
console.warn(`Attempted to load extension "${manifest.name}" that has already been loaded.`);
return manifest;
}
}
};
// Manage the background pages.
const backgroundPages = {}
const backgroundPages = {};
const startBackgroundPages = function (manifest) {
if (backgroundPages[manifest.extensionId] || !manifest.background) return
if (backgroundPages[manifest.extensionId] || !manifest.background) return;
let html
let name
let html;
let name;
if (manifest.background.page) {
name = manifest.background.page
html = fs.readFileSync(path.join(manifest.srcDirectory, manifest.background.page))
name = manifest.background.page;
html = fs.readFileSync(path.join(manifest.srcDirectory, manifest.background.page));
} else {
name = '_generated_background_page.html'
name = '_generated_background_page.html';
const scripts = manifest.background.scripts.map((name) => {
return `<script src="${name}"></script>`
}).join('')
html = Buffer.from(`<html><body>${scripts}</body></html>`)
return `<script src="${name}"></script>`;
}).join('');
html = Buffer.from(`<html><body>${scripts}</body></html>`);
}
const contents = webContents.create({
@@ -95,36 +95,36 @@ const startBackgroundPages = function (manifest) {
type: 'backgroundPage',
sandbox: true,
enableRemoteModule: false
})
backgroundPages[manifest.extensionId] = { html: html, webContents: contents, name: name }
});
backgroundPages[manifest.extensionId] = { html: html, webContents: contents, name: name };
contents.loadURL(url.format({
protocol: 'chrome-extension',
slashes: true,
hostname: manifest.extensionId,
pathname: name
}))
}
}));
};
const removeBackgroundPages = function (manifest) {
if (!backgroundPages[manifest.extensionId]) return
if (!backgroundPages[manifest.extensionId]) return;
backgroundPages[manifest.extensionId].webContents.destroy()
delete backgroundPages[manifest.extensionId]
}
backgroundPages[manifest.extensionId].webContents.destroy();
delete backgroundPages[manifest.extensionId];
};
const sendToBackgroundPages = function (...args) {
for (const page of Object.values(backgroundPages)) {
if (!page.webContents.isDestroyed()) {
page.webContents._sendInternalToAll(...args)
page.webContents._sendInternalToAll(...args);
}
}
}
};
// Dispatch web contents events to Chrome APIs
const hookWebContentsEvents = function (webContents) {
const tabId = webContents.id
const tabId = webContents.id;
sendToBackgroundPages('CHROME_TABS_ONCREATED')
sendToBackgroundPages('CHROME_TABS_ONCREATED');
webContents.on('will-navigate', (event, url) => {
sendToBackgroundPages('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', {
@@ -134,8 +134,8 @@ const hookWebContentsEvents = function (webContents) {
tabId: tabId,
timeStamp: Date.now(),
url: url
})
})
});
});
webContents.on('did-navigate', (event, url) => {
sendToBackgroundPages('CHROME_WEBNAVIGATION_ONCOMPLETED', {
@@ -145,189 +145,189 @@ const hookWebContentsEvents = function (webContents) {
tabId: tabId,
timeStamp: Date.now(),
url: url
})
})
});
});
webContents.once('destroyed', () => {
sendToBackgroundPages('CHROME_TABS_ONREMOVED', tabId)
})
}
sendToBackgroundPages('CHROME_TABS_ONREMOVED', tabId);
});
};
// Handle the chrome.* API messages.
let nextId = 0
let nextId = 0;
ipcMainUtils.handle('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo) {
if (isBackgroundPage(event.sender)) {
throw new Error('chrome.runtime.connect is not supported in background page')
throw new Error('chrome.runtime.connect is not supported in background page');
}
const page = backgroundPages[extensionId]
const page = backgroundPages[extensionId];
if (!page || page.webContents.isDestroyed()) {
throw new Error(`Connect to unknown extension ${extensionId}`)
throw new Error(`Connect to unknown extension ${extensionId}`);
}
const tabId = page.webContents.id
const portId = ++nextId
const tabId = page.webContents.id;
const portId = ++nextId;
event.sender.once('render-view-deleted', () => {
if (page.webContents.isDestroyed()) return
page.webContents._sendInternalToAll(`CHROME_PORT_DISCONNECT_${portId}`)
})
page.webContents._sendInternalToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo)
if (page.webContents.isDestroyed()) return;
page.webContents._sendInternalToAll(`CHROME_PORT_DISCONNECT_${portId}`);
});
page.webContents._sendInternalToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo);
return { tabId, portId }
})
return { tabId, portId };
});
ipcMainUtils.handle('CHROME_EXTENSION_MANIFEST', function (event, extensionId) {
const manifest = manifestMap[extensionId]
const manifest = manifestMap[extensionId];
if (!manifest) {
throw new Error(`Invalid extensionId: ${extensionId}`)
throw new Error(`Invalid extensionId: ${extensionId}`);
}
return manifest
})
return manifest;
});
ipcMainUtils.handle('CHROME_RUNTIME_SEND_MESSAGE', async function (event, extensionId, message) {
if (isBackgroundPage(event.sender)) {
throw new Error('chrome.runtime.sendMessage is not supported in background page')
throw new Error('chrome.runtime.sendMessage is not supported in background page');
}
const page = backgroundPages[extensionId]
const page = backgroundPages[extensionId];
if (!page || page.webContents.isDestroyed()) {
throw new Error(`Connect to unknown extension ${extensionId}`)
throw new Error(`Connect to unknown extension ${extensionId}`);
}
return ipcMainUtils.invokeInWebContents(page.webContents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message)
})
return ipcMainUtils.invokeInWebContents(page.webContents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message);
});
ipcMainUtils.handle('CHROME_TABS_SEND_MESSAGE', async function (event, tabId, extensionId, message) {
const contents = webContents.fromId(tabId)
const contents = webContents.fromId(tabId);
if (!contents) {
throw new Error(`Sending message to unknown tab ${tabId}`)
throw new Error(`Sending message to unknown tab ${tabId}`);
}
const senderTabId = isBackgroundPage(event.sender) ? null : event.sender.id
const senderTabId = isBackgroundPage(event.sender) ? null : event.sender.id;
return ipcMainUtils.invokeInWebContents(contents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, senderTabId, message)
})
return ipcMainUtils.invokeInWebContents(contents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, senderTabId, message);
});
const getLanguage = () => {
return app.getLocale().replace(/-.*$/, '').toLowerCase()
}
return app.getLocale().replace(/-.*$/, '').toLowerCase();
};
const getMessagesPath = (extensionId) => {
const metadata = manifestMap[extensionId]
const metadata = manifestMap[extensionId];
if (!metadata) {
throw new Error(`Invalid extensionId: ${extensionId}`)
throw new Error(`Invalid extensionId: ${extensionId}`);
}
const localesDirectory = path.join(metadata.srcDirectory, '_locales')
const language = getLanguage()
const localesDirectory = path.join(metadata.srcDirectory, '_locales');
const language = getLanguage();
try {
const filename = path.join(localesDirectory, language, 'messages.json')
fs.accessSync(filename, fs.constants.R_OK)
return filename
const filename = path.join(localesDirectory, language, 'messages.json');
fs.accessSync(filename, fs.constants.R_OK);
return filename;
} catch {
const defaultLocale = metadata.default_locale || 'en'
return path.join(localesDirectory, defaultLocale, 'messages.json')
const defaultLocale = metadata.default_locale || 'en';
return path.join(localesDirectory, defaultLocale, 'messages.json');
}
}
};
ipcMainUtils.handle('CHROME_GET_MESSAGES', async function (event, extensionId) {
const messagesPath = getMessagesPath(extensionId)
return fs.promises.readFile(messagesPath)
})
const messagesPath = getMessagesPath(extensionId);
return fs.promises.readFile(messagesPath);
});
const validStorageTypes = new Set(['sync', 'local'])
const validStorageTypes = new Set(['sync', 'local']);
const getChromeStoragePath = (storageType, extensionId) => {
if (!validStorageTypes.has(storageType)) {
throw new Error(`Invalid storageType: ${storageType}`)
throw new Error(`Invalid storageType: ${storageType}`);
}
if (!manifestMap[extensionId]) {
throw new Error(`Invalid extensionId: ${extensionId}`)
throw new Error(`Invalid extensionId: ${extensionId}`);
}
return path.join(app.getPath('userData'), `/Chrome Storage/${extensionId}-${storageType}.json`)
}
return path.join(app.getPath('userData'), `/Chrome Storage/${extensionId}-${storageType}.json`);
};
ipcMainUtils.handle('CHROME_STORAGE_READ', async function (event, storageType, extensionId) {
const filePath = getChromeStoragePath(storageType, extensionId)
const filePath = getChromeStoragePath(storageType, extensionId);
try {
return await fs.promises.readFile(filePath, 'utf8')
return await fs.promises.readFile(filePath, 'utf8');
} catch (error) {
if (error.code === 'ENOENT') {
return null
return null;
} else {
throw error
throw error;
}
}
})
});
ipcMainUtils.handle('CHROME_STORAGE_WRITE', async function (event, storageType, extensionId, data) {
const filePath = getChromeStoragePath(storageType, extensionId)
const filePath = getChromeStoragePath(storageType, extensionId);
try {
await fs.promises.mkdir(path.dirname(filePath), { recursive: true })
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
} catch {
// we just ignore the errors of mkdir
}
return fs.promises.writeFile(filePath, data, 'utf8')
})
return fs.promises.writeFile(filePath, data, 'utf8');
});
const isChromeExtension = function (pageURL) {
const { protocol } = url.parse(pageURL)
return protocol === 'chrome-extension:'
}
const { protocol } = url.parse(pageURL);
return protocol === 'chrome-extension:';
};
const assertChromeExtension = function (contents, api) {
const pageURL = contents._getURL()
const pageURL = contents._getURL();
if (!isChromeExtension(pageURL)) {
console.error(`Blocked ${pageURL} from calling ${api}`)
throw new Error(`Blocked ${api}`)
console.error(`Blocked ${pageURL} from calling ${api}`);
throw new Error(`Blocked ${api}`);
}
}
};
ipcMainUtils.handle('CHROME_TABS_EXECUTE_SCRIPT', async function (event, tabId, extensionId, details) {
assertChromeExtension(event.sender, 'chrome.tabs.executeScript()')
assertChromeExtension(event.sender, 'chrome.tabs.executeScript()');
const contents = webContents.fromId(tabId)
const contents = webContents.fromId(tabId);
if (!contents) {
throw new Error(`Sending message to unknown tab ${tabId}`)
throw new Error(`Sending message to unknown tab ${tabId}`);
}
let code, url
let code, url;
if (details.file) {
const manifest = manifestMap[extensionId]
code = String(fs.readFileSync(path.join(manifest.srcDirectory, details.file)))
url = `chrome-extension://${extensionId}${details.file}`
const manifest = manifestMap[extensionId];
code = String(fs.readFileSync(path.join(manifest.srcDirectory, details.file)));
url = `chrome-extension://${extensionId}${details.file}`;
} else {
code = details.code
url = `chrome-extension://${extensionId}/${String(Math.random()).substr(2, 8)}.js`
code = details.code;
url = `chrome-extension://${extensionId}/${String(Math.random()).substr(2, 8)}.js`;
}
return ipcMainUtils.invokeInWebContents(contents, false, 'CHROME_TABS_EXECUTE_SCRIPT', extensionId, url, code)
})
return ipcMainUtils.invokeInWebContents(contents, false, 'CHROME_TABS_EXECUTE_SCRIPT', extensionId, url, code);
});
exports.getContentScripts = () => {
return Object.values(contentScripts)
}
return Object.values(contentScripts);
};
// Transfer the content scripts to renderer.
const contentScripts = {}
const contentScripts = {};
const injectContentScripts = function (manifest) {
if (contentScripts[manifest.name] || !manifest.content_scripts) return
if (contentScripts[manifest.name] || !manifest.content_scripts) return;
const readArrayOfFiles = function (relativePath) {
return {
url: `chrome-extension://${manifest.extensionId}/${relativePath}`,
code: String(fs.readFileSync(path.join(manifest.srcDirectory, relativePath)))
}
}
};
};
const contentScriptToEntry = function (script) {
return {
@@ -336,25 +336,25 @@ const injectContentScripts = function (manifest) {
css: script.css ? script.css.map(readArrayOfFiles) : [],
runAt: script.run_at || 'document_idle',
allFrames: script.all_frames || false
}
}
};
};
try {
const entry = {
extensionId: manifest.extensionId,
contentScripts: manifest.content_scripts.map(contentScriptToEntry)
}
contentScripts[manifest.name] = entry
};
contentScripts[manifest.name] = entry;
} catch (e) {
console.error('Failed to read content scripts', e)
console.error('Failed to read content scripts', e);
}
}
};
const removeContentScripts = function (manifest) {
if (!contentScripts[manifest.name]) return
if (!contentScripts[manifest.name]) return;
delete contentScripts[manifest.name]
}
delete contentScripts[manifest.name];
};
// Transfer the |manifest| to a format that can be recognized by the
// |DevToolsAPI.addExtensions|.
@@ -364,166 +364,166 @@ const manifestToExtensionInfo = function (manifest) {
srcDirectory: manifest.srcDirectory,
name: manifest.name,
exposeExperimentalAPIs: true
}
}
};
};
// Load the extensions for the window.
const loadExtension = function (manifest) {
startBackgroundPages(manifest)
injectContentScripts(manifest)
}
startBackgroundPages(manifest);
injectContentScripts(manifest);
};
const loadDevToolsExtensions = function (win, manifests) {
if (!win.devToolsWebContents) return
if (!win.devToolsWebContents) return;
manifests.forEach(loadExtension)
manifests.forEach(loadExtension);
const extensionInfoArray = manifests.map(manifestToExtensionInfo)
const extensionInfoArray = manifests.map(manifestToExtensionInfo);
extensionInfoArray.forEach((extension) => {
win.devToolsWebContents._grantOriginAccess(extension.startPage)
})
win.devToolsWebContents._grantOriginAccess(extension.startPage);
});
extensionInfoArray.forEach((extensionInfo) => {
win.devToolsWebContents.executeJavaScript(`Extensions.extensionServer._addExtension(${JSON.stringify(extensionInfo)})`)
})
}
win.devToolsWebContents.executeJavaScript(`Extensions.extensionServer._addExtension(${JSON.stringify(extensionInfo)})`);
});
};
app.on('web-contents-created', function (event, webContents) {
if (!isWindowOrWebView(webContents)) return
if (!isWindowOrWebView(webContents)) return;
hookWebContentsEvents(webContents)
hookWebContentsEvents(webContents);
webContents.on('devtools-opened', function () {
loadDevToolsExtensions(webContents, Object.values(manifestMap))
})
})
loadDevToolsExtensions(webContents, Object.values(manifestMap));
});
});
// The chrome-extension: can map a extension URL request to real file path.
const chromeExtensionHandler = function (request, callback) {
const parsed = url.parse(request.url)
if (!parsed.hostname || !parsed.path) return callback()
const parsed = url.parse(request.url);
if (!parsed.hostname || !parsed.path) return callback();
const manifest = manifestMap[parsed.hostname]
if (!manifest) return callback()
const manifest = manifestMap[parsed.hostname];
if (!manifest) return callback();
const page = backgroundPages[parsed.hostname]
const page = backgroundPages[parsed.hostname];
if (page && parsed.path === `/${page.name}`) {
// Disabled due to false positive in StandardJS
// eslint-disable-next-line standard/no-callback-literal
return callback({
mimeType: 'text/html',
data: page.html
})
});
}
fs.readFile(path.join(manifest.srcDirectory, parsed.path), function (err, content) {
if (err) {
// Disabled due to false positive in StandardJS
// eslint-disable-next-line standard/no-callback-literal
return callback(-6) // FILE_NOT_FOUND
return callback(-6); // FILE_NOT_FOUND
} else {
return callback(content)
return callback(content);
}
})
}
});
};
app.on('session-created', function (ses) {
ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler)
})
ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler);
});
// The persistent path of "DevTools Extensions" preference file.
let loadedDevToolsExtensionsPath = null
let loadedDevToolsExtensionsPath = null;
app.on('will-quit', function () {
try {
const loadedDevToolsExtensions = Array.from(devToolsExtensionNames)
.map(name => manifestNameMap[name].srcDirectory)
.map(name => manifestNameMap[name].srcDirectory);
if (loadedDevToolsExtensions.length > 0) {
try {
fs.mkdirSync(path.dirname(loadedDevToolsExtensionsPath))
fs.mkdirSync(path.dirname(loadedDevToolsExtensionsPath));
} catch {
// Ignore error
}
fs.writeFileSync(loadedDevToolsExtensionsPath, JSON.stringify(loadedDevToolsExtensions))
fs.writeFileSync(loadedDevToolsExtensionsPath, JSON.stringify(loadedDevToolsExtensions));
} else {
fs.unlinkSync(loadedDevToolsExtensionsPath)
fs.unlinkSync(loadedDevToolsExtensionsPath);
}
} catch {
// Ignore error
}
})
});
// We can not use protocol or BrowserWindow until app is ready.
app.once('ready', function () {
// The public API to add/remove extensions.
BrowserWindow.addExtension = function (srcDirectory) {
const manifest = getManifestFromPath(srcDirectory)
const manifest = getManifestFromPath(srcDirectory);
if (manifest) {
loadExtension(manifest)
loadExtension(manifest);
for (const webContents of getAllWebContents()) {
if (isWindowOrWebView(webContents)) {
loadDevToolsExtensions(webContents, [manifest])
loadDevToolsExtensions(webContents, [manifest]);
}
}
return manifest.name
return manifest.name;
}
}
};
BrowserWindow.removeExtension = function (name) {
const manifest = manifestNameMap[name]
if (!manifest) return
const manifest = manifestNameMap[name];
if (!manifest) return;
removeBackgroundPages(manifest)
removeContentScripts(manifest)
delete manifestMap[manifest.extensionId]
delete manifestNameMap[name]
}
removeBackgroundPages(manifest);
removeContentScripts(manifest);
delete manifestMap[manifest.extensionId];
delete manifestNameMap[name];
};
BrowserWindow.getExtensions = function () {
const extensions = {}
const extensions = {};
Object.keys(manifestNameMap).forEach(function (name) {
const manifest = manifestNameMap[name]
extensions[name] = { name: manifest.name, version: manifest.version }
})
return extensions
}
const manifest = manifestNameMap[name];
extensions[name] = { name: manifest.name, version: manifest.version };
});
return extensions;
};
BrowserWindow.addDevToolsExtension = function (srcDirectory) {
const manifestName = BrowserWindow.addExtension(srcDirectory)
const manifestName = BrowserWindow.addExtension(srcDirectory);
if (manifestName) {
devToolsExtensionNames.add(manifestName)
devToolsExtensionNames.add(manifestName);
}
return manifestName
}
return manifestName;
};
BrowserWindow.removeDevToolsExtension = function (name) {
BrowserWindow.removeExtension(name)
devToolsExtensionNames.delete(name)
}
BrowserWindow.removeExtension(name);
devToolsExtensionNames.delete(name);
};
BrowserWindow.getDevToolsExtensions = function () {
const extensions = BrowserWindow.getExtensions()
const devExtensions = {}
const extensions = BrowserWindow.getExtensions();
const devExtensions = {};
Array.from(devToolsExtensionNames).forEach(function (name) {
if (!extensions[name]) return
devExtensions[name] = extensions[name]
})
return devExtensions
}
if (!extensions[name]) return;
devExtensions[name] = extensions[name];
});
return devExtensions;
};
// Load persisted extensions.
loadedDevToolsExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions')
loadedDevToolsExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions');
try {
const loadedDevToolsExtensions = JSON.parse(fs.readFileSync(loadedDevToolsExtensionsPath))
const loadedDevToolsExtensions = JSON.parse(fs.readFileSync(loadedDevToolsExtensionsPath));
if (Array.isArray(loadedDevToolsExtensions)) {
for (const srcDirectory of loadedDevToolsExtensions) {
// Start background pages and set content scripts.
BrowserWindow.addDevToolsExtension(srcDirectory)
BrowserWindow.addDevToolsExtension(srcDirectory);
}
}
} catch (error) {
if (process.env.ELECTRON_ENABLE_LOGGING && error.code !== 'ENOENT') {
console.error('Failed to load browser extensions from directory:', loadedDevToolsExtensionsPath)
console.error(error)
console.error('Failed to load browser extensions from directory:', loadedDevToolsExtensionsPath);
console.error(error);
}
}
})
});

View File

@@ -1,25 +1,25 @@
'use strict'
'use strict';
const { app } = require('electron')
const path = require('path')
const { app } = require('electron');
const path = require('path');
const getTempDirectory = function () {
try {
return app.getPath('temp')
return app.getPath('temp');
} catch {
// Delibrately laze-load the os module, this file is on the hot
// path when booting Electron and os takes between 5 - 8ms to load and we do not need it yet
return require('os').tmpdir()
return require('os').tmpdir();
}
}
};
exports.crashReporterInit = function (options) {
const productName = options.productName || app.name
const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`)
const productName = options.productName || app.name;
const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`);
return {
productName,
crashesDirectory,
appVersion: app.getVersion()
}
}
};
};

View File

@@ -1,11 +1,11 @@
import { shell, Menu } from 'electron'
import { shell, Menu } from 'electron';
const v8Util = process.electronBinding('v8_util')
const v8Util = process.electronBinding('v8_util');
const isMac = process.platform === 'darwin'
const isMac = process.platform === 'darwin';
export const setDefaultApplicationMenu = () => {
if (v8Util.getHiddenValue<boolean>(global, 'applicationMenuSet')) return
if (v8Util.getHiddenValue<boolean>(global, 'applicationMenuSet')) return;
const helpMenu: Electron.MenuItemConstructorOptions = {
role: 'help',
@@ -13,32 +13,32 @@ export const setDefaultApplicationMenu = () => {
{
label: 'Learn More',
click: async () => {
await shell.openExternal('https://electronjs.org')
await shell.openExternal('https://electronjs.org');
}
},
{
label: 'Documentation',
click: async () => {
const version = process.versions.electron
await shell.openExternal(`https://github.com/electron/electron/tree/v${version}/docs#readme`)
const version = process.versions.electron;
await shell.openExternal(`https://github.com/electron/electron/tree/v${version}/docs#readme`);
}
},
{
label: 'Community Discussions',
click: async () => {
await shell.openExternal('https://discuss.atom.io/c/electron')
await shell.openExternal('https://discuss.atom.io/c/electron');
}
},
{
label: 'Search Issues',
click: async () => {
await shell.openExternal('https://github.com/electron/electron/issues')
await shell.openExternal('https://github.com/electron/electron/issues');
}
}
]
}
};
const macAppMenu: Electron.MenuItemConstructorOptions = { role: 'appMenu' }
const macAppMenu: Electron.MenuItemConstructorOptions = { role: 'appMenu' };
const template: Electron.MenuItemConstructorOptions[] = [
...(isMac ? [macAppMenu] : []),
{ role: 'fileMenu' },
@@ -46,8 +46,8 @@ export const setDefaultApplicationMenu = () => {
{ role: 'viewMenu' },
{ role: 'windowMenu' },
helpMenu
]
];
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
}
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
};

View File

@@ -1,66 +1,66 @@
import { EventEmitter } from 'events'
import { EventEmitter } from 'events';
const { createDesktopCapturer } = process.electronBinding('desktop_capturer')
const { createDesktopCapturer } = process.electronBinding('desktop_capturer');
const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b)
const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b);
let currentlyRunning: {
options: ElectronInternal.GetSourcesOptions;
getSources: Promise<ElectronInternal.GetSourcesResult[]>;
}[] = []
}[] = [];
export const getSources = (event: Electron.IpcMainEvent, options: ElectronInternal.GetSourcesOptions) => {
for (const running of currentlyRunning) {
if (deepEqual(running.options, options)) {
// If a request is currently running for the same options
// return that promise
return running.getSources
return running.getSources;
}
}
const getSources = new Promise<ElectronInternal.GetSourcesResult[]>((resolve, reject) => {
let capturer: ElectronInternal.DesktopCapturer | null = createDesktopCapturer()
let capturer: ElectronInternal.DesktopCapturer | null = createDesktopCapturer();
const stopRunning = () => {
if (capturer) {
capturer.emit = null
capturer = null
capturer.emit = null;
capturer = null;
}
// Remove from currentlyRunning once we resolve or reject
currentlyRunning = currentlyRunning.filter(running => running.options !== options)
}
currentlyRunning = currentlyRunning.filter(running => running.options !== options);
};
const emitter = new EventEmitter()
const emitter = new EventEmitter();
emitter.once('error', (event, error: string) => {
stopRunning()
reject(error)
})
stopRunning();
reject(error);
});
emitter.once('finished', (event, sources: Electron.DesktopCapturerSource[], fetchWindowIcons: boolean) => {
stopRunning()
stopRunning();
resolve(sources.map(source => ({
id: source.id,
name: source.name,
thumbnail: source.thumbnail.toDataURL(),
display_id: source.display_id,
appIcon: (fetchWindowIcons && source.appIcon) ? source.appIcon.toDataURL() : null
})))
})
})));
});
capturer.emit = emitter.emit.bind(emitter)
capturer.startHandling(options.captureWindow, options.captureScreen, options.thumbnailSize, options.fetchWindowIcons)
capturer.emit = emitter.emit.bind(emitter);
capturer.startHandling(options.captureWindow, options.captureScreen, options.thumbnailSize, options.fetchWindowIcons);
// If the WebContents is destroyed before receiving result, just remove the
// reference to emit and the capturer itself so that it never dispatches
// back to the renderer
event.sender.once('destroyed', () => stopRunning())
})
event.sender.once('destroyed', () => stopRunning());
});
currentlyRunning.push({
options,
getSources
})
});
return getSources
}
return getSources;
};

View File

@@ -1,8 +1,8 @@
import { dialog, Menu } from 'electron'
import * as fs from 'fs'
import * as url from 'url'
import { dialog, Menu } from 'electron';
import * as fs from 'fs';
import * as url from 'url';
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
const convertToMenuTemplate = function (items: ContextMenuItem[], handler: (id: number) => void) {
return items.map(function (item) {
@@ -22,15 +22,15 @@ const convertToMenuTemplate = function (items: ContextMenuItem[], handler: (id:
type: 'normal',
label: item.label,
enabled: item.enabled
}
};
if (item.id != null) {
transformed.click = () => handler(item.id)
transformed.click = () => handler(item.id);
}
return transformed
})
}
return transformed;
});
};
const getEditMenuItems = function (): Electron.MenuItemConstructorOptions[] {
return [
@@ -43,56 +43,56 @@ const getEditMenuItems = function (): Electron.MenuItemConstructorOptions[] {
{ role: 'pasteAndMatchStyle' },
{ role: 'delete' },
{ role: 'selectAll' }
]
}
];
};
const isChromeDevTools = function (pageURL: string) {
const { protocol } = url.parse(pageURL)
return protocol === 'devtools:'
}
const { protocol } = url.parse(pageURL);
return protocol === 'devtools:';
};
const assertChromeDevTools = function (contents: Electron.WebContents, api: string) {
const pageURL = contents._getURL()
const pageURL = contents._getURL();
if (!isChromeDevTools(pageURL)) {
console.error(`Blocked ${pageURL} from calling ${api}`)
throw new Error(`Blocked ${api}`)
console.error(`Blocked ${pageURL} from calling ${api}`);
throw new Error(`Blocked ${api}`);
}
}
};
ipcMainUtils.handle('ELECTRON_INSPECTOR_CONTEXT_MENU', function (event: Electron.IpcMainEvent, items: ContextMenuItem[], isEditMenu: boolean) {
return new Promise(resolve => {
assertChromeDevTools(event.sender, 'window.InspectorFrontendHost.showContextMenuAtPoint()')
assertChromeDevTools(event.sender, 'window.InspectorFrontendHost.showContextMenuAtPoint()');
const template = isEditMenu ? getEditMenuItems() : convertToMenuTemplate(items, resolve)
const menu = Menu.buildFromTemplate(template)
const window = event.sender.getOwnerBrowserWindow()
const template = isEditMenu ? getEditMenuItems() : convertToMenuTemplate(items, resolve);
const menu = Menu.buildFromTemplate(template);
const window = event.sender.getOwnerBrowserWindow();
menu.popup({ window, callback: () => resolve() })
})
})
menu.popup({ window, callback: () => resolve() });
});
});
ipcMainUtils.handle('ELECTRON_INSPECTOR_SELECT_FILE', async function (event: Electron.IpcMainEvent) {
assertChromeDevTools(event.sender, 'window.UI.createFileSelectorElement()')
assertChromeDevTools(event.sender, 'window.UI.createFileSelectorElement()');
const result = await dialog.showOpenDialog({})
if (result.canceled) return []
const result = await dialog.showOpenDialog({});
if (result.canceled) return [];
const path = result.filePaths[0]
const data = await fs.promises.readFile(path)
const path = result.filePaths[0];
const data = await fs.promises.readFile(path);
return [path, data]
})
return [path, data];
});
ipcMainUtils.handle('ELECTRON_INSPECTOR_CONFIRM', async function (event: Electron.IpcMainEvent, message: string = '', title: string = '') {
assertChromeDevTools(event.sender, 'window.confirm()')
assertChromeDevTools(event.sender, 'window.confirm()');
const options = {
message: String(message),
title: String(title),
buttons: ['OK', 'Cancel'],
cancelId: 1
}
const window = event.sender.getOwnerBrowserWindow()
const { response } = await dialog.showMessageBox(window, options)
return response === 0
})
};
const window = event.sender.getOwnerBrowserWindow();
const { response } = await dialog.showMessageBox(window, options);
return response === 0;
});

View File

@@ -1,14 +1,14 @@
'use strict'
'use strict';
const { webContents } = require('electron')
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal')
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
const parseFeaturesString = require('@electron/internal/common/parse-features-string')
const { syncMethods, asyncMethods } = require('@electron/internal/common/web-view-methods')
const { serialize } = require('@electron/internal/common/type-utils')
const { webContents } = require('electron');
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
const parseFeaturesString = require('@electron/internal/common/parse-features-string');
const { syncMethods, asyncMethods, properties } = require('@electron/internal/common/web-view-methods');
const { serialize } = require('@electron/internal/common/type-utils');
// Doesn't exist in early initialization.
let webViewManager = null
let webViewManager = null;
const supportedWebViewEvents = [
'load-commit',
@@ -44,142 +44,142 @@ const supportedWebViewEvents = [
'found-in-page',
'did-change-theme-color',
'update-target-url'
]
];
const guestInstances = {}
const embedderElementsMap = {}
const guestInstances = {};
const embedderElementsMap = {};
// Create a new guest instance.
const createGuest = function (embedder, params) {
if (webViewManager == null) {
webViewManager = process.electronBinding('web_view_manager')
webViewManager = process.electronBinding('web_view_manager');
}
const guest = webContents.create({
type: 'webview',
partition: params.partition,
embedder: embedder
})
const guestInstanceId = guest.id
});
const guestInstanceId = guest.id;
guestInstances[guestInstanceId] = {
guest: guest,
embedder: embedder
}
};
// Clear the guest from map when it is destroyed.
guest.once('destroyed', () => {
if (guestInstanceId in guestInstances) {
detachGuest(embedder, guestInstanceId)
if (Object.prototype.hasOwnProperty.call(guestInstances, guestInstanceId)) {
detachGuest(embedder, guestInstanceId);
}
})
});
// Init guest web view after attached.
guest.once('did-attach', function (event) {
params = this.attachParams
delete this.attachParams
params = this.attachParams;
delete this.attachParams;
const previouslyAttached = this.viewInstanceId != null
this.viewInstanceId = params.instanceId
const previouslyAttached = this.viewInstanceId != null;
this.viewInstanceId = params.instanceId;
// Only load URL and set size on first attach
if (previouslyAttached) {
return
return;
}
if (params.src) {
const opts = {}
const opts = {};
if (params.httpreferrer) {
opts.httpReferrer = params.httpreferrer
opts.httpReferrer = params.httpreferrer;
}
if (params.useragent) {
opts.userAgent = params.useragent
opts.userAgent = params.useragent;
}
this.loadURL(params.src, opts)
this.loadURL(params.src, opts);
}
embedder.emit('did-attach-webview', event, guest)
})
embedder.emit('did-attach-webview', event, guest);
});
const sendToEmbedder = (channel, ...args) => {
if (!embedder.isDestroyed()) {
embedder._sendInternal(`${channel}-${guest.viewInstanceId}`, ...args)
embedder._sendInternal(`${channel}-${guest.viewInstanceId}`, ...args);
}
}
};
// Dispatch events to embedder.
const fn = function (event) {
guest.on(event, function (_, ...args) {
sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', event, ...args)
})
}
sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', event, ...args);
});
};
for (const event of supportedWebViewEvents) {
fn(event)
fn(event);
}
// Dispatch guest's IPC messages to embedder.
guest.on('ipc-message-host', function (_, channel, args) {
sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE', channel, ...args)
})
sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE', channel, ...args);
});
// Notify guest of embedder window visibility when it is ready
// FIXME Remove once https://github.com/electron/electron/issues/6828 is fixed
guest.on('dom-ready', function () {
const guestInstance = guestInstances[guestInstanceId]
const guestInstance = guestInstances[guestInstanceId];
if (guestInstance != null && guestInstance.visibilityState != null) {
guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', guestInstance.visibilityState)
guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', guestInstance.visibilityState);
}
})
});
// Forward internal web contents event to embedder to handle
// native window.open setup
guest.on('-add-new-contents', (...args) => {
if (guest.getLastWebPreferences().nativeWindowOpen === true) {
const embedder = getEmbedder(guestInstanceId)
const embedder = getEmbedder(guestInstanceId);
if (embedder != null) {
embedder.emit('-add-new-contents', ...args)
embedder.emit('-add-new-contents', ...args);
}
}
})
});
return guestInstanceId
}
return guestInstanceId;
};
// Attach the guest to an element of embedder.
const attachGuest = function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
const embedder = event.sender
const embedder = event.sender;
// Destroy the old guest when attaching.
const key = `${embedder.id}-${elementInstanceId}`
const oldGuestInstanceId = embedderElementsMap[key]
const key = `${embedder.id}-${elementInstanceId}`;
const oldGuestInstanceId = embedderElementsMap[key];
if (oldGuestInstanceId != null) {
// Reattachment to the same guest is just a no-op.
if (oldGuestInstanceId === guestInstanceId) {
return
return;
}
const oldGuestInstance = guestInstances[oldGuestInstanceId]
const oldGuestInstance = guestInstances[oldGuestInstanceId];
if (oldGuestInstance) {
oldGuestInstance.guest.detachFromOuterFrame()
oldGuestInstance.guest.detachFromOuterFrame();
}
}
const guestInstance = guestInstances[guestInstanceId]
const guestInstance = guestInstances[guestInstanceId];
// If this isn't a valid guest instance then do nothing.
if (!guestInstance) {
throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`)
throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`);
}
const { guest } = guestInstance
const { guest } = guestInstance;
if (guest.hostWebContents !== event.sender) {
throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`)
throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`);
}
// If this guest is already attached to an element then remove it
if (guestInstance.elementInstanceId) {
const oldKey = `${guestInstance.embedder.id}-${guestInstance.elementInstanceId}`
delete embedderElementsMap[oldKey]
const oldKey = `${guestInstance.embedder.id}-${guestInstance.elementInstanceId}`;
delete embedderElementsMap[oldKey];
// Remove guest from embedder if moving across web views
if (guest.viewInstanceId !== params.instanceId) {
webViewManager.removeGuest(guestInstance.embedder, guestInstanceId)
guestInstance.embedder._sendInternal(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${guest.viewInstanceId}`)
webViewManager.removeGuest(guestInstance.embedder, guestInstanceId);
guestInstance.embedder._sendInternal(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${guest.viewInstanceId}`);
}
}
@@ -194,7 +194,7 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
webSecurity: !params.disablewebsecurity,
enableBlinkFeatures: params.blinkfeatures,
disableBlinkFeatures: params.disableblinkfeatures
}
};
// parse the 'webpreferences' attribute string, if set
// this uses the same parsing rules as window.open uses for its features
@@ -202,14 +202,14 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
parseFeaturesString(params.webpreferences, function (key, value) {
if (value === undefined) {
// no value was specified, default it to true
value = true
value = true;
}
webPreferences[key] = value
})
webPreferences[key] = value;
});
}
if (params.preload) {
webPreferences.preloadURL = params.preload
webPreferences.preloadURL = params.preload;
}
// Security options that guest will always inherit from embedder
@@ -221,171 +221,189 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
['enableRemoteModule', false],
['sandbox', true],
['nodeIntegrationInSubFrames', false]
])
]);
// Inherit certain option values from embedder
const lastWebPreferences = embedder.getLastWebPreferences()
const lastWebPreferences = embedder.getLastWebPreferences();
for (const [name, value] of inheritedWebPreferences) {
if (lastWebPreferences[name] === value) {
webPreferences[name] = value
webPreferences[name] = value;
}
}
embedder.emit('will-attach-webview', event, webPreferences, params)
embedder.emit('will-attach-webview', event, webPreferences, params);
if (event.defaultPrevented) {
if (guest.viewInstanceId == null) guest.viewInstanceId = params.instanceId
guest.destroy()
return
if (guest.viewInstanceId == null) guest.viewInstanceId = params.instanceId;
guest.destroy();
return;
}
guest.attachParams = params
embedderElementsMap[key] = guestInstanceId
guest.attachParams = params;
embedderElementsMap[key] = guestInstanceId;
guest.setEmbedder(embedder)
guestInstance.embedder = embedder
guestInstance.elementInstanceId = elementInstanceId
guest.setEmbedder(embedder);
guestInstance.embedder = embedder;
guestInstance.elementInstanceId = elementInstanceId;
watchEmbedder(embedder)
watchEmbedder(embedder);
webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences)
guest.attachToIframe(embedder, embedderFrameId)
}
webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences);
guest.attachToIframe(embedder, embedderFrameId);
};
// Remove an guest-embedder relationship.
const detachGuest = function (embedder, guestInstanceId) {
const guestInstance = guestInstances[guestInstanceId]
const guestInstance = guestInstances[guestInstanceId];
if (embedder !== guestInstance.embedder) {
return
return;
}
webViewManager.removeGuest(embedder, guestInstanceId)
delete guestInstances[guestInstanceId]
webViewManager.removeGuest(embedder, guestInstanceId);
delete guestInstances[guestInstanceId];
const key = `${embedder.id}-${guestInstance.elementInstanceId}`
delete embedderElementsMap[key]
}
const key = `${embedder.id}-${guestInstance.elementInstanceId}`;
delete embedderElementsMap[key];
};
// Once an embedder has had a guest attached we watch it for destruction to
// destroy any remaining guests.
const watchedEmbedders = new Set()
const watchedEmbedders = new Set();
const watchEmbedder = function (embedder) {
if (watchedEmbedders.has(embedder)) {
return
return;
}
watchedEmbedders.add(embedder)
watchedEmbedders.add(embedder);
// Forward embedder window visiblity change events to guest
const onVisibilityChange = function (visibilityState) {
for (const guestInstanceId in guestInstances) {
const guestInstance = guestInstances[guestInstanceId]
guestInstance.visibilityState = visibilityState
for (const guestInstanceId of Object.keys(guestInstances)) {
const guestInstance = guestInstances[guestInstanceId];
guestInstance.visibilityState = visibilityState;
if (guestInstance.embedder === embedder) {
guestInstance.guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', visibilityState)
guestInstance.guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', visibilityState);
}
}
}
embedder.on('-window-visibility-change', onVisibilityChange)
};
embedder.on('-window-visibility-change', onVisibilityChange);
embedder.once('will-destroy', () => {
// Usually the guestInstances is cleared when guest is destroyed, but it
// may happen that the embedder gets manually destroyed earlier than guest,
// and the embedder will be invalid in the usual code path.
for (const guestInstanceId in guestInstances) {
const guestInstance = guestInstances[guestInstanceId]
for (const guestInstanceId of Object.keys(guestInstances)) {
const guestInstance = guestInstances[guestInstanceId];
if (guestInstance.embedder === embedder) {
detachGuest(embedder, parseInt(guestInstanceId))
detachGuest(embedder, parseInt(guestInstanceId));
}
}
// Clear the listeners.
embedder.removeListener('-window-visibility-change', onVisibilityChange)
watchedEmbedders.delete(embedder)
})
}
embedder.removeListener('-window-visibility-change', onVisibilityChange);
watchedEmbedders.delete(embedder);
});
};
const isWebViewTagEnabledCache = new WeakMap()
const isWebViewTagEnabledCache = new WeakMap();
const isWebViewTagEnabled = function (contents) {
if (!isWebViewTagEnabledCache.has(contents)) {
const webPreferences = contents.getLastWebPreferences() || {}
isWebViewTagEnabledCache.set(contents, !!webPreferences.webviewTag)
const webPreferences = contents.getLastWebPreferences() || {};
isWebViewTagEnabledCache.set(contents, !!webPreferences.webviewTag);
}
return isWebViewTagEnabledCache.get(contents)
}
return isWebViewTagEnabledCache.get(contents);
};
const handleMessage = function (channel, handler) {
ipcMainUtils.handle(channel, (event, ...args) => {
if (isWebViewTagEnabled(event.sender)) {
return handler(event, ...args)
return handler(event, ...args);
} else {
console.error(`<webview> IPC message ${channel} sent by WebContents with <webview> disabled (${event.sender.id})`)
throw new Error('<webview> disabled')
console.error(`<webview> IPC message ${channel} sent by WebContents with <webview> disabled (${event.sender.id})`);
throw new Error('<webview> disabled');
}
})
}
});
};
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params) {
return createGuest(event.sender, params)
})
return createGuest(event.sender, params);
});
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
try {
attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params)
attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params);
} catch (error) {
console.error(`Guest attach failed: ${error}`)
console.error(`Guest attach failed: ${error}`);
}
})
});
// this message is sent by the actual <webview>
ipcMainInternal.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, guestInstanceId) {
const guest = getGuest(guestInstanceId)
const guest = getGuest(guestInstanceId);
if (guest === event.sender) {
event.sender.emit('focus-change', {}, focus, guestInstanceId)
event.sender.emit('focus-change', {}, focus, guestInstanceId);
} else {
console.error(`focus-change for guestInstanceId: ${guestInstanceId}`)
console.error(`focus-change for guestInstanceId: ${guestInstanceId}`);
}
})
});
const allMethods = new Set([ ...syncMethods, ...asyncMethods ])
const allMethods = new Set([ ...syncMethods, ...asyncMethods ]);
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CALL', function (event, guestInstanceId, method, args) {
const guest = getGuestForWebContents(guestInstanceId, event.sender)
const guest = getGuestForWebContents(guestInstanceId, event.sender);
if (!allMethods.has(method)) {
throw new Error(`Invalid method: ${method}`)
throw new Error(`Invalid method: ${method}`);
}
return guest[method](...args)
})
return guest[method](...args);
});
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_GET', function (event, guestInstanceId, property) {
const guest = getGuestForWebContents(guestInstanceId, event.sender);
if (!properties.has(property)) {
throw new Error(`Invalid property: ${property}`);
}
return guest[property];
});
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_SET', function (event, guestInstanceId, property, val) {
const guest = getGuestForWebContents(guestInstanceId, event.sender);
if (!properties.has(property)) {
throw new Error(`Invalid property: ${property}`);
}
guest[property] = val;
});
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CAPTURE_PAGE', async function (event, guestInstanceId, args) {
const guest = getGuestForWebContents(guestInstanceId, event.sender)
const guest = getGuestForWebContents(guestInstanceId, event.sender);
return serialize(await guest.capturePage(...args))
})
return serialize(await guest.capturePage(...args));
});
// Returns WebContents from its guest id hosted in given webContents.
const getGuestForWebContents = function (guestInstanceId, contents) {
const guest = getGuest(guestInstanceId)
const guest = getGuest(guestInstanceId);
if (!guest) {
throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`)
throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`);
}
if (guest.hostWebContents !== contents) {
throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`)
throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`);
}
return guest
}
return guest;
};
// Returns WebContents from its guest id.
const getGuest = function (guestInstanceId) {
const guestInstance = guestInstances[guestInstanceId]
if (guestInstance != null) return guestInstance.guest
}
const guestInstance = guestInstances[guestInstanceId];
if (guestInstance != null) return guestInstance.guest;
};
// Returns the embedder of the guest.
const getEmbedder = function (guestInstanceId) {
const guestInstance = guestInstances[guestInstanceId]
if (guestInstance != null) return guestInstance.embedder
}
const guestInstance = guestInstances[guestInstanceId];
if (guestInstance != null) return guestInstance.embedder;
};
exports.getGuestForWebContents = getGuestForWebContents
exports.isWebViewTagEnabled = isWebViewTagEnabled
exports.getGuestForWebContents = getGuestForWebContents;
exports.isWebViewTagEnabled = isWebViewTagEnabled;

View File

@@ -1,14 +1,14 @@
'use strict'
'use strict';
const electron = require('electron')
const { BrowserWindow } = electron
const { isSameOrigin } = process.electronBinding('v8_util')
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal')
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
const parseFeaturesString = require('@electron/internal/common/parse-features-string')
const electron = require('electron');
const { BrowserWindow } = electron;
const { isSameOrigin } = process.electronBinding('v8_util');
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
const parseFeaturesString = require('@electron/internal/common/parse-features-string');
const hasProp = {}.hasOwnProperty
const frameToGuest = new Map()
const hasProp = {}.hasOwnProperty;
const frameToGuest = new Map();
// Security options that child windows will always inherit from parent windows
const inheritedWebPreferences = new Map([
@@ -20,109 +20,109 @@ const inheritedWebPreferences = new Map([
['sandbox', true],
['webviewTag', false],
['nodeIntegrationInSubFrames', false]
])
]);
// Copy attribute of |parent| to |child| if it is not defined in |child|.
const mergeOptions = function (child, parent, visited) {
// Check for circular reference.
if (visited == null) visited = new Set()
if (visited.has(parent)) return
if (visited == null) visited = new Set();
if (visited.has(parent)) return;
visited.add(parent)
visited.add(parent);
for (const key in parent) {
if (key === 'type') continue
if (!hasProp.call(parent, key)) continue
if (key in child && key !== 'webPreferences') continue
if (key === 'type') continue;
if (!hasProp.call(parent, key)) continue;
if (key in child && key !== 'webPreferences') continue;
const value = parent[key]
const value = parent[key];
if (typeof value === 'object' && !Array.isArray(value)) {
child[key] = mergeOptions(child[key] || {}, value, visited)
child[key] = mergeOptions(child[key] || {}, value, visited);
} else {
child[key] = value
child[key] = value;
}
}
visited.delete(parent)
visited.delete(parent);
return child
}
return child;
};
// Merge |options| with the |embedder|'s window's options.
const mergeBrowserWindowOptions = function (embedder, options) {
if (options.webPreferences == null) {
options.webPreferences = {}
options.webPreferences = {};
}
if (embedder.browserWindowOptions != null) {
let parentOptions = embedder.browserWindowOptions
let parentOptions = embedder.browserWindowOptions;
// if parent's visibility is available, that overrides 'show' flag (#12125)
const win = BrowserWindow.fromWebContents(embedder.webContents)
const win = BrowserWindow.fromWebContents(embedder.webContents);
if (win != null) {
parentOptions = { ...embedder.browserWindowOptions, show: win.isVisible() }
parentOptions = { ...embedder.browserWindowOptions, show: win.isVisible() };
}
// Inherit the original options if it is a BrowserWindow.
mergeOptions(options, parentOptions)
mergeOptions(options, parentOptions);
} else {
// Or only inherit webPreferences if it is a webview.
mergeOptions(options.webPreferences, embedder.getLastWebPreferences())
mergeOptions(options.webPreferences, embedder.getLastWebPreferences());
}
// Inherit certain option values from parent window
const webPreferences = embedder.getLastWebPreferences()
const webPreferences = embedder.getLastWebPreferences();
for (const [name, value] of inheritedWebPreferences) {
if (webPreferences[name] === value) {
options.webPreferences[name] = value
options.webPreferences[name] = value;
}
}
if (!webPreferences.nativeWindowOpen) {
// Sets correct openerId here to give correct options to 'new-window' event handler
options.webPreferences.openerId = embedder.id
options.webPreferences.openerId = embedder.id;
}
return options
}
return options;
};
// Setup a new guest with |embedder|
const setupGuest = function (embedder, frameName, guest, options) {
// When |embedder| is destroyed we should also destroy attached guest, and if
// guest is closed by user then we should prevent |embedder| from double
// closing guest.
const guestId = guest.webContents.id
const guestId = guest.webContents.id;
const closedByEmbedder = function () {
guest.removeListener('closed', closedByUser)
guest.destroy()
}
guest.removeListener('closed', closedByUser);
guest.destroy();
};
const closedByUser = function () {
embedder._sendInternal('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
embedder.removeListener('current-render-view-deleted', closedByEmbedder)
}
embedder.once('current-render-view-deleted', closedByEmbedder)
guest.once('closed', closedByUser)
embedder._sendInternal('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId);
embedder.removeListener('current-render-view-deleted', closedByEmbedder);
};
embedder.once('current-render-view-deleted', closedByEmbedder);
guest.once('closed', closedByUser);
if (frameName) {
frameToGuest.set(frameName, guest)
guest.frameName = frameName
frameToGuest.set(frameName, guest);
guest.frameName = frameName;
guest.once('closed', function () {
frameToGuest.delete(frameName)
})
frameToGuest.delete(frameName);
});
}
return guestId
}
return guestId;
};
// Create a new guest created by |embedder| with |options|.
const createGuest = function (embedder, url, referrer, frameName, options, postData) {
let guest = frameToGuest.get(frameName)
let guest = frameToGuest.get(frameName);
if (frameName && (guest != null)) {
guest.loadURL(url)
return guest.webContents.id
guest.loadURL(url);
return guest.webContents.id;
}
// Remember the embedder window's id.
if (options.webPreferences == null) {
options.webPreferences = {}
options.webPreferences = {};
}
guest = new BrowserWindow(options)
guest = new BrowserWindow(options);
if (!options.webContents) {
// We should not call `loadURL` if the window was constructed from an
// existing webContents (window.open in a sandboxed renderer).
@@ -131,214 +131,214 @@ const createGuest = function (embedder, url, referrer, frameName, options, postD
// webContents is not necessary (it will navigate there anyway).
const loadOptions = {
httpReferrer: referrer
}
};
if (postData != null) {
loadOptions.postData = postData
loadOptions.extraHeaders = 'content-type: application/x-www-form-urlencoded'
loadOptions.postData = postData;
loadOptions.extraHeaders = 'content-type: application/x-www-form-urlencoded';
if (postData.length > 0) {
const postDataFront = postData[0].bytes.toString()
const boundary = /^--.*[^-\r\n]/.exec(postDataFront)
const postDataFront = postData[0].bytes.toString();
const boundary = /^--.*[^-\r\n]/.exec(postDataFront);
if (boundary != null) {
loadOptions.extraHeaders = `content-type: multipart/form-data; boundary=${boundary[0].substr(2)}`
loadOptions.extraHeaders = `content-type: multipart/form-data; boundary=${boundary[0].substr(2)}`;
}
}
}
guest.loadURL(url, loadOptions)
guest.loadURL(url, loadOptions);
}
return setupGuest(embedder, frameName, guest, options)
}
return setupGuest(embedder, frameName, guest, options);
};
const getGuestWindow = function (guestContents) {
let guestWindow = BrowserWindow.fromWebContents(guestContents)
let guestWindow = BrowserWindow.fromWebContents(guestContents);
if (guestWindow == null) {
const hostContents = guestContents.hostWebContents
const hostContents = guestContents.hostWebContents;
if (hostContents != null) {
guestWindow = BrowserWindow.fromWebContents(hostContents)
guestWindow = BrowserWindow.fromWebContents(hostContents);
}
}
if (!guestWindow) {
throw new Error('getGuestWindow failed')
throw new Error('getGuestWindow failed');
}
return guestWindow
}
return guestWindow;
};
const isChildWindow = function (sender, target) {
return target.getLastWebPreferences().openerId === sender.id
}
return target.getLastWebPreferences().openerId === sender.id;
};
const isRelatedWindow = function (sender, target) {
return isChildWindow(sender, target) || isChildWindow(target, sender)
}
return isChildWindow(sender, target) || isChildWindow(target, sender);
};
const isScriptableWindow = function (sender, target) {
return isRelatedWindow(sender, target) && isSameOrigin(sender.getURL(), target.getURL())
}
return isRelatedWindow(sender, target) && isSameOrigin(sender.getURL(), target.getURL());
};
const isNodeIntegrationEnabled = function (sender) {
return sender.getLastWebPreferences().nodeIntegration === true
}
return sender.getLastWebPreferences().nodeIntegration === true;
};
// Checks whether |sender| can access the |target|:
const canAccessWindow = function (sender, target) {
return isChildWindow(sender, target) ||
isScriptableWindow(sender, target) ||
isNodeIntegrationEnabled(sender)
}
isNodeIntegrationEnabled(sender);
};
// Routed window.open messages with raw options
ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName, features) => {
if (url == null || url === '') url = 'about:blank'
if (frameName == null) frameName = ''
if (features == null) features = ''
if (url == null || url === '') url = 'about:blank';
if (frameName == null) frameName = '';
if (features == null) features = '';
const options = {}
const options = {};
const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor']
const webPreferences = ['zoomFactor', 'nodeIntegration', 'enableRemoteModule', 'preload', 'javascript', 'contextIsolation', 'webviewTag']
const disposition = 'new-window'
const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor'];
const webPreferences = ['zoomFactor', 'nodeIntegration', 'enableRemoteModule', 'preload', 'javascript', 'contextIsolation', 'webviewTag'];
const disposition = 'new-window';
// Used to store additional features
const additionalFeatures = []
const additionalFeatures = [];
// Parse the features
parseFeaturesString(features, function (key, value) {
if (value === undefined) {
additionalFeatures.push(key)
additionalFeatures.push(key);
} else {
// Don't allow webPreferences to be set since it must be an object
// that cannot be directly overridden
if (key === 'webPreferences') return
if (key === 'webPreferences') return;
if (webPreferences.includes(key)) {
if (options.webPreferences == null) {
options.webPreferences = {}
options.webPreferences = {};
}
options.webPreferences[key] = value
options.webPreferences[key] = value;
} else {
options[key] = value
options[key] = value;
}
}
})
});
if (options.left) {
if (options.x == null) {
options.x = options.left
options.x = options.left;
}
}
if (options.top) {
if (options.y == null) {
options.y = options.top
options.y = options.top;
}
}
if (options.title == null) {
options.title = frameName
options.title = frameName;
}
if (options.width == null) {
options.width = 800
options.width = 800;
}
if (options.height == null) {
options.height = 600
options.height = 600;
}
for (const name of ints) {
if (options[name] != null) {
options[name] = parseInt(options[name], 10)
options[name] = parseInt(options[name], 10);
}
}
const referrer = { url: '', policy: 'default' }
internalWindowOpen(event, url, referrer, frameName, disposition, options, additionalFeatures)
})
const referrer = { url: '', policy: 'default' };
internalWindowOpen(event, url, referrer, frameName, disposition, options, additionalFeatures);
});
// Routed window.open messages with fully parsed options
function internalWindowOpen (event, url, referrer, frameName, disposition, options, additionalFeatures, postData) {
options = mergeBrowserWindowOptions(event.sender, options)
event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures, referrer)
const { newGuest } = event
options = mergeBrowserWindowOptions(event.sender, options);
event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures, referrer);
const { newGuest } = event;
if ((event.sender.getType() === 'webview' && event.sender.getLastWebPreferences().disablePopups) || event.defaultPrevented) {
if (newGuest != null) {
if (options.webContents === newGuest.webContents) {
// the webContents is not changed, so set defaultPrevented to false to
// stop the callers of this event from destroying the webContents.
event.defaultPrevented = false
event.defaultPrevented = false;
}
event.returnValue = setupGuest(event.sender, frameName, newGuest, options)
event.returnValue = setupGuest(event.sender, frameName, newGuest, options);
} else {
event.returnValue = null
event.returnValue = null;
}
} else {
event.returnValue = createGuest(event.sender, url, referrer, frameName, options, postData)
event.returnValue = createGuest(event.sender, url, referrer, frameName, options, postData);
}
}
const handleMessage = function (channel, handler) {
ipcMainUtils.handle(channel, (event, guestId, ...args) => {
// Access webContents via electron to prevent circular require.
const guestContents = electron.webContents.fromId(guestId)
const guestContents = electron.webContents.fromId(guestId);
if (!guestContents) {
throw new Error(`Invalid guestId: ${guestId}`)
throw new Error(`Invalid guestId: ${guestId}`);
}
return handler(event, guestContents, ...args)
})
}
return handler(event, guestContents, ...args);
});
};
const securityCheck = function (contents, guestContents, check) {
if (!check(contents, guestContents)) {
console.error(`Blocked ${contents.getURL()} from accessing guestId: ${guestContents.id}`)
throw new Error(`Access denied to guestId: ${guestContents.id}`)
console.error(`Blocked ${contents.getURL()} from accessing guestId: ${guestContents.id}`);
throw new Error(`Access denied to guestId: ${guestContents.id}`);
}
}
};
const windowMethods = new Set([
'destroy',
'focus',
'blur'
])
]);
handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', (event, guestContents, method, ...args) => {
securityCheck(event.sender, guestContents, canAccessWindow)
securityCheck(event.sender, guestContents, canAccessWindow);
if (!windowMethods.has(method)) {
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`)
throw new Error(`Invalid method: ${method}`)
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`);
throw new Error(`Invalid method: ${method}`);
}
return getGuestWindow(guestContents)[method](...args)
})
return getGuestWindow(guestContents)[method](...args);
});
handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', (event, guestContents, message, targetOrigin, sourceOrigin) => {
if (targetOrigin == null) {
targetOrigin = '*'
targetOrigin = '*';
}
// The W3C does not seem to have word on how postMessage should work when the
// origins do not match, so we do not do |canAccessWindow| check here since
// postMessage across origins is useful and not harmful.
securityCheck(event.sender, guestContents, isRelatedWindow)
securityCheck(event.sender, guestContents, isRelatedWindow);
if (targetOrigin === '*' || isSameOrigin(guestContents.getURL(), targetOrigin)) {
const sourceId = event.sender.id
guestContents._sendInternal('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
const sourceId = event.sender.id;
guestContents._sendInternal('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin);
}
})
});
const webContentsMethods = new Set([
'getURL',
'loadURL',
'executeJavaScript',
'print'
])
]);
handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => {
securityCheck(event.sender, guestContents, canAccessWindow)
securityCheck(event.sender, guestContents, canAccessWindow);
if (!webContentsMethods.has(method)) {
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`)
throw new Error(`Invalid method: ${method}`)
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`);
throw new Error(`Invalid method: ${method}`);
}
return guestContents[method](...args)
})
return guestContents[method](...args);
});
exports.internalWindowOpen = internalWindowOpen
exports.internalWindowOpen = internalWindowOpen;

View File

@@ -1,45 +1,47 @@
import { Buffer } from 'buffer'
import * as fs from 'fs'
import * as path from 'path'
import * as util from 'util'
import { Buffer } from 'buffer';
import * as fs from 'fs';
import * as path from 'path';
import * as util from 'util';
const Module = require('module')
const Module = require('module');
// We modified the original process.argv to let node.js load the init.js,
// we need to restore it here.
process.argv.splice(1, 1)
process.argv.splice(1, 1);
// Clear search paths.
require('../common/reset-search-paths')
require('../common/reset-search-paths');
// Import common settings.
require('@electron/internal/common/init')
require('@electron/internal/common/init');
if (process.platform === 'win32') {
// Redirect node's console to use our own implementations, since node can not
// handle console output when running as GUI program.
const consoleLog = (format: any, ...args: any[]) => {
return process.log(util.format(format, ...args) + '\n')
}
const consoleLog = (...args: any[]) => {
// @ts-ignore this typing is incorrect; 'format' is an optional parameter
// See https://nodejs.org/api/util.html#util_util_format_format_args
return process.log(util.format(...args) + '\n');
};
const streamWrite: NodeJS.WritableStream['write'] = function (chunk: Buffer | string, encoding?: any, callback?: Function) {
if (Buffer.isBuffer(chunk)) {
chunk = chunk.toString(encoding)
chunk = chunk.toString(encoding);
}
process.log(chunk)
process.log(chunk);
if (callback) {
callback()
callback();
}
return true
}
console.log = console.error = console.warn = consoleLog
process.stdout.write = process.stderr.write = streamWrite
return true;
};
console.log = console.error = console.warn = consoleLog;
process.stdout.write = process.stderr.write = streamWrite;
}
// Don't quit on fatal error.
process.on('uncaughtException', function (error) {
// Do nothing if the user has a custom uncaught exception handler.
if (process.listenerCount('uncaughtException') > 1) {
return
return;
}
// Show error in GUI.
@@ -48,18 +50,18 @@ process.on('uncaughtException', function (error) {
// so we import it inside the handler down here
import('electron')
.then(({ dialog }) => {
const stack = error.stack ? error.stack : `${error.name}: ${error.message}`
const message = 'Uncaught Exception:\n' + stack
dialog.showErrorBox('A JavaScript error occurred in the main process', message)
})
})
const stack = error.stack ? error.stack : `${error.name}: ${error.message}`;
const message = 'Uncaught Exception:\n' + stack;
dialog.showErrorBox('A JavaScript error occurred in the main process', message);
});
});
// Emit 'exit' event on quit.
const { app } = require('electron')
const { app } = require('electron');
app.on('quit', function (event, exitCode) {
process.emit('exit', exitCode)
})
process.emit('exit', exitCode);
});
if (process.platform === 'win32') {
// If we are a Squirrel.Windows-installed app, set app user model ID
@@ -76,131 +78,131 @@ if (process.platform === 'win32') {
// form `com.squirrel.PACKAGE-NAME.OUREXE`. We need to call
// app.setAppUserModelId with a matching identifier so that renderer processes
// will inherit this value.
const updateDotExe = path.join(path.dirname(process.execPath), '..', 'update.exe')
const updateDotExe = path.join(path.dirname(process.execPath), '..', 'update.exe');
if (fs.existsSync(updateDotExe)) {
const packageDir = path.dirname(path.resolve(updateDotExe))
const packageName = path.basename(packageDir).replace(/\s/g, '')
const exeName = path.basename(process.execPath).replace(/\.exe$/i, '').replace(/\s/g, '')
const packageDir = path.dirname(path.resolve(updateDotExe));
const packageName = path.basename(packageDir).replace(/\s/g, '');
const exeName = path.basename(process.execPath).replace(/\.exe$/i, '').replace(/\s/g, '');
app.setAppUserModelId(`com.squirrel.${packageName}.${exeName}`)
app.setAppUserModelId(`com.squirrel.${packageName}.${exeName}`);
}
}
// Map process.exit to app.exit, which quits gracefully.
process.exit = app.exit as () => never
process.exit = app.exit as () => never;
// Load the RPC server.
require('@electron/internal/browser/rpc-server')
require('@electron/internal/browser/rpc-server');
// Load the guest view manager.
require('@electron/internal/browser/guest-view-manager')
require('@electron/internal/browser/guest-window-manager')
require('@electron/internal/browser/guest-view-manager');
require('@electron/internal/browser/guest-window-manager');
// Now we try to load app's package.json.
let packagePath = null
let packageJson = null
const searchPaths = ['app', 'app.asar', 'default_app.asar']
let packagePath = null;
let packageJson = null;
const searchPaths = ['app', 'app.asar', 'default_app.asar'];
if (process.resourcesPath) {
for (packagePath of searchPaths) {
try {
packagePath = path.join(process.resourcesPath, packagePath)
packageJson = Module._load(path.join(packagePath, 'package.json'))
break
packagePath = path.join(process.resourcesPath, packagePath);
packageJson = Module._load(path.join(packagePath, 'package.json'));
break;
} catch {
continue
continue;
}
}
}
if (packageJson == null) {
process.nextTick(function () {
return process.exit(1)
})
throw new Error('Unable to find a valid app')
return process.exit(1);
});
throw new Error('Unable to find a valid app');
}
// Set application's version.
if (packageJson.version != null) {
app.setVersion(packageJson.version)
app.setVersion(packageJson.version);
}
// Set application's name.
if (packageJson.productName != null) {
app.name = `${packageJson.productName}`.trim()
app.name = `${packageJson.productName}`.trim();
} else if (packageJson.name != null) {
app.name = `${packageJson.name}`.trim()
app.name = `${packageJson.name}`.trim();
}
// Set application's desktop name.
if (packageJson.desktopName != null) {
app.setDesktopName(packageJson.desktopName)
app.setDesktopName(packageJson.desktopName);
} else {
app.setDesktopName(`${app.name}.desktop`)
app.setDesktopName(`${app.name}.desktop`);
}
// Set v8 flags, delibrately lazy load so that apps that do not use this
// feature do not pay the price
if (packageJson.v8Flags != null) {
require('v8').setFlagsFromString(packageJson.v8Flags)
require('v8').setFlagsFromString(packageJson.v8Flags);
}
app._setDefaultAppPaths(packagePath)
app._setDefaultAppPaths(packagePath);
// Load the chrome devtools support.
require('@electron/internal/browser/devtools')
require('@electron/internal/browser/devtools');
// Load the chrome extension support.
require('@electron/internal/browser/chrome-extension')
require('@electron/internal/browser/chrome-extension');
// Load protocol module to ensure it is populated on app ready
require('@electron/internal/browser/api/protocol')
require('@electron/internal/browser/api/protocol');
// Set main startup script of the app.
const mainStartupScript = packageJson.main || 'index.js'
const mainStartupScript = packageJson.main || 'index.js';
const KNOWN_XDG_DESKTOP_VALUES = ['Pantheon', 'Unity:Unity7', 'pop:GNOME']
const KNOWN_XDG_DESKTOP_VALUES = ['Pantheon', 'Unity:Unity7', 'pop:GNOME'];
function currentPlatformSupportsAppIndicator () {
if (process.platform !== 'linux') return false
const currentDesktop = process.env.XDG_CURRENT_DESKTOP
if (process.platform !== 'linux') return false;
const currentDesktop = process.env.XDG_CURRENT_DESKTOP;
if (!currentDesktop) return false
if (KNOWN_XDG_DESKTOP_VALUES.includes(currentDesktop)) return true
if (!currentDesktop) return false;
if (KNOWN_XDG_DESKTOP_VALUES.includes(currentDesktop)) return true;
// ubuntu based or derived session (default ubuntu one, communitheme…) supports
// indicator too.
if (/ubuntu/ig.test(currentDesktop)) return true
if (/ubuntu/ig.test(currentDesktop)) return true;
return false
return false;
}
// Workaround for electron/electron#5050 and electron/electron#9046
if (currentPlatformSupportsAppIndicator()) {
process.env.XDG_CURRENT_DESKTOP = 'Unity'
process.env.XDG_CURRENT_DESKTOP = 'Unity';
}
// Quit when all windows are closed and no other one is listening to this.
app.on('window-all-closed', () => {
if (app.listenerCount('window-all-closed') === 1) {
app.quit()
app.quit();
}
})
});
const { setDefaultApplicationMenu } = require('@electron/internal/browser/default-menu')
const { setDefaultApplicationMenu } = require('@electron/internal/browser/default-menu');
// Create default menu.
//
// Note that the task must be added before loading any app, so we can make sure
// the call is maded before any user window is created, otherwise the default
// menu may show even when user explicitly hides the menu.
app.once('ready', setDefaultApplicationMenu)
app.once('ready', setDefaultApplicationMenu);
if (packagePath) {
// Finally load app's main.js and transfer control to C++.
process._firstFileName = Module._resolveFilename(path.join(packagePath, mainStartupScript), null, false)
Module._load(path.join(packagePath, mainStartupScript), Module, true)
process._firstFileName = Module._resolveFilename(path.join(packagePath, mainStartupScript), null, false);
Module._load(path.join(packagePath, mainStartupScript), Module, true);
} else {
console.error('Failed to locate a valid package to load (app, app.asar or default_app.asar)')
console.error('This normally means you\'ve damaged the Electron package somehow')
console.error('Failed to locate a valid package to load (app, app.asar or default_app.asar)');
console.error('This normally means you\'ve damaged the Electron package somehow');
}

View File

@@ -1,56 +1,56 @@
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal'
import * as errorUtils from '@electron/internal/common/error-utils'
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
import * as errorUtils from '@electron/internal/common/error-utils';
type IPCHandler = (event: ElectronInternal.IpcMainInternalEvent, ...args: any[]) => any
const callHandler = async function (handler: IPCHandler, event: ElectronInternal.IpcMainInternalEvent, args: any[], reply: (args: any[]) => void) {
try {
const result = await handler(event, ...args)
reply([null, result])
const result = await handler(event, ...args);
reply([null, result]);
} catch (error) {
reply([errorUtils.serialize(error)])
reply([errorUtils.serialize(error)]);
}
}
};
export const handle = function <T extends IPCHandler> (channel: string, handler: T) {
ipcMainInternal.on(channel, (event, requestId, ...args) => {
callHandler(handler, event, args, responseArgs => {
if (requestId) {
event._replyInternal(`${channel}_RESPONSE_${requestId}`, ...responseArgs)
event._replyInternal(`${channel}_RESPONSE_${requestId}`, ...responseArgs);
} else {
event.returnValue = responseArgs
event.returnValue = responseArgs;
}
})
})
}
});
});
};
let nextId = 0
let nextId = 0;
export function invokeInWebContents<T> (sender: Electron.WebContentsInternal, sendToAll: boolean, command: string, ...args: any[]) {
return new Promise<T>((resolve, reject) => {
const requestId = ++nextId
const channel = `${command}_RESPONSE_${requestId}`
const requestId = ++nextId;
const channel = `${command}_RESPONSE_${requestId}`;
ipcMainInternal.on(channel, function handler (
event, error: Electron.SerializedError, result: any
) {
if (event.sender !== sender) {
console.error(`Reply to ${command} sent by unexpected WebContents (${event.sender.id})`)
return
console.error(`Reply to ${command} sent by unexpected WebContents (${event.sender.id})`);
return;
}
ipcMainInternal.removeListener(channel, handler)
ipcMainInternal.removeListener(channel, handler);
if (error) {
reject(errorUtils.deserialize(error))
reject(errorUtils.deserialize(error));
} else {
resolve(result)
resolve(result);
}
})
});
if (sendToAll) {
sender._sendInternalToAll(command, requestId, ...args)
sender._sendInternalToAll(command, requestId, ...args);
} else {
sender._sendInternal(command, requestId, ...args)
sender._sendInternal(command, requestId, ...args);
}
})
});
}

View File

@@ -1,8 +1,8 @@
import { EventEmitter } from 'events'
import { EventEmitter } from 'events';
const emitter = new EventEmitter()
const emitter = new EventEmitter();
// Do not throw exception when channel name is "error".
emitter.on('error', () => {})
emitter.on('error', () => {});
export const ipcMainInternal = emitter as ElectronInternal.IpcMainInternal
export const ipcMainInternal = emitter as ElectronInternal.IpcMainInternal;

View File

@@ -1,23 +1,23 @@
'use strict'
'use strict';
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal')
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
// The history operation in renderer is redirected to browser.
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK', function (event) {
event.sender.goBack()
})
event.sender.goBack();
});
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD', function (event) {
event.sender.goForward()
})
event.sender.goForward();
});
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', function (event, offset) {
event.sender.goToOffset(offset)
})
event.sender.goToOffset(offset);
});
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) {
event.returnValue = event.sender.length()
})
event.returnValue = event.sender.length();
});
// JavaScript implementation of Chromium's NavigationController.
// Instead of relying on Chromium for history control, we compeletely do history
@@ -26,64 +26,64 @@ ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) {
// process is restarted everytime.
const NavigationController = (function () {
function NavigationController (webContents) {
this.webContents = webContents
this.clearHistory()
this.webContents = webContents;
this.clearHistory();
// webContents may have already navigated to a page.
if (this.webContents._getURL()) {
this.currentIndex++
this.history.push(this.webContents._getURL())
this.currentIndex++;
this.history.push(this.webContents._getURL());
}
this.webContents.on('navigation-entry-commited', (event, url, inPage, replaceEntry) => {
if (this.inPageIndex > -1 && !inPage) {
// Navigated to a new page, clear in-page mark.
this.inPageIndex = -1
this.inPageIndex = -1;
} else if (this.inPageIndex === -1 && inPage && !replaceEntry) {
// Started in-page navigations.
this.inPageIndex = this.currentIndex
this.inPageIndex = this.currentIndex;
}
if (this.pendingIndex >= 0) {
// Go to index.
this.currentIndex = this.pendingIndex
this.pendingIndex = -1
this.history[this.currentIndex] = url
this.currentIndex = this.pendingIndex;
this.pendingIndex = -1;
this.history[this.currentIndex] = url;
} else if (replaceEntry) {
// Non-user initialized navigation.
this.history[this.currentIndex] = url
this.history[this.currentIndex] = url;
} else {
// Normal navigation. Clear history.
this.history = this.history.slice(0, this.currentIndex + 1)
this.currentIndex++
this.history.push(url)
this.history = this.history.slice(0, this.currentIndex + 1);
this.currentIndex++;
this.history.push(url);
}
})
});
}
NavigationController.prototype.loadURL = function (url, options) {
if (options == null) {
options = {}
options = {};
}
const p = new Promise((resolve, reject) => {
const resolveAndCleanup = () => {
removeListeners()
resolve()
}
removeListeners();
resolve();
};
const rejectAndCleanup = (errorCode, errorDescription, url) => {
const err = new Error(`${errorDescription} (${errorCode}) loading '${typeof url === 'string' ? url.substr(0, 2048) : url}'`)
Object.assign(err, { errno: errorCode, code: errorDescription, url })
removeListeners()
reject(err)
}
const err = new Error(`${errorDescription} (${errorCode}) loading '${typeof url === 'string' ? url.substr(0, 2048) : url}'`);
Object.assign(err, { errno: errorCode, code: errorDescription, url });
removeListeners();
reject(err);
};
const finishListener = () => {
resolveAndCleanup()
}
resolveAndCleanup();
};
const failListener = (event, errorCode, errorDescription, validatedURL, isMainFrame, frameProcessId, frameRoutingId) => {
if (isMainFrame) {
rejectAndCleanup(errorCode, errorDescription, validatedURL)
rejectAndCleanup(errorCode, errorDescription, validatedURL);
}
}
};
let navigationStarted = false
let navigationStarted = false;
const navigationListener = (event, url, isSameDocument, isMainFrame, frameProcessId, frameRoutingId, navigationId) => {
if (isMainFrame) {
if (navigationStarted && !isSameDocument) {
@@ -96,11 +96,11 @@ const NavigationController = (function () {
// considered navigation events but are triggered with isSameDocument.
// We can ignore these to allow virtual routing on page load as long
// as the routing does not leave the document
return rejectAndCleanup(-3, 'ERR_ABORTED', url)
return rejectAndCleanup(-3, 'ERR_ABORTED', url);
}
navigationStarted = true
navigationStarted = true;
}
}
};
const stopLoadingListener = () => {
// By the time we get here, either 'finish' or 'fail' should have fired
// if the navigation occurred. However, in some situations (e.g. when
@@ -110,134 +110,134 @@ const NavigationController = (function () {
// TODO(jeremy): enumerate all the cases in which this can happen. If
// the only one is with a bad scheme, perhaps ERR_INVALID_ARGUMENT
// would be more appropriate.
rejectAndCleanup(-2, 'ERR_FAILED', url)
}
rejectAndCleanup(-2, 'ERR_FAILED', url);
};
const removeListeners = () => {
this.webContents.removeListener('did-finish-load', finishListener)
this.webContents.removeListener('did-fail-load', failListener)
this.webContents.removeListener('did-start-navigation', navigationListener)
this.webContents.removeListener('did-stop-loading', stopLoadingListener)
}
this.webContents.on('did-finish-load', finishListener)
this.webContents.on('did-fail-load', failListener)
this.webContents.on('did-start-navigation', navigationListener)
this.webContents.on('did-stop-loading', stopLoadingListener)
})
this.webContents.removeListener('did-finish-load', finishListener);
this.webContents.removeListener('did-fail-load', failListener);
this.webContents.removeListener('did-start-navigation', navigationListener);
this.webContents.removeListener('did-stop-loading', stopLoadingListener);
};
this.webContents.on('did-finish-load', finishListener);
this.webContents.on('did-fail-load', failListener);
this.webContents.on('did-start-navigation', navigationListener);
this.webContents.on('did-stop-loading', stopLoadingListener);
});
// Add a no-op rejection handler to silence the unhandled rejection error.
p.catch(() => {})
this.pendingIndex = -1
this.webContents._loadURL(url, options)
this.webContents.emit('load-url', url, options)
return p
}
p.catch(() => {});
this.pendingIndex = -1;
this.webContents._loadURL(url, options);
this.webContents.emit('load-url', url, options);
return p;
};
NavigationController.prototype.getURL = function () {
if (this.currentIndex === -1) {
return ''
return '';
} else {
return this.history[this.currentIndex]
return this.history[this.currentIndex];
}
}
};
NavigationController.prototype.stop = function () {
this.pendingIndex = -1
return this.webContents._stop()
}
this.pendingIndex = -1;
return this.webContents._stop();
};
NavigationController.prototype.reload = function () {
this.pendingIndex = this.currentIndex
return this.webContents._loadURL(this.getURL(), {})
}
this.pendingIndex = this.currentIndex;
return this.webContents._loadURL(this.getURL(), {});
};
NavigationController.prototype.reloadIgnoringCache = function () {
this.pendingIndex = this.currentIndex
this.pendingIndex = this.currentIndex;
return this.webContents._loadURL(this.getURL(), {
extraHeaders: 'pragma: no-cache\n',
reloadIgnoringCache: true
})
}
});
};
NavigationController.prototype.canGoBack = function () {
return this.getActiveIndex() > 0
}
return this.getActiveIndex() > 0;
};
NavigationController.prototype.canGoForward = function () {
return this.getActiveIndex() < this.history.length - 1
}
return this.getActiveIndex() < this.history.length - 1;
};
NavigationController.prototype.canGoToIndex = function (index) {
return index >= 0 && index < this.history.length
}
return index >= 0 && index < this.history.length;
};
NavigationController.prototype.canGoToOffset = function (offset) {
return this.canGoToIndex(this.currentIndex + offset)
}
return this.canGoToIndex(this.currentIndex + offset);
};
NavigationController.prototype.clearHistory = function () {
this.history = []
this.currentIndex = -1
this.pendingIndex = -1
this.inPageIndex = -1
}
this.history = [];
this.currentIndex = -1;
this.pendingIndex = -1;
this.inPageIndex = -1;
};
NavigationController.prototype.goBack = function () {
if (!this.canGoBack()) {
return
return;
}
this.pendingIndex = this.getActiveIndex() - 1
this.pendingIndex = this.getActiveIndex() - 1;
if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
return this.webContents._goBack()
return this.webContents._goBack();
} else {
return this.webContents._loadURL(this.history[this.pendingIndex], {})
return this.webContents._loadURL(this.history[this.pendingIndex], {});
}
}
};
NavigationController.prototype.goForward = function () {
if (!this.canGoForward()) {
return
return;
}
this.pendingIndex = this.getActiveIndex() + 1
this.pendingIndex = this.getActiveIndex() + 1;
if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
return this.webContents._goForward()
return this.webContents._goForward();
} else {
return this.webContents._loadURL(this.history[this.pendingIndex], {})
return this.webContents._loadURL(this.history[this.pendingIndex], {});
}
}
};
NavigationController.prototype.goToIndex = function (index) {
if (!this.canGoToIndex(index)) {
return
return;
}
this.pendingIndex = index
return this.webContents._loadURL(this.history[this.pendingIndex], {})
}
this.pendingIndex = index;
return this.webContents._loadURL(this.history[this.pendingIndex], {});
};
NavigationController.prototype.goToOffset = function (offset) {
if (!this.canGoToOffset(offset)) {
return
return;
}
const pendingIndex = this.currentIndex + offset
const pendingIndex = this.currentIndex + offset;
if (this.inPageIndex > -1 && pendingIndex >= this.inPageIndex) {
this.pendingIndex = pendingIndex
return this.webContents._goToOffset(offset)
this.pendingIndex = pendingIndex;
return this.webContents._goToOffset(offset);
} else {
return this.goToIndex(pendingIndex)
return this.goToIndex(pendingIndex);
}
}
};
NavigationController.prototype.getActiveIndex = function () {
if (this.pendingIndex === -1) {
return this.currentIndex
return this.currentIndex;
} else {
return this.pendingIndex
return this.pendingIndex;
}
}
};
NavigationController.prototype.length = function () {
return this.history.length
}
return this.history.length;
};
return NavigationController
})()
return NavigationController;
})();
module.exports = NavigationController
module.exports = NavigationController;

View File

@@ -1,51 +1,51 @@
'use strict'
'use strict';
const v8Util = process.electronBinding('v8_util')
const v8Util = process.electronBinding('v8_util');
const getOwnerKey = (webContents, contextId) => {
return `${webContents.id}-${contextId}`
}
return `${webContents.id}-${contextId}`;
};
class ObjectsRegistry {
constructor () {
this.nextId = 0
this.nextId = 0;
// Stores all objects by ref-counting.
// (id) => {object, count}
this.storage = {}
this.storage = {};
// Stores the IDs + refCounts of objects referenced by WebContents.
// (ownerKey) => { id: refCount }
this.owners = {}
this.owners = {};
}
// Register a new object and return its assigned ID. If the object is already
// registered then the already assigned ID would be returned.
add (webContents, contextId, obj) {
// Get or assign an ID to the object.
const id = this.saveToStorage(obj)
const id = this.saveToStorage(obj);
// Add object to the set of referenced objects.
const ownerKey = getOwnerKey(webContents, contextId)
let owner = this.owners[ownerKey]
const ownerKey = getOwnerKey(webContents, contextId);
let owner = this.owners[ownerKey];
if (!owner) {
owner = this.owners[ownerKey] = new Map()
this.registerDeleteListener(webContents, contextId)
owner = this.owners[ownerKey] = new Map();
this.registerDeleteListener(webContents, contextId);
}
if (!owner.has(id)) {
owner.set(id, 0)
owner.set(id, 0);
// Increase reference count if not referenced before.
this.storage[id].count++
this.storage[id].count++;
}
owner.set(id, owner.get(id) + 1)
return id
owner.set(id, owner.get(id) + 1);
return id;
}
// Get an object according to its ID.
get (id) {
const pointer = this.storage[id]
if (pointer != null) return pointer.object
const pointer = this.storage[id];
if (pointer != null) return pointer.object;
}
// Dereference an object according to its ID.
@@ -60,79 +60,79 @@ class ObjectsRegistry {
// For more details on why we do renderer side ref counting see
// https://github.com/electron/electron/pull/17464
remove (webContents, contextId, id, rendererSideRefCount) {
const ownerKey = getOwnerKey(webContents, contextId)
const owner = this.owners[ownerKey]
const ownerKey = getOwnerKey(webContents, contextId);
const owner = this.owners[ownerKey];
if (owner && owner.has(id)) {
const newRefCount = owner.get(id) - rendererSideRefCount
const newRefCount = owner.get(id) - rendererSideRefCount;
// Only completely remove if the number of references GCed in the
// renderer is the same as the number of references we sent them
if (newRefCount <= 0) {
// Remove the reference in owner.
owner.delete(id)
owner.delete(id);
// Dereference from the storage.
this.dereference(id)
this.dereference(id);
} else {
owner.set(id, newRefCount)
owner.set(id, newRefCount);
}
}
}
// Clear all references to objects refrenced by the WebContents.
clear (webContents, contextId) {
const ownerKey = getOwnerKey(webContents, contextId)
const owner = this.owners[ownerKey]
if (!owner) return
const ownerKey = getOwnerKey(webContents, contextId);
const owner = this.owners[ownerKey];
if (!owner) return;
for (const id of owner.keys()) this.dereference(id)
for (const id of owner.keys()) this.dereference(id);
delete this.owners[ownerKey]
delete this.owners[ownerKey];
}
// Private: Saves the object into storage and assigns an ID for it.
saveToStorage (object) {
let id = v8Util.getHiddenValue(object, 'atomId')
let id = v8Util.getHiddenValue(object, 'atomId');
if (!id) {
id = ++this.nextId
id = ++this.nextId;
this.storage[id] = {
count: 0,
object: object
}
v8Util.setHiddenValue(object, 'atomId', id)
};
v8Util.setHiddenValue(object, 'atomId', id);
}
return id
return id;
}
// Private: Dereference the object from store.
dereference (id) {
const pointer = this.storage[id]
const pointer = this.storage[id];
if (pointer == null) {
return
return;
}
pointer.count -= 1
pointer.count -= 1;
if (pointer.count === 0) {
v8Util.deleteHiddenValue(pointer.object, 'atomId')
delete this.storage[id]
v8Util.deleteHiddenValue(pointer.object, 'atomId');
delete this.storage[id];
}
}
// Private: Clear the storage when renderer process is destroyed.
registerDeleteListener (webContents, contextId) {
// contextId => ${processHostId}-${contextCount}
const processHostId = contextId.split('-')[0]
const processHostId = contextId.split('-')[0];
const listener = (event, deletedProcessHostId) => {
if (deletedProcessHostId &&
deletedProcessHostId.toString() === processHostId) {
webContents.removeListener('render-view-deleted', listener)
this.clear(webContents, contextId)
webContents.removeListener('render-view-deleted', listener);
this.clear(webContents, contextId);
}
}
};
// Note that the "render-view-deleted" event may not be emitted on time when
// the renderer process get destroyed because of navigation, we rely on the
// renderer process to send "ELECTRON_BROWSER_CONTEXT_RELEASE" message to
// guard this situation.
webContents.on('render-view-deleted', listener)
webContents.on('render-view-deleted', listener);
}
}
module.exports = new ObjectsRegistry()
module.exports = new ObjectsRegistry();

View File

@@ -1,135 +1,135 @@
'use strict'
'use strict';
const electron = require('electron')
const { EventEmitter } = require('events')
const fs = require('fs')
const electron = require('electron');
const { EventEmitter } = require('events');
const fs = require('fs');
const v8Util = process.electronBinding('v8_util')
const eventBinding = process.electronBinding('event')
const clipboard = process.electronBinding('clipboard')
const features = process.electronBinding('features')
const v8Util = process.electronBinding('v8_util');
const eventBinding = process.electronBinding('event');
const clipboard = process.electronBinding('clipboard');
const features = process.electronBinding('features');
const { getContentScripts } = require('@electron/internal/browser/chrome-extension')
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init')
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal')
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
const objectsRegistry = require('@electron/internal/browser/objects-registry')
const guestViewManager = require('@electron/internal/browser/guest-view-manager')
const bufferUtils = require('@electron/internal/common/buffer-utils')
const errorUtils = require('@electron/internal/common/error-utils')
const typeUtils = require('@electron/internal/common/type-utils')
const { isPromise } = require('@electron/internal/common/is-promise')
const { getContentScripts } = require('@electron/internal/browser/chrome-extension');
const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init');
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
const objectsRegistry = require('@electron/internal/browser/objects-registry');
const guestViewManager = require('@electron/internal/browser/guest-view-manager');
const bufferUtils = require('@electron/internal/common/buffer-utils');
const errorUtils = require('@electron/internal/common/error-utils');
const typeUtils = require('@electron/internal/common/type-utils');
const { isPromise } = require('@electron/internal/common/is-promise');
const hasProp = {}.hasOwnProperty
const hasProp = {}.hasOwnProperty;
// The internal properties of Function.
const FUNCTION_PROPERTIES = [
'length', 'name', 'arguments', 'caller', 'prototype'
]
];
// The remote functions in renderer processes.
// id => Function
const rendererFunctions = v8Util.createDoubleIDWeakMap()
const rendererFunctions = v8Util.createDoubleIDWeakMap();
// Return the description of object's members:
const getObjectMembers = function (object) {
let names = Object.getOwnPropertyNames(object)
let names = Object.getOwnPropertyNames(object);
// For Function, we should not override following properties even though they
// are "own" properties.
if (typeof object === 'function') {
names = names.filter((name) => {
return !FUNCTION_PROPERTIES.includes(name)
})
return !FUNCTION_PROPERTIES.includes(name);
});
}
// Map properties to descriptors.
return names.map((name) => {
const descriptor = Object.getOwnPropertyDescriptor(object, name)
const member = { name, enumerable: descriptor.enumerable, writable: false }
const descriptor = Object.getOwnPropertyDescriptor(object, name);
const member = { name, enumerable: descriptor.enumerable, writable: false };
if (descriptor.get === undefined && typeof object[name] === 'function') {
member.type = 'method'
member.type = 'method';
} else {
if (descriptor.set || descriptor.writable) member.writable = true
member.type = 'get'
if (descriptor.set || descriptor.writable) member.writable = true;
member.type = 'get';
}
return member
})
}
return member;
});
};
// Return the description of object's prototype.
const getObjectPrototype = function (object) {
const proto = Object.getPrototypeOf(object)
if (proto === null || proto === Object.prototype) return null
const proto = Object.getPrototypeOf(object);
if (proto === null || proto === Object.prototype) return null;
return {
members: getObjectMembers(proto),
proto: getObjectPrototype(proto)
}
}
};
};
// Convert a real value into meta data.
const valueToMeta = function (sender, contextId, value, optimizeSimpleObject = false) {
// Determine the type of value.
const meta = { type: typeof value }
const meta = { type: typeof value };
if (meta.type === 'object') {
// Recognize certain types of objects.
if (value === null) {
meta.type = 'value'
meta.type = 'value';
} else if (bufferUtils.isBuffer(value)) {
meta.type = 'buffer'
meta.type = 'buffer';
} else if (Array.isArray(value)) {
meta.type = 'array'
meta.type = 'array';
} else if (value instanceof Error) {
meta.type = 'error'
meta.type = 'error';
} else if (value instanceof Date) {
meta.type = 'date'
meta.type = 'date';
} else if (isPromise(value)) {
meta.type = 'promise'
meta.type = 'promise';
} else if (hasProp.call(value, 'callee') && value.length != null) {
// Treat the arguments object as array.
meta.type = 'array'
meta.type = 'array';
} else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) {
// Treat simple objects as value.
meta.type = 'value'
meta.type = 'value';
}
}
// Fill the meta object according to value's type.
if (meta.type === 'array') {
meta.members = value.map((el) => valueToMeta(sender, contextId, el, optimizeSimpleObject))
meta.members = value.map((el) => valueToMeta(sender, contextId, el, optimizeSimpleObject));
} else if (meta.type === 'object' || meta.type === 'function') {
meta.name = value.constructor ? value.constructor.name : ''
meta.name = value.constructor ? value.constructor.name : '';
// Reference the original value if it's an object, because when it's
// passed to renderer we would assume the renderer keeps a reference of
// it.
meta.id = objectsRegistry.add(sender, contextId, value)
meta.members = getObjectMembers(value)
meta.proto = getObjectPrototype(value)
meta.id = objectsRegistry.add(sender, contextId, value);
meta.members = getObjectMembers(value);
meta.proto = getObjectPrototype(value);
} else if (meta.type === 'buffer') {
meta.value = bufferUtils.bufferToMeta(value)
meta.value = bufferUtils.bufferToMeta(value);
} else if (meta.type === 'promise') {
// Add default handler to prevent unhandled rejections in main process
// Instead they should appear in the renderer process
value.then(function () {}, function () {})
value.then(function () {}, function () {});
meta.then = valueToMeta(sender, contextId, function (onFulfilled, onRejected) {
value.then(onFulfilled, onRejected)
})
value.then(onFulfilled, onRejected);
});
} else if (meta.type === 'error') {
meta.members = plainObjectToMeta(value)
meta.members = plainObjectToMeta(value);
// Error.name is not part of own properties.
meta.members.push({
name: 'name',
value: value.name
})
});
} else if (meta.type === 'date') {
meta.value = value.getTime()
meta.value = value.getTime();
} else {
meta.type = 'value'
meta.value = value
meta.type = 'value';
meta.value = value;
}
return meta
}
return meta;
};
// Convert object to meta by value.
const plainObjectToMeta = function (obj) {
@@ -137,399 +137,399 @@ const plainObjectToMeta = function (obj) {
return {
name: name,
value: obj[name]
}
})
}
};
});
};
// Convert Error into meta data.
const exceptionToMeta = function (error) {
return {
type: 'exception',
value: errorUtils.serialize(error)
}
}
};
};
const throwRPCError = function (message) {
const error = new Error(message)
error.code = 'EBADRPC'
error.errno = -72
throw error
}
const error = new Error(message);
error.code = 'EBADRPC';
error.errno = -72;
throw error;
};
const removeRemoteListenersAndLogWarning = (sender, callIntoRenderer) => {
const location = v8Util.getHiddenValue(callIntoRenderer, 'location')
const location = v8Util.getHiddenValue(callIntoRenderer, 'location');
let message = `Attempting to call a function in a renderer window that has been closed or released.` +
`\nFunction provided here: ${location}`
`\nFunction provided here: ${location}`;
if (sender instanceof EventEmitter) {
const remoteEvents = sender.eventNames().filter((eventName) => {
return sender.listeners(eventName).includes(callIntoRenderer)
})
return sender.listeners(eventName).includes(callIntoRenderer);
});
if (remoteEvents.length > 0) {
message += `\nRemote event names: ${remoteEvents.join(', ')}`
message += `\nRemote event names: ${remoteEvents.join(', ')}`;
remoteEvents.forEach((eventName) => {
sender.removeListener(eventName, callIntoRenderer)
})
sender.removeListener(eventName, callIntoRenderer);
});
}
}
console.warn(message)
}
console.warn(message);
};
// Convert array of meta data from renderer into array of real values.
const unwrapArgs = function (sender, frameId, contextId, args) {
const metaToValue = function (meta) {
switch (meta.type) {
case 'value':
return meta.value
return meta.value;
case 'remote-object':
return objectsRegistry.get(meta.id)
return objectsRegistry.get(meta.id);
case 'array':
return unwrapArgs(sender, frameId, contextId, meta.value)
return unwrapArgs(sender, frameId, contextId, meta.value);
case 'buffer':
return bufferUtils.metaToBuffer(meta.value)
return bufferUtils.metaToBuffer(meta.value);
case 'date':
return new Date(meta.value)
return new Date(meta.value);
case 'promise':
return Promise.resolve({
then: metaToValue(meta.then)
})
});
case 'object': {
const ret = {}
Object.defineProperty(ret.constructor, 'name', { value: meta.name })
const ret = {};
Object.defineProperty(ret.constructor, 'name', { value: meta.name });
for (const { name, value } of meta.members) {
ret[name] = metaToValue(value)
ret[name] = metaToValue(value);
}
return ret
return ret;
}
case 'function-with-return-value':
const returnValue = metaToValue(meta.value)
const returnValue = metaToValue(meta.value);
return function () {
return returnValue
}
return returnValue;
};
case 'function': {
// Merge contextId and meta.id, since meta.id can be the same in
// different webContents.
const objectId = [contextId, meta.id]
const objectId = [contextId, meta.id];
// Cache the callbacks in renderer.
if (rendererFunctions.has(objectId)) {
return rendererFunctions.get(objectId)
return rendererFunctions.get(objectId);
}
const callIntoRenderer = function (...args) {
let succeed = false
let succeed = false;
if (!sender.isDestroyed()) {
succeed = sender._sendToFrameInternal(frameId, 'ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args))
succeed = sender._sendToFrameInternal(frameId, 'ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args));
}
if (!succeed) {
removeRemoteListenersAndLogWarning(this, callIntoRenderer)
removeRemoteListenersAndLogWarning(this, callIntoRenderer);
}
}
v8Util.setHiddenValue(callIntoRenderer, 'location', meta.location)
Object.defineProperty(callIntoRenderer, 'length', { value: meta.length })
};
v8Util.setHiddenValue(callIntoRenderer, 'location', meta.location);
Object.defineProperty(callIntoRenderer, 'length', { value: meta.length });
v8Util.setRemoteCallbackFreer(callIntoRenderer, frameId, contextId, meta.id, sender)
rendererFunctions.set(objectId, callIntoRenderer)
return callIntoRenderer
v8Util.setRemoteCallbackFreer(callIntoRenderer, frameId, contextId, meta.id, sender);
rendererFunctions.set(objectId, callIntoRenderer);
return callIntoRenderer;
}
default:
throw new TypeError(`Unknown type: ${meta.type}`)
throw new TypeError(`Unknown type: ${meta.type}`);
}
}
return args.map(metaToValue)
}
};
return args.map(metaToValue);
};
const isRemoteModuleEnabledCache = new WeakMap()
const isRemoteModuleEnabledCache = new WeakMap();
const isRemoteModuleEnabled = function (contents) {
if (!isRemoteModuleEnabledCache.has(contents)) {
isRemoteModuleEnabledCache.set(contents, contents._isRemoteModuleEnabled())
isRemoteModuleEnabledCache.set(contents, contents._isRemoteModuleEnabled());
}
return isRemoteModuleEnabledCache.get(contents)
}
return isRemoteModuleEnabledCache.get(contents);
};
const handleRemoteCommand = function (channel, handler) {
ipcMainInternal.on(channel, (event, contextId, ...args) => {
let returnValue
let returnValue;
if (!isRemoteModuleEnabled(event.sender)) {
event.returnValue = null
return
event.returnValue = null;
return;
}
try {
returnValue = handler(event, contextId, ...args)
returnValue = handler(event, contextId, ...args);
} catch (error) {
returnValue = exceptionToMeta(error)
returnValue = exceptionToMeta(error);
}
if (returnValue !== undefined) {
event.returnValue = returnValue
event.returnValue = returnValue;
}
})
}
});
};
const emitCustomEvent = function (contents, eventName, ...args) {
const event = eventBinding.createWithSender(contents)
const event = eventBinding.createWithSender(contents);
electron.app.emit(eventName, event, contents, ...args)
contents.emit(eventName, event, ...args)
electron.app.emit(eventName, event, contents, ...args);
contents.emit(eventName, event, ...args);
return event
}
return event;
};
handleRemoteCommand('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', function (event, contextId, passedContextId, id) {
const objectId = [passedContextId, id]
const objectId = [passedContextId, id];
if (!rendererFunctions.has(objectId)) {
// Do nothing if the error has already been reported before.
return
return;
}
removeRemoteListenersAndLogWarning(event.sender, rendererFunctions.get(objectId))
})
removeRemoteListenersAndLogWarning(event.sender, rendererFunctions.get(objectId));
});
handleRemoteCommand('ELECTRON_BROWSER_REQUIRE', function (event, contextId, moduleName) {
const customEvent = emitCustomEvent(event.sender, 'remote-require', moduleName)
const customEvent = emitCustomEvent(event.sender, 'remote-require', moduleName);
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.require('${moduleName}')`)
throw new Error(`Blocked remote.require('${moduleName}')`);
} else {
customEvent.returnValue = process.mainModule.require(moduleName)
customEvent.returnValue = process.mainModule.require(moduleName);
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
handleRemoteCommand('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, moduleName) {
const customEvent = emitCustomEvent(event.sender, 'remote-get-builtin', moduleName)
const customEvent = emitCustomEvent(event.sender, 'remote-get-builtin', moduleName);
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.getBuiltin('${moduleName}')`)
throw new Error(`Blocked remote.getBuiltin('${moduleName}')`);
} else {
customEvent.returnValue = electron[moduleName]
customEvent.returnValue = electron[moduleName];
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
handleRemoteCommand('ELECTRON_BROWSER_GLOBAL', function (event, contextId, globalName) {
const customEvent = emitCustomEvent(event.sender, 'remote-get-global', globalName)
const customEvent = emitCustomEvent(event.sender, 'remote-get-global', globalName);
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.getGlobal('${globalName}')`)
throw new Error(`Blocked remote.getGlobal('${globalName}')`);
} else {
customEvent.returnValue = global[globalName]
customEvent.returnValue = global[globalName];
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WINDOW', function (event, contextId) {
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-window')
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-window');
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error('Blocked remote.getCurrentWindow()')
throw new Error('Blocked remote.getCurrentWindow()');
} else {
customEvent.returnValue = event.sender.getOwnerBrowserWindow()
customEvent.returnValue = event.sender.getOwnerBrowserWindow();
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event, contextId) {
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-web-contents')
const customEvent = emitCustomEvent(event.sender, 'remote-get-current-web-contents');
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error('Blocked remote.getCurrentWebContents()')
throw new Error('Blocked remote.getCurrentWebContents()');
} else {
customEvent.returnValue = event.sender
customEvent.returnValue = event.sender;
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
handleRemoteCommand('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, id, args) {
args = unwrapArgs(event.sender, event.frameId, contextId, args)
const constructor = objectsRegistry.get(id)
args = unwrapArgs(event.sender, event.frameId, contextId, args);
const constructor = objectsRegistry.get(id);
if (constructor == null) {
throwRPCError(`Cannot call constructor on missing remote object ${id}`)
throwRPCError(`Cannot call constructor on missing remote object ${id}`);
}
return valueToMeta(event.sender, contextId, new constructor(...args))
})
return valueToMeta(event.sender, contextId, new constructor(...args));
});
handleRemoteCommand('ELECTRON_BROWSER_FUNCTION_CALL', function (event, contextId, id, args) {
args = unwrapArgs(event.sender, event.frameId, contextId, args)
const func = objectsRegistry.get(id)
args = unwrapArgs(event.sender, event.frameId, contextId, args);
const func = objectsRegistry.get(id);
if (func == null) {
throwRPCError(`Cannot call function on missing remote object ${id}`)
throwRPCError(`Cannot call function on missing remote object ${id}`);
}
try {
return valueToMeta(event.sender, contextId, func(...args), true)
return valueToMeta(event.sender, contextId, func(...args), true);
} catch (error) {
const err = new Error(`Could not call remote function '${func.name || 'anonymous'}'. Check that the function signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`)
err.cause = error
throw err
const err = new Error(`Could not call remote function '${func.name || 'anonymous'}'. Check that the function signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`);
err.cause = error;
throw err;
}
})
});
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, contextId, id, method, args) {
args = unwrapArgs(event.sender, event.frameId, contextId, args)
const object = objectsRegistry.get(id)
args = unwrapArgs(event.sender, event.frameId, contextId, args);
const object = objectsRegistry.get(id);
if (object == null) {
throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`)
throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`);
}
return valueToMeta(event.sender, contextId, new object[method](...args))
})
return valueToMeta(event.sender, contextId, new object[method](...args));
});
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CALL', function (event, contextId, id, method, args) {
args = unwrapArgs(event.sender, event.frameId, contextId, args)
const object = objectsRegistry.get(id)
args = unwrapArgs(event.sender, event.frameId, contextId, args);
const object = objectsRegistry.get(id);
if (object == null) {
throwRPCError(`Cannot call method '${method}' on missing remote object ${id}`)
throwRPCError(`Cannot call method '${method}' on missing remote object ${id}`);
}
try {
return valueToMeta(event.sender, contextId, object[method](...args), true)
return valueToMeta(event.sender, contextId, object[method](...args), true);
} catch (error) {
const err = new Error(`Could not call remote method '${method}'. Check that the method signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`)
err.cause = error
throw err
const err = new Error(`Could not call remote method '${method}'. Check that the method signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`);
err.cause = error;
throw err;
}
})
});
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_SET', function (event, contextId, id, name, args) {
args = unwrapArgs(event.sender, event.frameId, contextId, args)
const obj = objectsRegistry.get(id)
args = unwrapArgs(event.sender, event.frameId, contextId, args);
const obj = objectsRegistry.get(id);
if (obj == null) {
throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`)
throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`);
}
obj[name] = args[0]
return null
})
obj[name] = args[0];
return null;
});
handleRemoteCommand('ELECTRON_BROWSER_MEMBER_GET', function (event, contextId, id, name) {
const obj = objectsRegistry.get(id)
const obj = objectsRegistry.get(id);
if (obj == null) {
throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`)
throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`);
}
return valueToMeta(event.sender, contextId, obj[name])
})
return valueToMeta(event.sender, contextId, obj[name]);
});
handleRemoteCommand('ELECTRON_BROWSER_DEREFERENCE', function (event, contextId, id, rendererSideRefCount) {
objectsRegistry.remove(event.sender, contextId, id, rendererSideRefCount)
})
objectsRegistry.remove(event.sender, contextId, id, rendererSideRefCount);
});
handleRemoteCommand('ELECTRON_BROWSER_CONTEXT_RELEASE', (event, contextId) => {
objectsRegistry.clear(event.sender, contextId)
})
objectsRegistry.clear(event.sender, contextId);
});
handleRemoteCommand('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, contextId, guestInstanceId) {
const guest = guestViewManager.getGuestForWebContents(guestInstanceId, event.sender)
const guest = guestViewManager.getGuestForWebContents(guestInstanceId, event.sender);
const customEvent = emitCustomEvent(event.sender, 'remote-get-guest-web-contents', guest)
const customEvent = emitCustomEvent(event.sender, 'remote-get-guest-web-contents', guest);
if (customEvent.returnValue === undefined) {
if (customEvent.defaultPrevented) {
throw new Error(`Blocked remote.getGuestForWebContents()`)
throw new Error(`Blocked remote.getGuestForWebContents()`);
} else {
customEvent.returnValue = guest
customEvent.returnValue = guest;
}
}
return valueToMeta(event.sender, contextId, customEvent.returnValue)
})
return valueToMeta(event.sender, contextId, customEvent.returnValue);
});
// Implements window.close()
ipcMainInternal.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
const window = event.sender.getOwnerBrowserWindow()
const window = event.sender.getOwnerBrowserWindow();
if (window) {
window.close()
window.close();
}
event.returnValue = null
})
event.returnValue = null;
});
ipcMainUtils.handle('ELECTRON_CRASH_REPORTER_INIT', function (event, options) {
return crashReporterInit(options)
})
return crashReporterInit(options);
});
ipcMainUtils.handle('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event) {
return event.sender.getLastWebPreferences()
})
return event.sender.getLastWebPreferences();
});
// Methods not listed in this set are called directly in the renderer process.
const allowedClipboardMethods = (() => {
switch (process.platform) {
case 'darwin':
return new Set(['readFindText', 'writeFindText'])
return new Set(['readFindText', 'writeFindText']);
case 'linux':
return new Set(Object.keys(clipboard))
return new Set(Object.keys(clipboard));
default:
return new Set()
return new Set();
}
})()
})();
ipcMainUtils.handle('ELECTRON_BROWSER_CLIPBOARD', function (event, method, ...args) {
if (!allowedClipboardMethods.has(method)) {
throw new Error(`Invalid method: ${method}`)
throw new Error(`Invalid method: ${method}`);
}
return typeUtils.serialize(electron.clipboard[method](...typeUtils.deserialize(args)))
})
return typeUtils.serialize(electron.clipboard[method](...typeUtils.deserialize(args)));
});
if (features.isDesktopCapturerEnabled()) {
const desktopCapturer = require('@electron/internal/browser/desktop-capturer')
const desktopCapturer = require('@electron/internal/browser/desktop-capturer');
ipcMainUtils.handle('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', function (event, ...args) {
const customEvent = emitCustomEvent(event.sender, 'desktop-capturer-get-sources')
const customEvent = emitCustomEvent(event.sender, 'desktop-capturer-get-sources');
if (customEvent.defaultPrevented) {
console.error('Blocked desktopCapturer.getSources()')
return []
console.error('Blocked desktopCapturer.getSources()');
return [];
}
return desktopCapturer.getSources(event, ...args)
})
return desktopCapturer.getSources(event, ...args);
});
}
const getPreloadScript = async function (preloadPath) {
let preloadSrc = null
let preloadError = null
let preloadSrc = null;
let preloadError = null;
try {
preloadSrc = (await fs.promises.readFile(preloadPath)).toString()
preloadSrc = (await fs.promises.readFile(preloadPath)).toString();
} catch (err) {
preloadError = errorUtils.serialize(err)
preloadError = errorUtils.serialize(err);
}
return { preloadPath, preloadSrc, preloadError }
}
return { preloadPath, preloadSrc, preloadError };
};
ipcMainUtils.handle('ELECTRON_GET_CONTENT_SCRIPTS', () => getContentScripts())
ipcMainUtils.handle('ELECTRON_GET_CONTENT_SCRIPTS', () => getContentScripts());
ipcMainUtils.handle('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event) {
const preloadPaths = event.sender._getPreloadPaths()
const webPreferences = event.sender.getLastWebPreferences() || {}
const preloadPaths = event.sender._getPreloadPaths();
const webPreferences = event.sender.getLastWebPreferences() || {};
return {
contentScripts: getContentScripts(),
@@ -546,9 +546,9 @@ ipcMainUtils.handle('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event) {
versions: process.versions,
execPath: process.helperExecPath
}
}
})
};
});
ipcMainInternal.on('ELECTRON_BROWSER_PRELOAD_ERROR', function (event, preloadPath, error) {
event.sender.emit('preload-error', event, preloadPath, errorUtils.deserialize(error))
})
event.sender.emit('preload-error', event, preloadPath, errorUtils.deserialize(error));
});

View File

@@ -1,4 +1,4 @@
import { EventEmitter } from 'events'
import { EventEmitter } from 'events';
/**
* Creates a lazy instance of modules that can't be required before the
@@ -16,23 +16,23 @@ export function createLazyInstance (
holder: Object,
isEventEmitter: Boolean
) {
let lazyModule: Object
const module: any = {}
for (const method in (holder as any).prototype) {
let lazyModule: Object;
const module: any = {};
for (const method in (holder as any).prototype) { // eslint-disable-line guard-for-in
module[method] = (...args: any) => {
// create new instance of module at runtime if none exists
if (!lazyModule) {
lazyModule = creator()
if (isEventEmitter) EventEmitter.call(lazyModule as any)
lazyModule = creator();
if (isEventEmitter) EventEmitter.call(lazyModule as any);
}
// check for properties on the prototype chain that aren't functions
if (typeof (lazyModule as any)[method] !== 'function') {
return (lazyModule as any)[method]
return (lazyModule as any)[method];
}
return (lazyModule as any)[method](...args)
}
return (lazyModule as any)[method](...args);
};
}
return module
return module;
}

View File

@@ -1,29 +1,29 @@
'use strict'
'use strict';
const clipboard = process.electronBinding('clipboard')
const clipboard = process.electronBinding('clipboard');
if (process.type === 'renderer') {
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils')
const typeUtils = require('@electron/internal/common/type-utils')
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils');
const typeUtils = require('@electron/internal/common/type-utils');
const makeRemoteMethod = function (method) {
return (...args) => {
args = typeUtils.serialize(args)
const result = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_CLIPBOARD', method, ...args)
return typeUtils.deserialize(result)
}
}
args = typeUtils.serialize(args);
const result = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_CLIPBOARD', method, ...args);
return typeUtils.deserialize(result);
};
};
if (process.platform === 'linux') {
// On Linux we could not access clipboard in renderer process.
for (const method of Object.keys(clipboard)) {
clipboard[method] = makeRemoteMethod(method)
clipboard[method] = makeRemoteMethod(method);
}
} else if (process.platform === 'darwin') {
// Read/write to find pasteboard over IPC since only main process is notified of changes
clipboard.readFindText = makeRemoteMethod('readFindText')
clipboard.writeFindText = makeRemoteMethod('writeFindText')
clipboard.readFindText = makeRemoteMethod('readFindText');
clipboard.writeFindText = makeRemoteMethod('writeFindText');
}
}
module.exports = clipboard
module.exports = clipboard;

View File

@@ -1,95 +1,95 @@
let deprecationHandler: ElectronInternal.DeprecationHandler | null = null
let deprecationHandler: ElectronInternal.DeprecationHandler | null = null;
function warnOnce (oldName: string, newName?: string) {
let warned = false
let warned = false;
const msg = newName
? `'${oldName}' is deprecated and will be removed. Please use '${newName}' instead.`
: `'${oldName}' is deprecated and will be removed.`
: `'${oldName}' is deprecated and will be removed.`;
return () => {
if (!warned && !process.noDeprecation) {
warned = true
deprecate.log(msg)
warned = true;
deprecate.log(msg);
}
}
};
}
const deprecate: ElectronInternal.DeprecationUtil = {
warnOnce,
setHandler: (handler) => { deprecationHandler = handler },
setHandler: (handler) => { deprecationHandler = handler; },
getHandler: () => deprecationHandler,
warn: (oldName, newName) => {
if (!process.noDeprecation) {
deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`)
deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`);
}
},
log: (message) => {
if (typeof deprecationHandler === 'function') {
deprecationHandler(message)
deprecationHandler(message);
} else if (process.throwDeprecation) {
throw new Error(message)
throw new Error(message);
} else if (process.traceDeprecation) {
return console.trace(message)
return console.trace(message);
} else {
return console.warn(`(electron) ${message}`)
return console.warn(`(electron) ${message}`);
}
},
// remove a function with no replacement
removeFunction: (fn, removedName) => {
if (!fn) { throw Error(`'${removedName} function' is invalid or does not exist.`) }
if (!fn) { throw Error(`'${removedName} function' is invalid or does not exist.`); }
// wrap the deprecated function to warn user
const warn = warnOnce(`${fn.name} function`)
const warn = warnOnce(`${fn.name} function`);
return function (this: any) {
warn()
fn.apply(this, arguments)
}
warn();
fn.apply(this, arguments);
};
},
// change the name of a function
renameFunction: (fn, newName) => {
const warn = warnOnce(`${fn.name} function`, `${newName} function`)
const warn = warnOnce(`${fn.name} function`, `${newName} function`);
return function (this: any) {
warn()
return fn.apply(this, arguments)
}
warn();
return fn.apply(this, arguments);
};
},
moveAPI: (fn: Function, oldUsage: string, newUsage: string) => {
const warn = warnOnce(oldUsage, newUsage)
const warn = warnOnce(oldUsage, newUsage);
return function (this: any) {
warn()
return fn.apply(this, arguments)
}
warn();
return fn.apply(this, arguments);
};
},
// change the name of an event
event: (emitter, oldName, newName) => {
const warn = newName.startsWith('-') /* internal event */
? warnOnce(`${oldName} event`)
: warnOnce(`${oldName} event`, `${newName} event`)
: warnOnce(`${oldName} event`, `${newName} event`);
return emitter.on(newName, function (this: NodeJS.EventEmitter, ...args) {
if (this.listenerCount(oldName) !== 0) {
warn()
this.emit(oldName, ...args)
warn();
this.emit(oldName, ...args);
}
})
});
},
// deprecate a getter/setter function pair in favor of a property
fnToProperty: (prototype: any, prop: string, getter: string, setter?: string) => {
const withWarnOnce = function (obj: any, key: any, oldName: string, newName: string) {
const warn = warnOnce(oldName, newName)
const method = obj[key]
const warn = warnOnce(oldName, newName);
const method = obj[key];
return function (this: any, ...args: any) {
warn()
return method.apply(this, args)
}
}
warn();
return method.apply(this, args);
};
};
prototype[getter.substr(1)] = withWarnOnce(prototype, getter, `${getter.substr(1)} function`, `${prop} property`)
prototype[getter.substr(1)] = withWarnOnce(prototype, getter, `${getter.substr(1)} function`, `${prop} property`);
if (setter) {
prototype[setter.substr(1)] = withWarnOnce(prototype, setter, `${setter.substr(1)} function`, `${prop} property`)
prototype[setter.substr(1)] = withWarnOnce(prototype, setter, `${setter.substr(1)} function`, `${prop} property`);
}
},
@@ -97,107 +97,107 @@ const deprecate: ElectronInternal.DeprecationUtil = {
removeProperty: (o, removedName) => {
// if the property's already been removed, warn about it
if (!(removedName in o)) {
deprecate.log(`Unable to remove property '${removedName}' from an object that lacks it.`)
deprecate.log(`Unable to remove property '${removedName}' from an object that lacks it.`);
}
// wrap the deprecated property in an accessor to warn
const warn = warnOnce(removedName)
let val = o[removedName]
const warn = warnOnce(removedName);
let val = o[removedName];
return Object.defineProperty(o, removedName, {
configurable: true,
get: () => {
warn()
return val
warn();
return val;
},
set: newVal => {
warn()
val = newVal
warn();
val = newVal;
}
})
});
},
// deprecate a callback-based function in favor of one returning a Promise
promisify: <T extends (...args: any[]) => any>(fn: T): T => {
const fnName = fn.name || 'function'
const oldName = `${fnName} with callbacks`
const newName = `${fnName} with Promises`
const warn = warnOnce(oldName, newName)
const fnName = fn.name || 'function';
const oldName = `${fnName} with callbacks`;
const newName = `${fnName} with Promises`;
const warn = warnOnce(oldName, newName);
return function (this: any, ...params: any[]) {
let cb: Function | undefined
let cb: Function | undefined;
if (params.length > 0 && typeof params[params.length - 1] === 'function') {
cb = params.pop()
cb = params.pop();
}
const promise = fn.apply(this, params)
if (!cb) return promise
if (process.enablePromiseAPIs) warn()
const promise = fn.apply(this, params);
if (!cb) return promise;
if (process.enablePromiseAPIs) warn();
return promise
.then((res: any) => {
process.nextTick(() => {
cb!.length === 2 ? cb!(null, res) : cb!(res)
})
return res
cb!.length === 2 ? cb!(null, res) : cb!(res);
});
return res;
}, (err: Error) => {
process.nextTick(() => {
cb!.length === 2 ? cb!(err) : cb!()
})
throw err
})
} as T
cb!.length === 2 ? cb!(err) : cb!();
});
throw err;
});
} as T;
},
// convertPromiseValue: Temporarily disabled until it's used
// deprecate a callback-based function in favor of one returning a Promise
promisifyMultiArg: <T extends (...args: any[]) => any>(fn: T /* convertPromiseValue: (v: any) => any */): T => {
const fnName = fn.name || 'function'
const oldName = `${fnName} with callbacks`
const newName = `${fnName} with Promises`
const warn = warnOnce(oldName, newName)
const fnName = fn.name || 'function';
const oldName = `${fnName} with callbacks`;
const newName = `${fnName} with Promises`;
const warn = warnOnce(oldName, newName);
return function (this: any, ...params) {
let cb: Function | undefined
let cb: Function | undefined;
if (params.length > 0 && typeof params[params.length - 1] === 'function') {
cb = params.pop()
cb = params.pop();
}
const promise = fn.apply(this, params)
if (!cb) return promise
if (process.enablePromiseAPIs) warn()
const promise = fn.apply(this, params);
if (!cb) return promise;
if (process.enablePromiseAPIs) warn();
return promise
.then((res: any) => {
process.nextTick(() => {
// eslint-disable-next-line standard/no-callback-literal
cb!.length > 2 ? cb!(null, ...res) : cb!(...res)
})
cb!.length > 2 ? cb!(null, ...res) : cb!(...res);
});
}, (err: Error) => {
process.nextTick(() => cb!(err))
})
} as T
process.nextTick(() => cb!(err));
});
} as T;
},
// change the name of a property
renameProperty: (o, oldName, newName) => {
const warn = warnOnce(oldName, newName)
const warn = warnOnce(oldName, newName);
// if the new property isn't there yet,
// inject it and warn about it
if ((oldName in o) && !(newName in o)) {
warn()
o[newName] = (o as any)[oldName]
warn();
o[newName] = (o as any)[oldName];
}
// wrap the deprecated property in an accessor to warn
// and redirect to the new property
return Object.defineProperty(o, oldName, {
get: () => {
warn()
return o[newName]
warn();
return o[newName];
},
set: value => {
warn()
o[newName] = value
warn();
o[newName] = value;
}
})
});
}
}
};
export default deprecate
export default deprecate;

View File

@@ -1,12 +1,12 @@
'use strict'
'use strict';
const moduleList = require('@electron/internal/common/api/module-list')
const moduleList = require('@electron/internal/common/api/module-list');
exports.handleESModule = (loader) => () => {
const value = loader()
if (value.__esModule && value.default) return value.default
return value
}
const value = loader();
if (value.__esModule && value.default) return value.default;
return value;
};
exports.memoizedGetter = (getter) => {
/*
@@ -14,24 +14,24 @@ exports.memoizedGetter = (getter) => {
* node module cache anyway at `Module._cache`. This memoization
* is dramatically faster than relying on nodes module cache however
*/
let memoizedValue = null
let memoizedValue = null;
return () => {
if (memoizedValue === null) {
memoizedValue = getter()
memoizedValue = getter();
}
return memoizedValue
}
}
return memoizedValue;
};
};
// Attaches properties to |targetExports|.
exports.defineProperties = function (targetExports) {
const descriptors = {}
const descriptors = {};
for (const module of moduleList) {
descriptors[module.name] = {
enumerable: !module.private,
get: exports.handleESModule(module.loader)
}
};
}
return Object.defineProperties(targetExports, descriptors)
}
return Object.defineProperties(targetExports, descriptors);
};

View File

@@ -1,4 +1,4 @@
'use strict'
'use strict';
// Common modules, please sort alphabetically
module.exports = [
@@ -7,4 +7,4 @@ module.exports = [
{ name: 'shell', loader: () => require('./shell') },
// The internal modules, invisible unless you know their names.
{ name: 'deprecate', loader: () => require('./deprecate'), private: true }
]
];

View File

@@ -1,8 +1,8 @@
'use strict'
'use strict';
const { deprecate } = require('electron')
const { NativeImage, nativeImage } = process.electronBinding('native_image')
const { deprecate } = require('electron');
const { NativeImage, nativeImage } = process.electronBinding('native_image');
deprecate.fnToProperty(NativeImage.prototype, 'isMacTemplateImage', '_isTemplateImage', '_setTemplateImage')
deprecate.fnToProperty(NativeImage.prototype, 'isMacTemplateImage', '_isTemplateImage', '_setTemplateImage');
module.exports = nativeImage
module.exports = nativeImage;

View File

@@ -1,3 +1,3 @@
'use strict'
'use strict';
module.exports = process.electronBinding('shell')
module.exports = process.electronBinding('shell');

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
'use strict'
'use strict';
/* global require */
// Monkey-patch the fs module.
require('electron/js2c/asar').wrapFsWithAsar(require('fs'))
require('electron/js2c/asar').wrapFsWithAsar(require('fs'));

View File

@@ -1,4 +1,4 @@
import { Buffer } from 'buffer'
import { Buffer } from 'buffer';
const typedArrays: Record<string, Function> = {
Buffer,
@@ -12,31 +12,31 @@ const typedArrays: Record<string, Function> = {
Uint32Array,
Float32Array,
Float64Array
}
};
type BufferLike = Buffer | ArrayBuffer | ArrayBufferView
function getType (value: BufferLike) {
for (const type of Object.keys(typedArrays)) {
if (value instanceof typedArrays[type]) {
return type
return type;
}
}
throw new Error('Invalid buffer')
throw new Error('Invalid buffer');
}
function getBuffer (value: BufferLike) {
if (value instanceof Buffer) {
return value
return value;
} else if (value instanceof ArrayBuffer) {
return Buffer.from(value)
return Buffer.from(value);
} else {
return Buffer.from(value.buffer, value.byteOffset, value.byteLength)
return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
}
}
export function isBuffer (value: BufferLike) {
return ArrayBuffer.isView(value) || value instanceof ArrayBuffer
return ArrayBuffer.isView(value) || value instanceof ArrayBuffer;
}
interface BufferMeta {
@@ -52,20 +52,20 @@ export function bufferToMeta (value: BufferLike): BufferMeta {
// NB. We only use length when decoding Int8Array and friends.
// For other buffer-like types this is expected to be undefined.
length: (value as Buffer).length
}
};
}
export function metaToBuffer (value: BufferMeta) {
const constructor = typedArrays[value.type]
const data = getBuffer(value.data)
const constructor = typedArrays[value.type];
const data = getBuffer(value.data);
if (constructor === Buffer) {
return data
return data;
} else if (constructor === ArrayBuffer) {
return data.buffer
return data.buffer;
} else if (constructor) {
return new (constructor as any)(data.buffer, data.byteOffset, value.length)
return new (constructor as any)(data.buffer, data.byteOffset, value.length);
} else {
return data
return data;
}
}

View File

@@ -1,19 +1,19 @@
'use strict'
'use strict';
const binding = process.electronBinding('crash_reporter')
const binding = process.electronBinding('crash_reporter');
class CrashReporter {
contructor () {
this.productName = null
this.crashesDirectory = null
this.productName = null;
this.crashesDirectory = null;
}
init (options) {
throw new Error('Not implemented')
throw new Error('Not implemented');
}
start (options) {
if (options == null) options = {}
if (options == null) options = {};
const {
productName,
@@ -22,81 +22,81 @@ class CrashReporter {
ignoreSystemCrashHandler = false,
submitURL,
uploadToServer = true
} = options
} = options;
if (companyName == null) throw new Error('companyName is a required option to crashReporter.start')
if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start')
if (companyName == null) throw new Error('companyName is a required option to crashReporter.start');
if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start');
const ret = this.init({
submitURL,
productName
})
});
this.productName = ret.productName
this.crashesDirectory = ret.crashesDirectory
this.productName = ret.productName;
this.crashesDirectory = ret.crashesDirectory;
if (extra._productName == null) extra._productName = ret.productName
if (extra._companyName == null) extra._companyName = companyName
if (extra._version == null) extra._version = ret.appVersion
if (extra._productName == null) extra._productName = ret.productName;
if (extra._companyName == null) extra._companyName = companyName;
if (extra._version == null) extra._version = ret.appVersion;
binding.start(ret.productName, companyName, submitURL, ret.crashesDirectory, uploadToServer, ignoreSystemCrashHandler, extra)
binding.start(ret.productName, companyName, submitURL, ret.crashesDirectory, uploadToServer, ignoreSystemCrashHandler, extra);
}
getLastCrashReport () {
const reports = this.getUploadedReports()
.sort((a, b) => {
const ats = (a && a.date) ? new Date(a.date).getTime() : 0
const bts = (b && b.date) ? new Date(b.date).getTime() : 0
return bts - ats
})
const ats = (a && a.date) ? new Date(a.date).getTime() : 0;
const bts = (b && b.date) ? new Date(b.date).getTime() : 0;
return bts - ats;
});
return (reports.length > 0) ? reports[0] : null
return (reports.length > 0) ? reports[0] : null;
}
getUploadedReports () {
const crashDir = this.getCrashesDirectory()
const crashDir = this.getCrashesDirectory();
if (!crashDir) {
throw new Error('crashReporter has not been started')
throw new Error('crashReporter has not been started');
}
return binding.getUploadedReports(crashDir)
return binding.getUploadedReports(crashDir);
}
getCrashesDirectory () {
return this.crashesDirectory
return this.crashesDirectory;
}
getProductName () {
return this.productName
return this.productName;
}
getUploadToServer () {
if (process.type === 'browser') {
return binding.getUploadToServer()
return binding.getUploadToServer();
} else {
throw new Error('getUploadToServer can only be called from the main process')
throw new Error('getUploadToServer can only be called from the main process');
}
}
setUploadToServer (uploadToServer) {
if (process.type === 'browser') {
return binding.setUploadToServer(uploadToServer)
return binding.setUploadToServer(uploadToServer);
} else {
throw new Error('setUploadToServer can only be called from the main process')
throw new Error('setUploadToServer can only be called from the main process');
}
}
addExtraParameter (key, value) {
binding.addExtraParameter(key, value)
binding.addExtraParameter(key, value);
}
removeExtraParameter (key) {
binding.removeExtraParameter(key)
binding.removeExtraParameter(key);
}
getParameters () {
return binding.getParameters()
return binding.getParameters();
}
}
module.exports = CrashReporter
module.exports = CrashReporter;

View File

@@ -1,13 +1,13 @@
export function electronBindingSetup (binding: typeof process['_linkedBinding'], processType: typeof process['type']): typeof process['electronBinding'] {
return function electronBinding (name: string) {
try {
return binding(`atom_${processType}_${name}`)
return binding(`atom_${processType}_${name}`);
} catch (error) {
if (/No such module/.test(error.message)) {
return binding(`atom_common_${name}`)
return binding(`atom_common_${name}`);
} else {
throw error
throw error;
}
}
}
};
}

View File

@@ -6,18 +6,18 @@ const constructors = new Map([
[SyntaxError.name, SyntaxError],
[TypeError.name, TypeError],
[URIError.name, URIError]
])
]);
export function deserialize (error: Electron.SerializedError): Electron.ErrorWithCause {
if (error && error.__ELECTRON_SERIALIZED_ERROR__ && constructors.has(error.name)) {
const constructor = constructors.get(error.name)
const deserializedError = new constructor!(error.message) as Electron.ErrorWithCause
deserializedError.stack = error.stack
deserializedError.from = error.from
deserializedError.cause = exports.deserialize(error.cause)
return deserializedError
const constructor = constructors.get(error.name);
const deserializedError = new constructor!(error.message) as Electron.ErrorWithCause;
deserializedError.stack = error.stack;
deserializedError.from = error.from;
deserializedError.cause = exports.deserialize(error.cause);
return deserializedError;
}
return error
return error;
}
export function serialize (error: Electron.ErrorWithCause): Electron.SerializedError {
@@ -31,7 +31,7 @@ export function serialize (error: Electron.ErrorWithCause): Electron.SerializedE
from: process.type as Electron.ProcessType,
cause: exports.serialize(error.cause),
__ELECTRON_SERIALIZED_ERROR__: true
}
};
}
return error
return error;
}

View File

@@ -1,10 +1,10 @@
import * as util from 'util'
import * as util from 'util';
import { electronBindingSetup } from '@electron/internal/common/electron-binding-setup'
import { electronBindingSetup } from '@electron/internal/common/electron-binding-setup';
const timers = require('timers')
const timers = require('timers');
process.electronBinding = electronBindingSetup(process._linkedBinding, process.type)
process.electronBinding = electronBindingSetup(process._linkedBinding, process.type);
type AnyFn = (...args: any[]) => any
@@ -17,11 +17,11 @@ type AnyFn = (...args: any[]) => any
const wrapWithActivateUvLoop = function <T extends AnyFn> (func: T): T {
return wrap(func, function (func) {
return function (this: any, ...args: any[]) {
process.activateUvLoop()
return func.apply(this, args)
}
}) as T
}
process.activateUvLoop();
return func.apply(this, args);
};
}) as T;
};
/**
* Casts to any below for func are due to Typescript not supporting symbols
@@ -30,41 +30,41 @@ const wrapWithActivateUvLoop = function <T extends AnyFn> (func: T): T {
* Refs: https://github.com/Microsoft/TypeScript/issues/1863
*/
function wrap <T extends AnyFn> (func: T, wrapper: (fn: AnyFn) => T) {
const wrapped = wrapper(func)
const wrapped = wrapper(func);
if ((func as any)[util.promisify.custom]) {
(wrapped as any)[util.promisify.custom] = wrapper((func as any)[util.promisify.custom])
(wrapped as any)[util.promisify.custom] = wrapper((func as any)[util.promisify.custom]);
}
return wrapped
return wrapped;
}
process.nextTick = wrapWithActivateUvLoop(process.nextTick)
process.nextTick = wrapWithActivateUvLoop(process.nextTick);
global.setImmediate = timers.setImmediate = wrapWithActivateUvLoop(timers.setImmediate)
global.clearImmediate = timers.clearImmediate
global.setImmediate = timers.setImmediate = wrapWithActivateUvLoop(timers.setImmediate);
global.clearImmediate = timers.clearImmediate;
// setTimeout needs to update the polling timeout of the event loop, when
// called under Chromium's event loop the node's event loop won't get a chance
// to update the timeout, so we have to force the node's event loop to
// recalculate the timeout in browser process.
timers.setTimeout = wrapWithActivateUvLoop(timers.setTimeout)
timers.setInterval = wrapWithActivateUvLoop(timers.setInterval)
timers.setTimeout = wrapWithActivateUvLoop(timers.setTimeout);
timers.setInterval = wrapWithActivateUvLoop(timers.setInterval);
// Only override the global setTimeout/setInterval impls in the browser process
if (process.type === 'browser') {
global.setTimeout = timers.setTimeout
global.setInterval = timers.setInterval
global.setTimeout = timers.setTimeout;
global.setInterval = timers.setInterval;
}
if (process.platform === 'win32') {
// Always returns EOF for stdin stream.
const { Readable } = require('stream')
const stdin = new Readable()
stdin.push(null)
const { Readable } = require('stream');
const stdin = new Readable();
stdin.push(null);
Object.defineProperty(process, 'stdin', {
configurable: false,
enumerable: true,
get () {
return stdin
return stdin;
}
})
});
}

View File

@@ -8,5 +8,5 @@ export function isPromise (val: any) {
val.constructor.reject instanceof Function &&
val.constructor.resolve &&
val.constructor.resolve instanceof Function
)
);
}

View File

@@ -1,21 +1,21 @@
'use strict'
'use strict';
// parses a feature string that has the format used in window.open()
// - `features` input string
// - `emit` function(key, value) - called for each parsed KV
module.exports = function parseFeaturesString (features, emit) {
features = `${features}`.trim()
features = `${features}`.trim();
// split the string by ','
features.split(/\s*,\s*/).forEach((feature) => {
// expected form is either a key by itself or a key/value pair in the form of
// 'key=value'
let [key, value] = feature.split(/\s*=\s*/)
if (!key) return
let [key, value] = feature.split(/\s*=\s*/);
if (!key) return;
// interpret the value as a boolean, if possible
value = (value === 'yes' || value === '1') ? true : (value === 'no' || value === '0') ? false : value
value = (value === 'yes' || value === '1') ? true : (value === 'no' || value === '0') ? false : value;
// emit the parsed pair
emit(key, value)
})
}
emit(key, value);
});
};

View File

@@ -1,42 +1,42 @@
import * as path from 'path'
import * as path from 'path';
const Module = require('module')
const Module = require('module');
// Clear Node's global search paths.
Module.globalPaths.length = 0
Module.globalPaths.length = 0;
// Prevent Node from adding paths outside this app to search paths.
const resourcesPathWithTrailingSlash = process.resourcesPath + path.sep
const originalNodeModulePaths = Module._nodeModulePaths
const resourcesPathWithTrailingSlash = process.resourcesPath + path.sep;
const originalNodeModulePaths = Module._nodeModulePaths;
Module._nodeModulePaths = function (from: string) {
const paths: string[] = originalNodeModulePaths(from)
const fromPath = path.resolve(from) + path.sep
const paths: string[] = originalNodeModulePaths(from);
const fromPath = path.resolve(from) + path.sep;
// If "from" is outside the app then we do nothing.
if (fromPath.startsWith(resourcesPathWithTrailingSlash)) {
return paths.filter(function (candidate) {
return candidate.startsWith(resourcesPathWithTrailingSlash)
})
return candidate.startsWith(resourcesPathWithTrailingSlash);
});
} else {
return paths
return paths;
}
}
};
// Make a fake Electron module that we will insert into the module cache
const electronModule = new Module('electron', null)
electronModule.id = 'electron'
electronModule.loaded = true
electronModule.filename = 'electron'
const electronModule = new Module('electron', null);
electronModule.id = 'electron';
electronModule.loaded = true;
electronModule.filename = 'electron';
Object.defineProperty(electronModule, 'exports', {
get: () => require('electron')
})
});
Module._cache['electron'] = electronModule
Module._cache['electron'] = electronModule;
const originalResolveFilename = Module._resolveFilename
const originalResolveFilename = Module._resolveFilename;
Module._resolveFilename = function (request: string, parent: NodeModule, isMain: boolean) {
if (request === 'electron') {
return 'electron'
return 'electron';
} else {
return originalResolveFilename(request, parent, isMain)
return originalResolveFilename(request, parent, isMain);
}
}
};

View File

@@ -1,10 +1,10 @@
const { nativeImage, NativeImage } = process.electronBinding('native_image')
const { nativeImage, NativeImage } = process.electronBinding('native_image');
const objectMap = function (source: Object, mapper: (value: any) => any) {
const sourceEntries = Object.entries(source)
const targetEntries = sourceEntries.map(([key, val]) => [key, mapper(val)])
return Object.fromEntries(targetEntries)
}
const sourceEntries = Object.entries(source);
const targetEntries = sourceEntries.map(([key, val]) => [key, mapper(val)]);
return Object.fromEntries(targetEntries);
};
export function serialize (value: any): any {
if (value instanceof NativeImage) {
@@ -12,28 +12,28 @@ export function serialize (value: any): any {
buffer: value.toBitmap(),
size: value.getSize(),
__ELECTRON_SERIALIZED_NativeImage__: true
}
};
} else if (Array.isArray(value)) {
return value.map(serialize)
return value.map(serialize);
} else if (value instanceof Buffer) {
return value
return value;
} else if (value instanceof Object) {
return objectMap(value, serialize)
return objectMap(value, serialize);
} else {
return value
return value;
}
}
export function deserialize (value: any): any {
if (value && value.__ELECTRON_SERIALIZED_NativeImage__) {
return nativeImage.createFromBitmap(value.buffer, value.size)
return nativeImage.createFromBitmap(value.buffer, value.size);
} else if (Array.isArray(value)) {
return value.map(deserialize)
return value.map(deserialize);
} else if (value instanceof Buffer) {
return value
return value;
} else if (value instanceof Object) {
return objectMap(value, deserialize)
return objectMap(value, deserialize);
} else {
return value
return value;
}
}

View File

@@ -48,7 +48,15 @@ export const syncMethods = new Set([
'getZoomLevel',
'setZoomFactor',
'setZoomLevel'
])
]);
export const properties = new Set([
'audioMuted',
'userAgent',
'zoomLevel',
'zoomFactor',
'frameRate'
]);
export const asyncMethods = new Set([
'loadURL',
@@ -62,4 +70,4 @@ export const asyncMethods = new Set([
'setVisualZoomLevelLimits',
'print',
'printToPDF'
])
]);

View File

@@ -5,4 +5,4 @@
//
// Will mutate this captured one as well and that is OK.
export const Promise = global.Promise
export const Promise = global.Promise;

View File

@@ -1,37 +1,37 @@
'use strict'
'use strict';
/* global nodeProcess, isolatedWorld, worldId */
const { EventEmitter } = require('events')
const { EventEmitter } = require('events');
process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(nodeProcess._linkedBinding, 'renderer')
process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(nodeProcess._linkedBinding, 'renderer');
const v8Util = process.electronBinding('v8_util')
const v8Util = process.electronBinding('v8_util');
// The `lib/renderer/ipc-renderer-internal.js` module looks for the ipc object in the
// "ipc-internal" hidden value
v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal'))
v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal'));
// The process object created by webpack is not an event emitter, fix it so
// the API is more compatible with non-sandboxed renderers.
for (const prop of Object.keys(EventEmitter.prototype)) {
if (process.hasOwnProperty(prop)) {
delete process[prop]
delete process[prop];
}
}
Object.setPrototypeOf(process, EventEmitter.prototype)
Object.setPrototypeOf(process, EventEmitter.prototype);
const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-args')
const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-args');
if (isolatedWorldArgs) {
const { guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen } = isolatedWorldArgs
const { windowSetup } = require('@electron/internal/renderer/window-setup')
windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen)
const { guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen } = isolatedWorldArgs;
const { windowSetup } = require('@electron/internal/renderer/window-setup');
windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen);
}
const extensionId = v8Util.getHiddenValue(isolatedWorld, `extension-${worldId}`)
const extensionId = v8Util.getHiddenValue(isolatedWorld, `extension-${worldId}`);
if (extensionId) {
const chromeAPI = require('@electron/internal/renderer/chrome-api')
chromeAPI.injectTo(extensionId, window)
const chromeAPI = require('@electron/internal/renderer/chrome-api');
chromeAPI.injectTo(extensionId, window);
}

View File

@@ -1,27 +1,27 @@
'use strict'
'use strict';
/* global nodeProcess, isolatedWorld */
process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(nodeProcess._linkedBinding, 'renderer')
process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(nodeProcess._linkedBinding, 'renderer');
const v8Util = process.electronBinding('v8_util')
const v8Util = process.electronBinding('v8_util');
// The `lib/renderer/ipc-renderer-internal.js` module looks for the ipc object in the
// "ipc-internal" hidden value
v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal'))
v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal'));
const webViewImpl = v8Util.getHiddenValue(isolatedWorld, 'web-view-impl')
const webViewImpl = v8Util.getHiddenValue(isolatedWorld, 'web-view-impl');
if (webViewImpl) {
// Must setup the WebView element in main world.
const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element')
setupWebView(v8Util, webViewImpl)
const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element');
setupWebView(v8Util, webViewImpl);
}
const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-args')
const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-args');
if (isolatedWorldArgs) {
const { guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen } = isolatedWorldArgs
const { windowSetup } = require('@electron/internal/renderer/window-setup')
windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen)
const { guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen } = isolatedWorldArgs;
const { windowSetup } = require('@electron/internal/renderer/window-setup');
windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen);
}

View File

@@ -1,20 +1,20 @@
const { hasSwitch } = process.electronBinding('command_line')
const binding = process.electronBinding('context_bridge')
const { hasSwitch } = process.electronBinding('command_line');
const binding = process.electronBinding('context_bridge');
const contextIsolationEnabled = hasSwitch('context-isolation')
const contextIsolationEnabled = hasSwitch('context-isolation');
const checkContextIsolationEnabled = () => {
if (!contextIsolationEnabled) throw new Error('contextBridge API can only be used when contextIsolation is enabled')
}
if (!contextIsolationEnabled) throw new Error('contextBridge API can only be used when contextIsolation is enabled');
};
const contextBridge = {
exposeInMainWorld: (key: string, api: Record<string, any>) => {
checkContextIsolationEnabled()
return binding.exposeAPIInMainWorld(key, api)
checkContextIsolationEnabled();
return binding.exposeAPIInMainWorld(key, api);
},
debugGC: () => binding._debugGCMaps({})
}
};
if (!binding._debugGCMaps) delete contextBridge.debugGC
if (!binding._debugGCMaps) delete contextBridge.debugGC;
export default contextBridge
export default contextBridge;

View File

@@ -1,12 +1,12 @@
'use strict'
'use strict';
const CrashReporter = require('@electron/internal/common/crash-reporter')
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils')
const CrashReporter = require('@electron/internal/common/crash-reporter');
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils');
class CrashReporterRenderer extends CrashReporter {
init (options) {
return ipcRendererUtils.invokeSync('ELECTRON_CRASH_REPORTER_INIT', options)
return ipcRendererUtils.invokeSync('ELECTRON_CRASH_REPORTER_INIT', options);
}
}
module.exports = new CrashReporterRenderer()
module.exports = new CrashReporterRenderer();

View File

@@ -1,27 +1,27 @@
import { nativeImage } from 'electron'
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
import { nativeImage } from 'electron';
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils';
// |options.types| can't be empty and must be an array
function isValid (options: Electron.SourcesOptions) {
const types = options ? options.types : undefined
return Array.isArray(types)
const types = options ? options.types : undefined;
return Array.isArray(types);
}
export async function getSources (options: Electron.SourcesOptions) {
if (!isValid(options)) throw new Error('Invalid options')
if (!isValid(options)) throw new Error('Invalid options');
const captureWindow = options.types.includes('window')
const captureScreen = options.types.includes('screen')
const captureWindow = options.types.includes('window');
const captureScreen = options.types.includes('screen');
const { thumbnailSize = { width: 150, height: 150 } } = options
const { fetchWindowIcons = false } = options
const { thumbnailSize = { width: 150, height: 150 } } = options;
const { fetchWindowIcons = false } = options;
const sources = await ipcRendererUtils.invoke<ElectronInternal.GetSourcesResult[]>('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', {
captureWindow,
captureScreen,
thumbnailSize,
fetchWindowIcons
} as ElectronInternal.GetSourcesOptions)
} as ElectronInternal.GetSourcesOptions);
return sources.map(source => ({
id: source.id,
@@ -29,5 +29,5 @@ export async function getSources (options: Electron.SourcesOptions) {
thumbnail: nativeImage.createFromDataURL(source.thumbnail),
display_id: source.display_id,
appIcon: source.appIcon ? nativeImage.createFromDataURL(source.appIcon) : null
}))
}));
}

View File

@@ -1,14 +1,14 @@
'use strict'
'use strict';
const common = require('@electron/internal/common/api/exports/electron')
const moduleList = require('@electron/internal/renderer/api/module-list')
const common = require('@electron/internal/common/api/exports/electron');
const moduleList = require('@electron/internal/renderer/api/module-list');
// Import common modules.
common.defineProperties(exports)
common.defineProperties(exports);
for (const module of moduleList) {
Object.defineProperty(exports, module.name, {
enumerable: !module.private,
get: common.handleESModule(module.loader)
})
});
}

View File

@@ -1,37 +1,37 @@
'use strict'
'use strict';
const { ipc } = process.electronBinding('ipc')
const v8Util = process.electronBinding('v8_util')
const { ipc } = process.electronBinding('ipc');
const v8Util = process.electronBinding('v8_util');
// Created by init.js.
const ipcRenderer = v8Util.getHiddenValue(global, 'ipc')
const internal = false
const ipcRenderer = v8Util.getHiddenValue(global, 'ipc');
const internal = false;
ipcRenderer.send = function (channel, ...args) {
return ipc.send(internal, channel, args)
}
return ipc.send(internal, channel, args);
};
ipcRenderer.sendSync = function (channel, ...args) {
return ipc.sendSync(internal, channel, args)[0]
}
return ipc.sendSync(internal, channel, args)[0];
};
ipcRenderer.sendToHost = function (channel, ...args) {
return ipc.sendToHost(channel, args)
}
return ipc.sendToHost(channel, args);
};
ipcRenderer.sendTo = function (webContentsId, channel, ...args) {
return ipc.sendTo(internal, false, webContentsId, channel, args)
}
return ipc.sendTo(internal, false, webContentsId, channel, args);
};
ipcRenderer.sendToAll = function (webContentsId, channel, ...args) {
return ipc.sendTo(internal, true, webContentsId, channel, args)
}
return ipc.sendTo(internal, true, webContentsId, channel, args);
};
ipcRenderer.invoke = function (channel, ...args) {
return ipc.invoke(channel, args).then(({ error, result }) => {
if (error) { throw new Error(`Error invoking remote method '${channel}': ${error}`) }
return result
})
}
if (error) { throw new Error(`Error invoking remote method '${channel}': ${error}`); }
return result;
});
};
module.exports = ipcRenderer
module.exports = ipcRenderer;

View File

@@ -1,9 +1,9 @@
'use strict'
'use strict';
const features = process.electronBinding('features')
const v8Util = process.electronBinding('v8_util')
const features = process.electronBinding('features');
const v8Util = process.electronBinding('v8_util');
const enableRemoteModule = v8Util.getHiddenValue(global, 'enableRemoteModule')
const enableRemoteModule = v8Util.getHiddenValue(global, 'enableRemoteModule');
// Renderer side modules, please sort alphabetically.
// A module is `enabled` if there is no explicit condition defined.
@@ -12,12 +12,12 @@ module.exports = [
{ name: 'crashReporter', loader: () => require('./crash-reporter') },
{ name: 'ipcRenderer', loader: () => require('./ipc-renderer') },
{ name: 'webFrame', loader: () => require('./web-frame') }
]
];
if (features.isDesktopCapturerEnabled()) {
module.exports.push({ name: 'desktopCapturer', loader: () => require('./desktop-capturer') })
module.exports.push({ name: 'desktopCapturer', loader: () => require('./desktop-capturer') });
}
if (enableRemoteModule) {
module.exports.push({ name: 'remote', loader: () => require('./remote') })
module.exports.push({ name: 'remote', loader: () => require('./remote') });
}

View File

@@ -1,27 +1,27 @@
'use strict'
'use strict';
const v8Util = process.electronBinding('v8_util')
const v8Util = process.electronBinding('v8_util');
const { CallbacksRegistry } = require('@electron/internal/renderer/callbacks-registry')
const bufferUtils = require('@electron/internal/common/buffer-utils')
const errorUtils = require('@electron/internal/common/error-utils')
const { isPromise } = require('@electron/internal/common/is-promise')
const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal')
const { CallbacksRegistry } = require('@electron/internal/renderer/callbacks-registry');
const bufferUtils = require('@electron/internal/common/buffer-utils');
const errorUtils = require('@electron/internal/common/error-utils');
const { isPromise } = require('@electron/internal/common/is-promise');
const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal');
const callbacksRegistry = new CallbacksRegistry()
const remoteObjectCache = v8Util.createIDWeakMap()
const callbacksRegistry = new CallbacksRegistry();
const remoteObjectCache = v8Util.createIDWeakMap();
// An unique ID that can represent current context.
const contextId = v8Util.getHiddenValue(global, 'contextId')
const contextId = v8Util.getHiddenValue(global, 'contextId');
// Notify the main process when current context is going to be released.
// Note that when the renderer process is destroyed, the message may not be
// sent, we also listen to the "render-view-deleted" event in the main process
// to guard that situation.
process.on('exit', () => {
const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE'
ipcRendererInternal.send(command, contextId)
})
const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE';
ipcRendererInternal.send(command, contextId);
});
// Convert the arguments object into an array of meta data.
function wrapArgs (args, visited = new Set()) {
@@ -31,182 +31,182 @@ function wrapArgs (args, visited = new Set()) {
return {
type: 'value',
value: null
}
};
}
if (Array.isArray(value)) {
visited.add(value)
visited.add(value);
const meta = {
type: 'array',
value: wrapArgs(value, visited)
}
visited.delete(value)
return meta
};
visited.delete(value);
return meta;
} else if (bufferUtils.isBuffer(value)) {
return {
type: 'buffer',
value: bufferUtils.bufferToMeta(value)
}
};
} else if (value instanceof Date) {
return {
type: 'date',
value: value.getTime()
}
};
} else if ((value != null) && typeof value === 'object') {
if (isPromise(value)) {
return {
type: 'promise',
then: valueToMeta(function (onFulfilled, onRejected) {
value.then(onFulfilled, onRejected)
value.then(onFulfilled, onRejected);
})
}
};
} else if (v8Util.getHiddenValue(value, 'atomId')) {
return {
type: 'remote-object',
id: v8Util.getHiddenValue(value, 'atomId')
}
};
}
const meta = {
type: 'object',
name: value.constructor ? value.constructor.name : '',
members: []
}
visited.add(value)
for (const prop in value) {
};
visited.add(value);
for (const prop in value) { // eslint-disable-line guard-for-in
meta.members.push({
name: prop,
value: valueToMeta(value[prop])
})
});
}
visited.delete(value)
return meta
visited.delete(value);
return meta;
} else if (typeof value === 'function' && v8Util.getHiddenValue(value, 'returnValue')) {
return {
type: 'function-with-return-value',
value: valueToMeta(value())
}
};
} else if (typeof value === 'function') {
return {
type: 'function',
id: callbacksRegistry.add(value),
location: v8Util.getHiddenValue(value, 'location'),
length: value.length
}
};
} else {
return {
type: 'value',
value: value
}
};
}
}
return args.map(valueToMeta)
};
return args.map(valueToMeta);
}
// Populate object's members from descriptors.
// The |ref| will be kept referenced by |members|.
// This matches |getObjectMemebers| in rpc-server.
function setObjectMembers (ref, object, metaId, members) {
if (!Array.isArray(members)) return
if (!Array.isArray(members)) return;
for (const member of members) {
if (object.hasOwnProperty(member.name)) continue
if (object.hasOwnProperty(member.name)) continue;
const descriptor = { enumerable: member.enumerable }
const descriptor = { enumerable: member.enumerable };
if (member.type === 'method') {
const remoteMemberFunction = function (...args) {
let command
let command;
if (this && this.constructor === remoteMemberFunction) {
command = 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR'
command = 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR';
} else {
command = 'ELECTRON_BROWSER_MEMBER_CALL'
command = 'ELECTRON_BROWSER_MEMBER_CALL';
}
const ret = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, wrapArgs(args))
return metaToValue(ret)
}
const ret = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, wrapArgs(args));
return metaToValue(ret);
};
let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name)
let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name);
descriptor.get = () => {
descriptorFunction.ref = ref // The member should reference its object.
return descriptorFunction
}
descriptorFunction.ref = ref; // The member should reference its object.
return descriptorFunction;
};
// Enable monkey-patch the method
descriptor.set = (value) => {
descriptorFunction = value
return value
}
descriptor.configurable = true
descriptorFunction = value;
return value;
};
descriptor.configurable = true;
} else if (member.type === 'get') {
descriptor.get = () => {
const command = 'ELECTRON_BROWSER_MEMBER_GET'
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name)
return metaToValue(meta)
}
const command = 'ELECTRON_BROWSER_MEMBER_GET';
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name);
return metaToValue(meta);
};
if (member.writable) {
descriptor.set = (value) => {
const args = wrapArgs([value])
const command = 'ELECTRON_BROWSER_MEMBER_SET'
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, args)
if (meta != null) metaToValue(meta)
return value
}
const args = wrapArgs([value]);
const command = 'ELECTRON_BROWSER_MEMBER_SET';
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, args);
if (meta != null) metaToValue(meta);
return value;
};
}
}
Object.defineProperty(object, member.name, descriptor)
Object.defineProperty(object, member.name, descriptor);
}
}
// Populate object's prototype from descriptor.
// This matches |getObjectPrototype| in rpc-server.
function setObjectPrototype (ref, object, metaId, descriptor) {
if (descriptor === null) return
const proto = {}
setObjectMembers(ref, proto, metaId, descriptor.members)
setObjectPrototype(ref, proto, metaId, descriptor.proto)
Object.setPrototypeOf(object, proto)
if (descriptor === null) return;
const proto = {};
setObjectMembers(ref, proto, metaId, descriptor.members);
setObjectPrototype(ref, proto, metaId, descriptor.proto);
Object.setPrototypeOf(object, proto);
}
// Wrap function in Proxy for accessing remote properties
function proxyFunctionProperties (remoteMemberFunction, metaId, name) {
let loaded = false
let loaded = false;
// Lazily load function properties
const loadRemoteProperties = () => {
if (loaded) return
loaded = true
const command = 'ELECTRON_BROWSER_MEMBER_GET'
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, name)
setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members)
}
if (loaded) return;
loaded = true;
const command = 'ELECTRON_BROWSER_MEMBER_GET';
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, name);
setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members);
};
return new Proxy(remoteMemberFunction, {
set: (target, property, value, receiver) => {
if (property !== 'ref') loadRemoteProperties()
target[property] = value
return true
if (property !== 'ref') loadRemoteProperties();
target[property] = value;
return true;
},
get: (target, property, receiver) => {
if (!target.hasOwnProperty(property)) loadRemoteProperties()
const value = target[property]
if (!target.hasOwnProperty(property)) loadRemoteProperties();
const value = target[property];
if (property === 'toString' && typeof value === 'function') {
return value.bind(target)
return value.bind(target);
}
return value
return value;
},
ownKeys: (target) => {
loadRemoteProperties()
return Object.getOwnPropertyNames(target)
loadRemoteProperties();
return Object.getOwnPropertyNames(target);
},
getOwnPropertyDescriptor: (target, property) => {
const descriptor = Object.getOwnPropertyDescriptor(target, property)
if (descriptor) return descriptor
loadRemoteProperties()
return Object.getOwnPropertyDescriptor(target, property)
const descriptor = Object.getOwnPropertyDescriptor(target, property);
if (descriptor) return descriptor;
loadRemoteProperties();
return Object.getOwnPropertyDescriptor(target, property);
}
})
});
}
// Convert meta data from browser into real value.
@@ -218,143 +218,143 @@ function metaToValue (meta) {
promise: () => Promise.resolve({ then: metaToValue(meta.then) }),
error: () => metaToPlainObject(meta),
date: () => new Date(meta.value),
exception: () => { throw errorUtils.deserialize(meta.value) }
}
exception: () => { throw errorUtils.deserialize(meta.value); }
};
if (meta.type in types) {
return types[meta.type]()
if (Object.prototype.hasOwnProperty.call(types, meta.type)) {
return types[meta.type]();
} else {
let ret
let ret;
if (remoteObjectCache.has(meta.id)) {
v8Util.addRemoteObjectRef(contextId, meta.id)
return remoteObjectCache.get(meta.id)
v8Util.addRemoteObjectRef(contextId, meta.id);
return remoteObjectCache.get(meta.id);
}
// A shadow class to represent the remote function object.
if (meta.type === 'function') {
const remoteFunction = function (...args) {
let command
let command;
if (this && this.constructor === remoteFunction) {
command = 'ELECTRON_BROWSER_CONSTRUCTOR'
command = 'ELECTRON_BROWSER_CONSTRUCTOR';
} else {
command = 'ELECTRON_BROWSER_FUNCTION_CALL'
command = 'ELECTRON_BROWSER_FUNCTION_CALL';
}
const obj = ipcRendererInternal.sendSync(command, contextId, meta.id, wrapArgs(args))
return metaToValue(obj)
}
ret = remoteFunction
const obj = ipcRendererInternal.sendSync(command, contextId, meta.id, wrapArgs(args));
return metaToValue(obj);
};
ret = remoteFunction;
} else {
ret = {}
ret = {};
}
setObjectMembers(ret, ret, meta.id, meta.members)
setObjectPrototype(ret, ret, meta.id, meta.proto)
Object.defineProperty(ret.constructor, 'name', { value: meta.name })
setObjectMembers(ret, ret, meta.id, meta.members);
setObjectPrototype(ret, ret, meta.id, meta.proto);
Object.defineProperty(ret.constructor, 'name', { value: meta.name });
// Track delegate obj's lifetime & tell browser to clean up when object is GCed.
v8Util.setRemoteObjectFreer(ret, contextId, meta.id)
v8Util.setHiddenValue(ret, 'atomId', meta.id)
v8Util.addRemoteObjectRef(contextId, meta.id)
remoteObjectCache.set(meta.id, ret)
return ret
v8Util.setRemoteObjectFreer(ret, contextId, meta.id);
v8Util.setHiddenValue(ret, 'atomId', meta.id);
v8Util.addRemoteObjectRef(contextId, meta.id);
remoteObjectCache.set(meta.id, ret);
return ret;
}
}
// Construct a plain object from the meta.
function metaToPlainObject (meta) {
const obj = (() => meta.type === 'error' ? new Error() : {})()
const obj = (() => meta.type === 'error' ? new Error() : {})();
for (let i = 0; i < meta.members.length; i++) {
const { name, value } = meta.members[i]
obj[name] = value
const { name, value } = meta.members[i];
obj[name] = value;
}
return obj
return obj;
}
function handleMessage (channel, handler) {
ipcRendererInternal.on(channel, (event, passedContextId, id, ...args) => {
if (passedContextId === contextId) {
handler(id, ...args)
handler(id, ...args);
} else {
// Message sent to an un-exist context, notify the error to main process.
ipcRendererInternal.send('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', contextId, passedContextId, id)
ipcRendererInternal.send('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', contextId, passedContextId, id);
}
})
});
}
// Browser calls a callback in renderer.
handleMessage('ELECTRON_RENDERER_CALLBACK', (id, args) => {
callbacksRegistry.apply(id, metaToValue(args))
})
callbacksRegistry.apply(id, metaToValue(args));
});
// A callback in browser is released.
handleMessage('ELECTRON_RENDERER_RELEASE_CALLBACK', (id) => {
callbacksRegistry.remove(id)
})
callbacksRegistry.remove(id);
});
exports.require = (module) => {
const command = 'ELECTRON_BROWSER_REQUIRE'
const meta = ipcRendererInternal.sendSync(command, contextId, module)
return metaToValue(meta)
}
const command = 'ELECTRON_BROWSER_REQUIRE';
const meta = ipcRendererInternal.sendSync(command, contextId, module);
return metaToValue(meta);
};
// Alias to remote.require('electron').xxx.
exports.getBuiltin = (module) => {
const command = 'ELECTRON_BROWSER_GET_BUILTIN'
const meta = ipcRendererInternal.sendSync(command, contextId, module)
return metaToValue(meta)
}
const command = 'ELECTRON_BROWSER_GET_BUILTIN';
const meta = ipcRendererInternal.sendSync(command, contextId, module);
return metaToValue(meta);
};
exports.getCurrentWindow = () => {
const command = 'ELECTRON_BROWSER_CURRENT_WINDOW'
const meta = ipcRendererInternal.sendSync(command, contextId)
return metaToValue(meta)
}
const command = 'ELECTRON_BROWSER_CURRENT_WINDOW';
const meta = ipcRendererInternal.sendSync(command, contextId);
return metaToValue(meta);
};
// Get current WebContents object.
exports.getCurrentWebContents = () => {
const command = 'ELECTRON_BROWSER_CURRENT_WEB_CONTENTS'
const meta = ipcRendererInternal.sendSync(command, contextId)
return metaToValue(meta)
}
const command = 'ELECTRON_BROWSER_CURRENT_WEB_CONTENTS';
const meta = ipcRendererInternal.sendSync(command, contextId);
return metaToValue(meta);
};
// Get a global object in browser.
exports.getGlobal = (name) => {
const command = 'ELECTRON_BROWSER_GLOBAL'
const meta = ipcRendererInternal.sendSync(command, contextId, name)
return metaToValue(meta)
}
const command = 'ELECTRON_BROWSER_GLOBAL';
const meta = ipcRendererInternal.sendSync(command, contextId, name);
return metaToValue(meta);
};
// Get the process object in browser.
Object.defineProperty(exports, 'process', {
get: () => exports.getGlobal('process')
})
});
// Create a function that will return the specified value when called in browser.
exports.createFunctionWithReturnValue = (returnValue) => {
const func = () => returnValue
v8Util.setHiddenValue(func, 'returnValue', true)
return func
}
const func = () => returnValue;
v8Util.setHiddenValue(func, 'returnValue', true);
return func;
};
// Get the guest WebContents from guestInstanceId.
exports.getGuestWebContents = (guestInstanceId) => {
const command = 'ELECTRON_BROWSER_GUEST_WEB_CONTENTS'
const meta = ipcRendererInternal.sendSync(command, contextId, guestInstanceId)
return metaToValue(meta)
}
const command = 'ELECTRON_BROWSER_GUEST_WEB_CONTENTS';
const meta = ipcRendererInternal.sendSync(command, contextId, guestInstanceId);
return metaToValue(meta);
};
const addBuiltinProperty = (name) => {
Object.defineProperty(exports, name, {
get: () => exports.getBuiltin(name)
})
}
});
};
const browserModules =
require('@electron/internal/common/api/module-list').concat(
require('@electron/internal/browser/api/module-keys'))
require('@electron/internal/browser/api/module-keys'));
// And add a helper receiver for each one.
browserModules
.filter((m) => !m.private)
.map((m) => m.name)
.forEach(addBuiltinProperty)
.forEach(addBuiltinProperty);

View File

@@ -1,49 +1,49 @@
import { EventEmitter } from 'events'
import { EventEmitter } from 'events';
const binding = process.electronBinding('web_frame')
const binding = process.electronBinding('web_frame');
class WebFrame extends EventEmitter {
constructor (public context: Window) {
super()
super();
// Lots of webview would subscribe to webFrame's events.
this.setMaxListeners(0)
this.setMaxListeners(0);
}
findFrameByRoutingId (...args: Array<any>) {
return getWebFrame(binding._findFrameByRoutingId(this.context, ...args))
return getWebFrame(binding._findFrameByRoutingId(this.context, ...args));
}
getFrameForSelector (...args: Array<any>) {
return getWebFrame(binding._getFrameForSelector(this.context, ...args))
return getWebFrame(binding._getFrameForSelector(this.context, ...args));
}
findFrameByName (...args: Array<any>) {
return getWebFrame(binding._findFrameByName(this.context, ...args))
return getWebFrame(binding._findFrameByName(this.context, ...args));
}
get opener () {
return getWebFrame(binding._getOpener(this.context))
return getWebFrame(binding._getOpener(this.context));
}
get parent () {
return getWebFrame(binding._getParent(this.context))
return getWebFrame(binding._getParent(this.context));
}
get top () {
return getWebFrame(binding._getTop(this.context))
return getWebFrame(binding._getTop(this.context));
}
get firstChild () {
return getWebFrame(binding._getFirstChild(this.context))
return getWebFrame(binding._getFirstChild(this.context));
}
get nextSibling () {
return getWebFrame(binding._getNextSibling(this.context))
return getWebFrame(binding._getNextSibling(this.context));
}
get routingId () {
return binding._getRoutingId(this.context)
return binding._getRoutingId(this.context);
}
}
@@ -53,17 +53,17 @@ for (const name in binding) {
// TODO(felixrieseberg): Once we can type web_frame natives, we could
// use a neat `keyof` here
(WebFrame as any).prototype[name] = function (...args: Array<any>) {
return binding[name](this.context, ...args)
}
return binding[name](this.context, ...args);
};
}
}
// Helper to return WebFrame or null depending on context.
// TODO(zcbenz): Consider returning same WebFrame for the same frame.
function getWebFrame (context: Window) {
return context ? new WebFrame(context) : null
return context ? new WebFrame(context) : null;
}
const _webFrame = new WebFrame(window)
const _webFrame = new WebFrame(window);
export default _webFrame
export default _webFrame;

View File

@@ -1,4 +1,4 @@
const v8Util = process.electronBinding('v8_util')
const v8Util = process.electronBinding('v8_util');
export class CallbacksRegistry {
private nextId: number = 0
@@ -6,50 +6,50 @@ export class CallbacksRegistry {
add (callback: Function) {
// The callback is already added.
let id = v8Util.getHiddenValue<number>(callback, 'callbackId')
if (id != null) return id
let id = v8Util.getHiddenValue<number>(callback, 'callbackId');
if (id != null) return id;
id = this.nextId += 1
id = this.nextId += 1;
// Capture the location of the function and put it in the ID string,
// so that release errors can be tracked down easily.
const regexp = /at (.*)/gi
const stackString = (new Error()).stack
if (!stackString) return
const regexp = /at (.*)/gi;
const stackString = (new Error()).stack;
if (!stackString) return;
let filenameAndLine
let match
let filenameAndLine;
let match;
while ((match = regexp.exec(stackString)) !== null) {
const location = match[1]
if (location.includes('(native)')) continue
if (location.includes('(<anonymous>)')) continue
if (location.includes('electron/js2c')) continue
const location = match[1];
if (location.includes('(native)')) continue;
if (location.includes('(<anonymous>)')) continue;
if (location.includes('electron/js2c')) continue;
const ref = /([^/^)]*)\)?$/gi.exec(location)
if (ref) filenameAndLine = ref![1]
break
const ref = /([^/^)]*)\)?$/gi.exec(location);
if (ref) filenameAndLine = ref![1];
break;
}
this.callbacks[id] = callback
v8Util.setHiddenValue(callback, 'callbackId', id)
v8Util.setHiddenValue(callback, 'location', filenameAndLine)
return id
this.callbacks[id] = callback;
v8Util.setHiddenValue(callback, 'callbackId', id);
v8Util.setHiddenValue(callback, 'location', filenameAndLine);
return id;
}
get (id: number) {
return this.callbacks[id] || function () {}
return this.callbacks[id] || function () {};
}
apply (id: number, ...args: any[]) {
return this.get(id).apply(global, ...args)
return this.get(id).apply(global, ...args);
}
remove (id: number) {
const callback = this.callbacks[id]
const callback = this.callbacks[id];
if (callback) {
v8Util.deleteHiddenValue(callback, 'callbackId')
delete this.callbacks[id]
v8Util.deleteHiddenValue(callback, 'callbackId');
delete this.callbacks[id];
}
}
}

View File

@@ -1,14 +1,14 @@
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
import * as url from 'url'
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils';
import * as url from 'url';
import { Event } from '@electron/internal/renderer/extensions/event'
import { Event } from '@electron/internal/renderer/extensions/event';
class Tab {
public id: number
constructor (tabId: number) {
this.id = tabId
this.id = tabId;
}
}
@@ -18,9 +18,9 @@ class MessageSender {
public url: string
constructor (tabId: number, extensionId: string) {
this.tab = tabId ? new Tab(tabId) : null
this.id = extensionId
this.url = `chrome-extension://${extensionId}`
this.tab = tabId ? new Tab(tabId) : null;
this.id = extensionId;
this.url = `chrome-extension://${extensionId}`;
}
}
@@ -31,65 +31,65 @@ class Port {
public sender: MessageSender
constructor (public tabId: number, public portId: number, extensionId: string, public name: string) {
this.onDisconnect = new Event()
this.onMessage = new Event()
this.sender = new MessageSender(tabId, extensionId)
this.onDisconnect = new Event();
this.onMessage = new Event();
this.sender = new MessageSender(tabId, extensionId);
ipcRendererInternal.once(`CHROME_PORT_DISCONNECT_${portId}`, () => {
this._onDisconnect()
})
this._onDisconnect();
});
ipcRendererInternal.on(`CHROME_PORT_POSTMESSAGE_${portId}`, (
_event: Electron.Event, message: any
) => {
const sendResponse = function () { console.error('sendResponse is not implemented') }
this.onMessage.emit(JSON.parse(message), this.sender, sendResponse)
})
const sendResponse = function () { console.error('sendResponse is not implemented'); };
this.onMessage.emit(JSON.parse(message), this.sender, sendResponse);
});
}
disconnect () {
if (this.disconnected) return
if (this.disconnected) return;
ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_DISCONNECT_${this.portId}`)
this._onDisconnect()
ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_DISCONNECT_${this.portId}`);
this._onDisconnect();
}
postMessage (message: any) {
ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_POSTMESSAGE_${this.portId}`, JSON.stringify(message))
ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_POSTMESSAGE_${this.portId}`, JSON.stringify(message));
}
_onDisconnect () {
this.disconnected = true
ipcRendererInternal.removeAllListeners(`CHROME_PORT_POSTMESSAGE_${this.portId}`)
this.onDisconnect.emit()
this.disconnected = true;
ipcRendererInternal.removeAllListeners(`CHROME_PORT_POSTMESSAGE_${this.portId}`);
this.onDisconnect.emit();
}
}
// Inject chrome API to the |context|
export function injectTo (extensionId: string, context: any) {
const chrome = context.chrome = context.chrome || {}
const chrome = context.chrome = context.chrome || {};
ipcRendererInternal.on(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, (
_event: Electron.Event, tabId: number, portId: number, connectInfo: { name: string }
) => {
chrome.runtime.onConnect.emit(new Port(tabId, portId, extensionId, connectInfo.name))
})
chrome.runtime.onConnect.emit(new Port(tabId, portId, extensionId, connectInfo.name));
});
ipcRendererUtils.handle(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, (
_event: Electron.Event, tabId: number, message: string
) => {
return new Promise(resolve => {
chrome.runtime.onMessage.emit(message, new MessageSender(tabId, extensionId), resolve)
})
})
chrome.runtime.onMessage.emit(message, new MessageSender(tabId, extensionId), resolve);
});
});
ipcRendererInternal.on('CHROME_TABS_ONCREATED', (_event: Electron.Event, tabId: number) => {
chrome.tabs.onCreated.emit(new Tab(tabId))
})
chrome.tabs.onCreated.emit(new Tab(tabId));
});
ipcRendererInternal.on('CHROME_TABS_ONREMOVED', (_event: Electron.Event, tabId: number) => {
chrome.tabs.onRemoved.emit(tabId)
})
chrome.tabs.onRemoved.emit(tabId);
});
chrome.runtime = {
id: extensionId,
@@ -101,69 +101,69 @@ export function injectTo (extensionId: string, context: any) {
slashes: true,
hostname: extensionId,
pathname: path
})
});
},
// https://developer.chrome.com/extensions/runtime#method-getManifest
getManifest: function () {
const manifest = ipcRendererUtils.invokeSync('CHROME_EXTENSION_MANIFEST', extensionId)
return manifest
const manifest = ipcRendererUtils.invokeSync('CHROME_EXTENSION_MANIFEST', extensionId);
return manifest;
},
// https://developer.chrome.com/extensions/runtime#method-connect
connect (...args: Array<any>) {
// Parse the optional args.
let targetExtensionId = extensionId
let connectInfo = { name: '' }
let targetExtensionId = extensionId;
let connectInfo = { name: '' };
if (args.length === 1) {
if (typeof args[0] === 'string') {
targetExtensionId = args[0]
targetExtensionId = args[0];
} else {
connectInfo = args[0]
connectInfo = args[0];
}
} else if (args.length === 2) {
[targetExtensionId, connectInfo] = args
[targetExtensionId, connectInfo] = args;
}
const { tabId, portId } = ipcRendererUtils.invokeSync('CHROME_RUNTIME_CONNECT', targetExtensionId, connectInfo)
return new Port(tabId, portId, extensionId, connectInfo.name)
const { tabId, portId } = ipcRendererUtils.invokeSync('CHROME_RUNTIME_CONNECT', targetExtensionId, connectInfo);
return new Port(tabId, portId, extensionId, connectInfo.name);
},
// https://developer.chrome.com/extensions/runtime#method-sendMessage
sendMessage (...args: Array<any>) {
// Parse the optional args.
const targetExtensionId = extensionId
let message: string
let options: Object | undefined
let responseCallback: Chrome.Tabs.SendMessageCallback = () => {}
const targetExtensionId = extensionId;
let message: string;
let options: Object | undefined;
let responseCallback: Chrome.Tabs.SendMessageCallback = () => {};
if (typeof args[args.length - 1] === 'function') {
responseCallback = args.pop()
responseCallback = args.pop();
}
if (args.length === 1) {
[message] = args
[message] = args;
} else if (args.length === 2) {
if (typeof args[0] === 'string') {
[extensionId, message] = args
[extensionId, message] = args;
} else {
[message, options] = args
[message, options] = args;
}
} else {
[extensionId, message, options] = args
[extensionId, message, options] = args;
}
if (options) {
console.error('options are not supported')
console.error('options are not supported');
}
ipcRendererUtils.invoke('CHROME_RUNTIME_SEND_MESSAGE', targetExtensionId, message).then(responseCallback)
ipcRendererUtils.invoke('CHROME_RUNTIME_SEND_MESSAGE', targetExtensionId, message).then(responseCallback);
},
onConnect: new Event(),
onMessage: new Event(),
onInstalled: new Event()
}
};
chrome.tabs = {
// https://developer.chrome.com/extensions/tabs#method-executeScript
@@ -173,7 +173,7 @@ export function injectTo (extensionId: string, context: any) {
resultCallback: Chrome.Tabs.ExecuteScriptCallback = () => {}
) {
ipcRendererUtils.invoke('CHROME_TABS_EXECUTE_SCRIPT', tabId, extensionId, details)
.then((result: any) => resultCallback([result]))
.then((result: any) => resultCallback([result]));
},
// https://developer.chrome.com/extensions/tabs#method-sendMessage
@@ -183,13 +183,13 @@ export function injectTo (extensionId: string, context: any) {
_options: Chrome.Tabs.SendMessageDetails,
responseCallback: Chrome.Tabs.SendMessageCallback = () => {}
) {
ipcRendererUtils.invoke('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, message).then(responseCallback)
ipcRendererUtils.invoke('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, message).then(responseCallback);
},
onUpdated: new Event(),
onCreated: new Event(),
onRemoved: new Event()
}
};
chrome.extension = {
getURL: chrome.runtime.getURL,
@@ -197,9 +197,9 @@ export function injectTo (extensionId: string, context: any) {
onConnect: chrome.runtime.onConnect,
sendMessage: chrome.runtime.sendMessage,
onMessage: chrome.runtime.onMessage
}
};
chrome.storage = require('@electron/internal/renderer/extensions/storage').setup(extensionId)
chrome.storage = require('@electron/internal/renderer/extensions/storage').setup(extensionId);
chrome.pageAction = {
show () {},
@@ -209,14 +209,14 @@ export function injectTo (extensionId: string, context: any) {
setIcon () {},
setPopup () {},
getPopup () {}
}
};
chrome.i18n = require('@electron/internal/renderer/extensions/i18n').setup(extensionId)
chrome.webNavigation = require('@electron/internal/renderer/extensions/web-navigation').setup()
chrome.i18n = require('@electron/internal/renderer/extensions/i18n').setup(extensionId);
chrome.webNavigation = require('@electron/internal/renderer/extensions/web-navigation').setup();
// Electron has no concept of a browserAction but we should stub these APIs for compatibility
chrome.browserAction = {
setIcon () {},
setPopup () {}
}
};
}

Some files were not shown because too many files have changed in this diff Show More