mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
94 Commits
v11.0.0-ni
...
v10.0.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30db185060 | ||
|
|
c804212426 | ||
|
|
7a5b18b0d6 | ||
|
|
d608a30015 | ||
|
|
74efe8d834 | ||
|
|
3283211a15 | ||
|
|
d4a6affa4b | ||
|
|
73e13ab6d6 | ||
|
|
c32c69604a | ||
|
|
2761be6467 | ||
|
|
30cb2b134b | ||
|
|
48cd3eeced | ||
|
|
831b6fa227 | ||
|
|
91ce51cfb3 | ||
|
|
bf1870facc | ||
|
|
39bc7c82a6 | ||
|
|
991d982622 | ||
|
|
195662ead0 | ||
|
|
d580cc1578 | ||
|
|
d8de43f534 | ||
|
|
c48a6b48c9 | ||
|
|
d701c292a0 | ||
|
|
19e7af9841 | ||
|
|
86e4a96f45 | ||
|
|
e059ae7bd6 | ||
|
|
fb292c26d5 | ||
|
|
0e0dd6460f | ||
|
|
b464589143 | ||
|
|
ee372c152a | ||
|
|
53bdc4a71b | ||
|
|
ab0960ecd9 | ||
|
|
29c9c80c89 | ||
|
|
72f2dad3a1 | ||
|
|
64cdfda810 | ||
|
|
9f8f82af00 | ||
|
|
306d76b317 | ||
|
|
097c501cf5 | ||
|
|
d4bc4e1d4c | ||
|
|
0c861dc83e | ||
|
|
57b766aa82 | ||
|
|
4b8295a24d | ||
|
|
710199c308 | ||
|
|
c2d9cde38f | ||
|
|
fbaca68bd0 | ||
|
|
ee9f95df30 | ||
|
|
9df936d941 | ||
|
|
baa2940488 | ||
|
|
25879ba929 | ||
|
|
34b04e5fbb | ||
|
|
710ac2a5a8 | ||
|
|
1f0d2594bd | ||
|
|
656d9e7490 | ||
|
|
5bf938f899 | ||
|
|
7759048136 | ||
|
|
321c1c1075 | ||
|
|
d69d1bbf20 | ||
|
|
f3dd997dbd | ||
|
|
faf7951439 | ||
|
|
3087492994 | ||
|
|
f7e4377ee8 | ||
|
|
ef288a6ae2 | ||
|
|
7797835f93 | ||
|
|
9572209e91 | ||
|
|
3602216e6c | ||
|
|
c29382b6d0 | ||
|
|
a8fc41d8b3 | ||
|
|
9ea42da40e | ||
|
|
87cd20b9e9 | ||
|
|
79acd3e0ef | ||
|
|
67be46986c | ||
|
|
d4001c1787 | ||
|
|
eeaad9c6ab | ||
|
|
cc0c7632ac | ||
|
|
12ea1c0c41 | ||
|
|
9d2aa93581 | ||
|
|
cee9e6f0d0 | ||
|
|
eb93acc463 | ||
|
|
241e74c098 | ||
|
|
c0183d15af | ||
|
|
4fe7c9ac24 | ||
|
|
50efa847a5 | ||
|
|
c2354d44ea | ||
|
|
7709e600c6 | ||
|
|
0962c1bd74 | ||
|
|
471f80521d | ||
|
|
1fb11e1e76 | ||
|
|
f8508b3c18 | ||
|
|
95e3853b77 | ||
|
|
9de5ede1fb | ||
|
|
05efbbcdd5 | ||
|
|
2789f32efb | ||
|
|
4c8b884998 | ||
|
|
03ddd2d7af | ||
|
|
c141b1a906 |
@@ -832,7 +832,7 @@ step-restore-out-cache: &step-restore-out-cache
|
||||
paths:
|
||||
- ./src/out/Default
|
||||
keys:
|
||||
- v9-out-cache-{{ checksum "src/electron/.depshash" }}-{{ checksum "src/electron/.depshash-target" }}
|
||||
- v8-out-cache-{{ checksum "src/electron/.depshash" }}-{{ checksum "src/electron/.depshash-target" }}
|
||||
name: Restoring out cache
|
||||
|
||||
step-set-git-cache-path: &step-set-git-cache-path
|
||||
@@ -856,7 +856,7 @@ step-save-out-cache: &step-save-out-cache
|
||||
save_cache:
|
||||
paths:
|
||||
- ./src/out/Default
|
||||
key: v9-out-cache-{{ checksum "src/electron/.depshash" }}-{{ checksum "src/electron/.depshash-target" }}
|
||||
key: v8-out-cache-{{ checksum "src/electron/.depshash" }}-{{ checksum "src/electron/.depshash-target" }}
|
||||
name: Persisting out cache
|
||||
|
||||
step-run-electron-only-hooks: &step-run-electron-only-hooks
|
||||
|
||||
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -15,7 +15,6 @@ Contributors guide: https://github.com/electron/electron/blob/master/CONTRIBUTIN
|
||||
- [ ] relevant documentation is changed or added
|
||||
- [ ] PR title follows semantic [commit guidelines](https://github.com/electron/electron/blob/master/docs/development/pull-requests.md#commit-message-guidelines)
|
||||
- [ ] [PR release notes](https://github.com/electron/clerk/blob/master/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/master/README.md#examples).
|
||||
- [ ] This is **NOT A BREAKING CHANGE**. Breaking changes may not be merged to master until 11-x-y is branched.
|
||||
|
||||
#### Release Notes
|
||||
|
||||
|
||||
90
BUILD.gn
90
BUILD.gn
@@ -347,7 +347,6 @@ source_set("electron_lib") {
|
||||
"//device/bluetooth",
|
||||
"//device/bluetooth/public/cpp",
|
||||
"//gin",
|
||||
"//media/blink:blink",
|
||||
"//media/capture/mojom:video_capture",
|
||||
"//media/mojo/mojom",
|
||||
"//net:extras",
|
||||
@@ -497,17 +496,16 @@ source_set("electron_lib") {
|
||||
} else {
|
||||
libs += [
|
||||
"Squirrel.framework",
|
||||
"ReactiveObjC.framework",
|
||||
"ReactiveCocoa.framework",
|
||||
"Mantle.framework",
|
||||
]
|
||||
|
||||
deps += [
|
||||
"//third_party/squirrel.mac:reactiveobjc_framework+link",
|
||||
"//third_party/squirrel.mac:squirrel_framework+link",
|
||||
cflags_objcc = [
|
||||
"-F",
|
||||
rebase_path("external_binaries", root_build_dir),
|
||||
]
|
||||
|
||||
# ReactiveObjC which is used by Squirrel requires using __weak.
|
||||
cflags_objcc = [ "-fobjc-weak" ]
|
||||
# ReactiveCocoa which is used by Squirrel requires using __weak.
|
||||
cflags_objcc += [ "-fobjc-weak" ]
|
||||
}
|
||||
}
|
||||
if (is_linux) {
|
||||
@@ -603,6 +601,15 @@ source_set("electron_lib") {
|
||||
]
|
||||
}
|
||||
|
||||
if (enable_remote_module) {
|
||||
sources += [
|
||||
"shell/common/api/remote/remote_callback_freer.cc",
|
||||
"shell/common/api/remote/remote_callback_freer.h",
|
||||
"shell/common/api/remote/remote_object_freer.cc",
|
||||
"shell/common/api/remote/remote_object_freer.h",
|
||||
]
|
||||
}
|
||||
|
||||
if (enable_desktop_capturer) {
|
||||
if (is_component_build && !is_linux) {
|
||||
# On windows the implementation relies on unexported
|
||||
@@ -812,6 +819,8 @@ if (is_mac) {
|
||||
}
|
||||
|
||||
ldflags = [
|
||||
"-F",
|
||||
rebase_path("external_binaries", root_build_dir),
|
||||
"-Wl,-install_name,@rpath/$output_name.framework/$output_name",
|
||||
"-rpath",
|
||||
"@loader_path/Libraries",
|
||||
@@ -862,49 +871,17 @@ if (is_mac) {
|
||||
}
|
||||
}
|
||||
|
||||
template("stripped_framework") {
|
||||
action(target_name) {
|
||||
assert(defined(invoker.framework))
|
||||
|
||||
script = "//electron/build/strip_framework.py"
|
||||
|
||||
forward_variables_from(invoker, [ "deps" ])
|
||||
inputs = [ "$root_out_dir/" + invoker.framework ]
|
||||
outputs = [ "$target_out_dir/stripped_frameworks/" + invoker.framework ]
|
||||
|
||||
args = rebase_path(inputs) + rebase_path(outputs)
|
||||
}
|
||||
}
|
||||
|
||||
stripped_framework("stripped_mantle_framework") {
|
||||
framework = "Mantle.framework"
|
||||
deps = [ "//third_party/squirrel.mac:mantle_framework" ]
|
||||
}
|
||||
|
||||
stripped_framework("stripped_reactiveobjc_framework") {
|
||||
framework = "ReactiveObjC.framework"
|
||||
deps = [ "//third_party/squirrel.mac:reactiveobjc_framework" ]
|
||||
}
|
||||
|
||||
stripped_framework("stripped_squirrel_framework") {
|
||||
framework = "Squirrel.framework"
|
||||
deps = [ "//third_party/squirrel.mac:squirrel_framework" ]
|
||||
}
|
||||
|
||||
bundle_data("electron_app_framework_bundle_data") {
|
||||
sources = [ "$root_out_dir/$electron_framework_name.framework" ]
|
||||
if (!is_mas_build) {
|
||||
sources += get_target_outputs(":stripped_mantle_framework") +
|
||||
get_target_outputs(":stripped_reactiveobjc_framework") +
|
||||
get_target_outputs(":stripped_squirrel_framework")
|
||||
sources += [
|
||||
"external_binaries/Mantle.framework",
|
||||
"external_binaries/ReactiveCocoa.framework",
|
||||
"external_binaries/Squirrel.framework",
|
||||
]
|
||||
}
|
||||
outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ]
|
||||
public_deps = [
|
||||
":electron_framework+link",
|
||||
":stripped_mantle_framework",
|
||||
":stripped_reactiveobjc_framework",
|
||||
":stripped_squirrel_framework",
|
||||
]
|
||||
public_deps = [ ":electron_framework+link" ]
|
||||
|
||||
foreach(helper_params, content_mac_helpers) {
|
||||
sources +=
|
||||
@@ -1102,9 +1079,7 @@ if (is_mac) {
|
||||
data += [ "$root_out_dir/resources/default_app.asar" ]
|
||||
}
|
||||
|
||||
if (use_v8_context_snapshot) {
|
||||
public_deps = [ "//tools/v8_context_snapshot:v8_context_snapshot" ]
|
||||
}
|
||||
public_deps = [ "//tools/v8_context_snapshot:v8_context_snapshot" ]
|
||||
|
||||
if (is_win) {
|
||||
sources += [
|
||||
@@ -1212,6 +1187,18 @@ test("shell_browser_ui_unittests") {
|
||||
"//ui/base",
|
||||
"//ui/strings",
|
||||
]
|
||||
|
||||
if (is_mac) {
|
||||
# Resolve paths owing to different test executable locations
|
||||
ldflags = [
|
||||
"-F",
|
||||
rebase_path("external_binaries", root_build_dir),
|
||||
"-rpath",
|
||||
"@loader_path",
|
||||
"-rpath",
|
||||
"@executable_path/" + rebase_path("external_binaries", root_build_dir),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
template("dist_zip") {
|
||||
@@ -1309,13 +1296,10 @@ dist_zip("electron_chromedriver_zip") {
|
||||
|
||||
mksnapshot_deps = [
|
||||
":licenses",
|
||||
"//tools/v8_context_snapshot:v8_context_snapshot_generator($v8_snapshot_toolchain)",
|
||||
"//v8:mksnapshot($v8_snapshot_toolchain)",
|
||||
]
|
||||
|
||||
if (use_v8_context_snapshot) {
|
||||
mksnapshot_deps += [ "//tools/v8_context_snapshot:v8_context_snapshot_generator($v8_snapshot_toolchain)" ]
|
||||
}
|
||||
|
||||
group("electron_mksnapshot") {
|
||||
public_deps = mksnapshot_deps
|
||||
}
|
||||
|
||||
33
DEPS
33
DEPS
@@ -8,18 +8,17 @@ gclient_gn_args = [
|
||||
'checkout_pgo_profiles',
|
||||
'checkout_oculus_sdk',
|
||||
'checkout_openxr',
|
||||
'checkout_google_benchmark'
|
||||
'checkout_google_benchmark',
|
||||
'mac_xcode_version',
|
||||
]
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'9ae03ef8f7d4f6ac663f725bcfe70311987652f3',
|
||||
'85.0.4183.20',
|
||||
'node_version':
|
||||
'v12.18.2',
|
||||
'v12.16.3',
|
||||
'nan_version':
|
||||
'2c4ee8a32a299eada3cd6e468bbd0a473bfea96d',
|
||||
'squirrel.mac_version':
|
||||
'44468f858ce0d25c27bd5e674abfa104e0119738',
|
||||
|
||||
'boto_version': 'f7574aa6cc2c819430c1f05e9a1a1a666ef8169b',
|
||||
'pyyaml_version': '3.12',
|
||||
@@ -31,7 +30,6 @@ vars = {
|
||||
'nodejs_git': 'https://github.com/nodejs',
|
||||
'requests_git': 'https://github.com/kennethreitz',
|
||||
'yaml_git': 'https://github.com/yaml',
|
||||
'squirrel_git': 'https://github.com/Squirrel',
|
||||
|
||||
# KEEP IN SYNC WITH utils.js FILE
|
||||
'yarn_version': '1.15.2',
|
||||
@@ -54,6 +52,8 @@ vars = {
|
||||
# Python "requests" module is used for releases only.
|
||||
'checkout_requests': False,
|
||||
|
||||
'mac_xcode_version': 'default',
|
||||
|
||||
# To allow running hooks without parsing the DEPS tree
|
||||
'process_deps': True,
|
||||
|
||||
@@ -104,18 +104,6 @@ deps = {
|
||||
'url': Var('requests_git') + '/requests.git' + '@' + Var('requests_version'),
|
||||
'condition': 'checkout_requests and process_deps',
|
||||
},
|
||||
'src/third_party/squirrel.mac': {
|
||||
'url': Var("squirrel_git") + '/Squirrel.Mac.git@' + Var("squirrel.mac_version"),
|
||||
'condition': 'process_deps',
|
||||
},
|
||||
'src/third_party/squirrel.mac/vendor/ReactiveObjC': {
|
||||
'url': 'https://github.com/ReactiveCocoa/ReactiveObjC.git@74ab5baccc6f7202c8ac69a8d1e152c29dc1ea76',
|
||||
'condition': 'process_deps'
|
||||
},
|
||||
'src/third_party/squirrel.mac/vendor/Mantle': {
|
||||
'url': 'https://github.com/Mantle/Mantle.git@78d3966b3c331292ea29ec38661b25df0a245948',
|
||||
'condition': 'process_deps',
|
||||
}
|
||||
}
|
||||
|
||||
hooks = [
|
||||
@@ -124,7 +112,7 @@ hooks = [
|
||||
'condition': '(checkout_chromium and apply_patches) and process_deps',
|
||||
'pattern': 'src/electron',
|
||||
'action': [
|
||||
'python3',
|
||||
'python',
|
||||
'src/electron/script/apply_all_patches.py',
|
||||
'src/electron/patches/config.json',
|
||||
],
|
||||
@@ -142,7 +130,7 @@ hooks = [
|
||||
'name': 'electron_npm_deps',
|
||||
'pattern': 'src/electron/package.json',
|
||||
'action': [
|
||||
'python3',
|
||||
'python',
|
||||
'-c',
|
||||
'import os, subprocess; os.chdir(os.path.join("src", "electron")); subprocess.check_call(["python", "script/lib/npx.py", "yarn@' + (Var("yarn_version")) + '", "install", "--frozen-lockfile"]);',
|
||||
],
|
||||
@@ -152,7 +140,7 @@ hooks = [
|
||||
'pattern': 'src/electron',
|
||||
'condition': 'checkout_boto and process_deps',
|
||||
'action': [
|
||||
'python3',
|
||||
'python',
|
||||
'-c',
|
||||
'import os, subprocess; os.chdir(os.path.join("src", "electron", "vendor", "boto")); subprocess.check_call(["python", "setup.py", "build"]);',
|
||||
],
|
||||
@@ -162,7 +150,7 @@ hooks = [
|
||||
'pattern': 'src/electron',
|
||||
'condition': 'checkout_requests and process_deps',
|
||||
'action': [
|
||||
'python3',
|
||||
'python',
|
||||
'-c',
|
||||
'import os, subprocess; os.chdir(os.path.join("src", "electron", "vendor", "requests")); subprocess.check_call(["python", "setup.py", "build"]);',
|
||||
],
|
||||
@@ -171,5 +159,4 @@ hooks = [
|
||||
|
||||
recursedeps = [
|
||||
'src',
|
||||
'src/third_party/squirrel.mac',
|
||||
]
|
||||
|
||||
@@ -1 +1 @@
|
||||
11.0.0-nightly.20200709
|
||||
10.0.0-beta.11
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
version: 1.0.{build}
|
||||
build_cloud: electron-16-core
|
||||
image: vs2019bt-16.4.0
|
||||
image: vs2019bt-16.6.2
|
||||
environment:
|
||||
GIT_CACHE_PATH: C:\Users\electron\libcc_cache
|
||||
ELECTRON_OUT_DIR: Default
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
source = sys.argv[1]
|
||||
dest = sys.argv[2]
|
||||
|
||||
# Ensure any existing framework is removed
|
||||
subprocess.check_output(["rm", "-rf", dest])
|
||||
|
||||
subprocess.check_output(["cp", "-a", source, dest])
|
||||
|
||||
# Strip headers, we do not need to ship them
|
||||
subprocess.check_output(["rm", "-r", os.path.join(dest, 'Headers')])
|
||||
subprocess.check_output(["rm", "-r", os.path.join(dest, 'Versions', 'Current', 'Headers')])
|
||||
@@ -93,6 +93,14 @@ static_library("chrome") {
|
||||
]
|
||||
}
|
||||
|
||||
if (is_win) {
|
||||
sources += [
|
||||
"//chrome/browser/win/icon_reader_service.cc",
|
||||
"//chrome/browser/win/icon_reader_service.h",
|
||||
]
|
||||
public_deps += [ "//chrome/services/util_win:lib" ]
|
||||
}
|
||||
|
||||
if (enable_desktop_capturer) {
|
||||
sources += [
|
||||
"//chrome/browser/media/webrtc/desktop_media_list.h",
|
||||
@@ -153,13 +161,6 @@ static_library("chrome") {
|
||||
}
|
||||
}
|
||||
|
||||
if (enable_tts) {
|
||||
sources += [
|
||||
"//chrome/browser/speech/tts_controller_delegate_impl.cc",
|
||||
"//chrome/browser/speech/tts_controller_delegate_impl.h",
|
||||
]
|
||||
}
|
||||
|
||||
if (enable_widevine) {
|
||||
sources += [
|
||||
"//chrome/renderer/media/chrome_key_systems.cc",
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import * as electron from 'electron';
|
||||
import { app, dialog } from 'electron';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as url from 'url';
|
||||
const { app, dialog } = electron;
|
||||
|
||||
type DefaultAppOptions = {
|
||||
file: null | string;
|
||||
@@ -146,90 +145,13 @@ function startRepl () {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Prevent quitting.
|
||||
// prevent quitting
|
||||
app.on('window-all-closed', () => {});
|
||||
|
||||
const GREEN = '32';
|
||||
const colorize = (color: string, s: string) => `\x1b[${color}m${s}\x1b[0m`;
|
||||
const electronVersion = colorize(GREEN, `v${process.versions.electron}`);
|
||||
const nodeVersion = colorize(GREEN, `v${process.versions.node}`);
|
||||
|
||||
console.info(`
|
||||
Welcome to the Electron.js REPL \\[._.]/
|
||||
|
||||
You can access all Electron.js modules here as well as Node.js modules.
|
||||
Using: Node.js ${nodeVersion} and Electron.js ${electronVersion}
|
||||
`);
|
||||
|
||||
const { REPLServer } = require('repl');
|
||||
const repl = new REPLServer({
|
||||
prompt: '> '
|
||||
}).on('exit', () => {
|
||||
const repl = require('repl');
|
||||
repl.start('> ').on('exit', () => {
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
function defineBuiltin (context: any, name: string, getter: Function) {
|
||||
const setReal = (val: any) => {
|
||||
// Deleting the property before re-assigning it disables the
|
||||
// getter/setter mechanism.
|
||||
delete context[name];
|
||||
context[name] = val;
|
||||
};
|
||||
|
||||
Object.defineProperty(context, name, {
|
||||
get: () => {
|
||||
const lib = getter();
|
||||
|
||||
delete context[name];
|
||||
Object.defineProperty(context, name, {
|
||||
get: () => lib,
|
||||
set: setReal,
|
||||
configurable: true,
|
||||
enumerable: false
|
||||
});
|
||||
|
||||
return lib;
|
||||
},
|
||||
set: setReal,
|
||||
configurable: true,
|
||||
enumerable: false
|
||||
});
|
||||
}
|
||||
|
||||
defineBuiltin(repl.context, 'electron', () => electron);
|
||||
for (const api of Object.keys(electron) as (keyof typeof electron)[]) {
|
||||
defineBuiltin(repl.context, api, () => electron[api]);
|
||||
}
|
||||
|
||||
// Copied from node/lib/repl.js. For better DX, we don't want to
|
||||
// show e.g 'contentTracing' at a higher priority than 'const', so
|
||||
// we only trigger custom tab-completion when no common words are
|
||||
// potentially matches.
|
||||
const commonWords = [
|
||||
'async', 'await', 'break', 'case', 'catch', 'const', 'continue',
|
||||
'debugger', 'default', 'delete', 'do', 'else', 'export', 'false',
|
||||
'finally', 'for', 'function', 'if', 'import', 'in', 'instanceof', 'let',
|
||||
'new', 'null', 'return', 'switch', 'this', 'throw', 'true', 'try',
|
||||
'typeof', 'var', 'void', 'while', 'with', 'yield'
|
||||
];
|
||||
|
||||
const electronBuiltins = [...Object.keys(electron), 'original-fs', 'electron'];
|
||||
|
||||
const defaultComplete = repl.completer;
|
||||
repl.completer = (line: string, callback: Function) => {
|
||||
const lastSpace = line.lastIndexOf(' ');
|
||||
const currentSymbol = line.substring(lastSpace + 1, repl.cursor);
|
||||
|
||||
const filterFn = (c: string) => c.startsWith(currentSymbol);
|
||||
const ignores = commonWords.filter(filterFn);
|
||||
const hits = electronBuiltins.filter(filterFn);
|
||||
|
||||
if (!ignores.length && hits.length) {
|
||||
callback(null, [hits, currentSymbol]);
|
||||
} else {
|
||||
defaultComplete.apply(repl, [line, callback]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Start the specified app if there is one specified in command line, otherwise
|
||||
|
||||
@@ -92,6 +92,7 @@ These individual tutorials expand on topics discussed in the guide above.
|
||||
* Electron Releases & Developer Feedback
|
||||
* [Versioning Policy](tutorial/electron-versioning.md)
|
||||
* [Release Timelines](tutorial/electron-timelines.md)
|
||||
* [App Feedback Program](tutorial/app-feedback-program.md)
|
||||
* [Packaging App Source Code with asar](tutorial/application-packaging.md)
|
||||
* [Generating asar Archives](tutorial/application-packaging.md#generating-asar-archives)
|
||||
* [Using asar Archives](tutorial/application-packaging.md#using-asar-archives)
|
||||
|
||||
@@ -140,16 +140,6 @@ this event, such as launching the application for the first time, attempting
|
||||
to re-launch the application when it's already running, or clicking on the
|
||||
application's dock or taskbar icon.
|
||||
|
||||
### Event: 'did-become-active' _macOS_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
|
||||
Emitted when mac application become active. Difference from `activate` event is
|
||||
that `did-become-active` is emitted every time the app becomes active, not only
|
||||
when Dock icon is clicked or application is re-launched.
|
||||
|
||||
### Event: 'continue-activity' _macOS_
|
||||
|
||||
Returns:
|
||||
@@ -815,20 +805,6 @@ Returns `String` - Name of the application handling the protocol, or an empty
|
||||
This method returns the application name of the default handler for the protocol
|
||||
(aka URI scheme) of a URL.
|
||||
|
||||
### `app.getApplicationInfoForProtocol(url)` _macOS_ _Windows_
|
||||
|
||||
* `url` String - a URL with the protocol name to check. Unlike the other
|
||||
methods in this family, this accepts an entire URL, including `://` at a
|
||||
minimum (e.g. `https://`).
|
||||
|
||||
Returns `Promise<Object>` - Resolve with an object containing the following:
|
||||
* `icon` NativeImage - the display icon of the app handling the protocol.
|
||||
* `path` String - installation path of the app handling the protocol.
|
||||
* `name` String - display name of the app handling the protocol.
|
||||
|
||||
This method returns a promise that contains the application name, icon and path of the default handler for the protocol
|
||||
(aka URI scheme) of a URL.
|
||||
|
||||
### `app.setUserTasks(tasks)` _Windows_
|
||||
|
||||
* `tasks` [Task[]](structures/task.md) - Array of `Task` objects
|
||||
@@ -1246,7 +1222,7 @@ Show the app's about panel options. These options can be overridden with `app.se
|
||||
* `credits` String (optional) _macOS_ _Windows_ - Credit information.
|
||||
* `authors` String[] (optional) _Linux_ - List of app authors.
|
||||
* `website` String (optional) _Linux_ - The app's website.
|
||||
* `iconPath` String (optional) _Linux_ _Windows_ - Path to the app's icon in a JPEG or PNG file format. On Linux, will be shown as 64x64 pixels while retaining aspect ratio.
|
||||
* `iconPath` String (optional) _Linux_ _Windows_ - Path to the app's icon. On Linux, will be shown as 64x64 pixels while retaining aspect ratio.
|
||||
|
||||
Set the about panel options. This will override the values defined in the app's `.plist` file on macOS. See the [Apple docs][about-panel-options] for more details. On Linux, values must be set in order to be shown; there are no defaults.
|
||||
|
||||
|
||||
@@ -692,7 +692,7 @@ Returns `BrowserWindow | null` - The window that owns the given `browserView`. I
|
||||
|
||||
* `id` Integer
|
||||
|
||||
Returns `BrowserWindow | null` - The window with the given `id`.
|
||||
Returns `BrowserWindow` - The window with the given `id`.
|
||||
|
||||
#### `BrowserWindow.addExtension(path)` _Deprecated_
|
||||
|
||||
|
||||
@@ -72,50 +72,6 @@ const constraints = {
|
||||
}
|
||||
```
|
||||
|
||||
This example shows how to capture a video from a [WebContents](web-contents.md)
|
||||
|
||||
```javascript
|
||||
// In the renderer process.
|
||||
const { desktopCapturer, remote } = require('electron')
|
||||
|
||||
desktopCapturer.getMediaSourceIdForWebContents(remote.getCurrentWebContents().id).then(async mediaSourceId => {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
mandatory: {
|
||||
chromeMediaSource: 'tab',
|
||||
chromeMediaSourceId: mediaSourceId
|
||||
}
|
||||
},
|
||||
video: {
|
||||
mandatory: {
|
||||
chromeMediaSource: 'tab',
|
||||
chromeMediaSourceId: mediaSourceId,
|
||||
minWidth: 1280,
|
||||
maxWidth: 1280,
|
||||
minHeight: 720,
|
||||
maxHeight: 720
|
||||
}
|
||||
}
|
||||
})
|
||||
handleStream(stream)
|
||||
} catch (e) {
|
||||
handleError(e)
|
||||
}
|
||||
})
|
||||
|
||||
function handleStream (stream) {
|
||||
const video = document.querySelector('video')
|
||||
video.srcObject = stream
|
||||
video.onloadedmetadata = (e) => video.play()
|
||||
}
|
||||
|
||||
function handleError (e) {
|
||||
console.log(e)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Methods
|
||||
|
||||
The `desktopCapturer` module has the following methods:
|
||||
@@ -138,15 +94,6 @@ Returns `Promise<DesktopCapturerSource[]>` - Resolves with an array of [`Desktop
|
||||
**Note** Capturing the screen contents requires user consent on macOS 10.15 Catalina or higher,
|
||||
which can detected by [`systemPreferences.getMediaAccessStatus`].
|
||||
|
||||
### `desktopCapturer.getMediaSourceIdForWebContents(webContentsId)`
|
||||
|
||||
* `webContentsId` number - Id of the WebContents to get stream of
|
||||
|
||||
Returns `Promise<string>` - Resolves with the identifier of a WebContents stream, this identifier can be
|
||||
used with [`navigator.mediaDevices.getUserMedia`].
|
||||
The identifier is **only valid for 10 seconds**.
|
||||
The identifier may be empty if not requested from a renderer process.
|
||||
|
||||
[`navigator.mediaDevices.getUserMedia`]: https://developer.mozilla.org/en/docs/Web/API/MediaDevices/getUserMedia
|
||||
[`systemPreferences.getMediaAccessStatus`]: system-preferences.md#systempreferencesgetmediaaccessstatusmediatype-macos
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ Appends the `menuItem` to the menu.
|
||||
|
||||
* `id` String
|
||||
|
||||
Returns `MenuItem | null` the item with the specified `id`
|
||||
Returns `MenuItem` the item with the specified `id`
|
||||
|
||||
#### `menu.insert(pos, menuItem)`
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ Process: [Main](../glossary.md#main-process)
|
||||
|
||||
The `powerMonitor` module emits the following events:
|
||||
|
||||
### Event: 'suspend'
|
||||
### Event: 'suspend' _Linux_ _Windows_
|
||||
|
||||
Emitted when the system is suspending.
|
||||
|
||||
### Event: 'resume'
|
||||
### Event: 'resume' _Linux_ _Windows_
|
||||
|
||||
Emitted when system is resuming.
|
||||
|
||||
|
||||
@@ -63,8 +63,8 @@ The `protocol` module has the following methods:
|
||||
module gets emitted and can be called only once.
|
||||
|
||||
Registers the `scheme` as standard, secure, bypasses content security policy for
|
||||
resources, allows registering ServiceWorker, supports fetch API, and streaming
|
||||
video/audio. Specify a privilege with the value of `true` to enable the capability.
|
||||
resources, allows registering ServiceWorker and supports fetch API. Specify a
|
||||
privilege with the value of `true` to enable the capability.
|
||||
|
||||
An example of registering a privileged scheme, that bypasses Content Security
|
||||
Policy:
|
||||
@@ -103,11 +103,6 @@ cookies) are disabled for non standard schemes. So in general if you want to
|
||||
register a custom protocol to replace the `http` protocol, you have to register
|
||||
it as a standard scheme.
|
||||
|
||||
Protocols that use streams (http and stream protocols) should set `stream: true`.
|
||||
The `<video>` and `<audio>` HTML elements expect protocols to buffer their
|
||||
responses by default. The `stream` flag configures those elements to correctly
|
||||
expect streaming responses.
|
||||
|
||||
### `protocol.registerFileProtocol(scheme, handler)`
|
||||
|
||||
* `scheme` String
|
||||
|
||||
@@ -39,7 +39,7 @@ Returns:
|
||||
* `message` String - The actual console message
|
||||
* `versionId` Number - The version ID of the service worker that sent the log message
|
||||
* `source` String - The type of source for this message. Can be `javascript`, `xml`, `network`, `console-api`, `storage`, `app-cache`, `rendering`, `security`, `deprecation`, `worker`, `violation`, `intervention`, `recommendation` or `other`.
|
||||
* `level` Number - The log level, from 0 to 3. In order it matches `verbose`, `info`, `warning` and `error`.
|
||||
* `level` Number - The log level, from 0 to 3. In order it matches `verbose`, `info`, `warning` and `error`.
|
||||
* `sourceUrl` String - The URL the message came from
|
||||
* `lineNumber` Number - The line number of the source that triggered this console message
|
||||
|
||||
|
||||
@@ -8,4 +8,3 @@
|
||||
* `allowServiceWorkers` Boolean (optional) - Default false.
|
||||
* `supportFetchAPI` Boolean (optional) - Default false.
|
||||
* `corsEnabled` Boolean (optional) - Default false.
|
||||
* `stream` Boolean (optional) - Default false.
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
* `Pepper Plugin`
|
||||
* `Pepper Plugin Broker`
|
||||
* `Unknown`
|
||||
* `name` String (optional) - The name of the process. i.e. for plugins it might be Flash.
|
||||
Examples for utility: `Audio Service`, `Content Decryption Module Service`, `Network Service`, `Video Capture`, etc.
|
||||
* `cpu` [CPUUsage](cpu-usage.md) - CPU usage of the process.
|
||||
* `creationTime` Number - Creation time for this process.
|
||||
The time is represented as number of milliseconds since epoch.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* `trace_buffer_size_in_events` number (optional) - maximum size of the trace
|
||||
recording buffer in events.
|
||||
* `enable_argument_filter` boolean (optional) - if true, filter event data
|
||||
according to a specific list of events that have been manually vetted to not
|
||||
according to a whitelist of events that have been manually vetted to not
|
||||
include any PII. See [the implementation in
|
||||
Chromium][trace_event_args_whitelist.cc] for specifics.
|
||||
* `included_categories` String[] (optional) - a list of tracing categories to
|
||||
|
||||
@@ -738,9 +738,9 @@ Emitted when a `<webview>` has been attached to this web contents.
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `level` Integer - The log level, from 0 to 3. In order it matches `verbose`, `info`, `warning` and `error`.
|
||||
* `message` String - The actual console message
|
||||
* `line` Integer - The line number of the source that triggered this console message
|
||||
* `level` Integer
|
||||
* `message` String
|
||||
* `line` Integer
|
||||
* `sourceId` String
|
||||
|
||||
Emitted when the associated window logs a console message.
|
||||
@@ -1371,6 +1371,8 @@ An example of `webContents.printToPDF`:
|
||||
```javascript
|
||||
const { BrowserWindow } = require('electron')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const os = require('os')
|
||||
|
||||
let win = new BrowserWindow({ width: 800, height: 600 })
|
||||
win.loadURL('http://github.com')
|
||||
@@ -1378,12 +1380,13 @@ win.loadURL('http://github.com')
|
||||
win.webContents.on('did-finish-load', () => {
|
||||
// Use default printing options
|
||||
win.webContents.printToPDF({}).then(data => {
|
||||
fs.writeFile('/tmp/print.pdf', data, (error) => {
|
||||
const pdfPath = path.join(os.homedir(), 'Desktop', 'temp.pdf')
|
||||
fs.writeFile(pdfPath, data, (error) => {
|
||||
if (error) throw error
|
||||
console.log('Write PDF successfully.')
|
||||
console.log(`Wrote PDF successfully to ${pdfPath}`)
|
||||
})
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
console.log(`Failed to write PDF to ${pdfPath}: `, error)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
@@ -755,9 +755,9 @@ Fired when page leaves fullscreen triggered by HTML API.
|
||||
|
||||
Returns:
|
||||
|
||||
* `level` Integer - The log level, from 0 to 3. In order it matches `verbose`, `info`, `warning` and `error`.
|
||||
* `message` String - The actual console message
|
||||
* `line` Integer - The line number of the source that triggered this console message
|
||||
* `level` Integer
|
||||
* `message` String
|
||||
* `line` Integer
|
||||
* `sourceId` String
|
||||
|
||||
Fired when the guest window logs a console message.
|
||||
|
||||
@@ -14,15 +14,6 @@ This document uses the following convention to categorize breaking changes:
|
||||
|
||||
## Planned Breaking API Changes (12.0)
|
||||
|
||||
### Default Changed: `contextIsolation` defaults to `true`
|
||||
|
||||
In Electron 12, `contextIsolation` will be enabled by default. To restore
|
||||
the previous behavior, `contextIsolation: false` must be specified in WebPreferences.
|
||||
|
||||
We [recommend having contextIsolation enabled](https://github.com/electron/electron/blob/master/docs/tutorial/security.md#3-enable-context-isolation-for-remote-content) for the security of your application.
|
||||
|
||||
For more details see: https://github.com/electron/electron/issues/23506
|
||||
|
||||
### Removed: `crashReporter` methods in the renderer process
|
||||
|
||||
The following `crashReporter` methods are no longer available in the renderer
|
||||
|
||||
@@ -23,3 +23,4 @@ For guides on Electron app development, see
|
||||
* [Debugging on macOS](debugging-instructions-macos.md)
|
||||
* [Setting Up Symbol Server in Debugger](setting-up-symbol-server.md)
|
||||
* [Patches](patches.md)
|
||||
* [Upgrading Node](upgrading-node.md)
|
||||
|
||||
@@ -10,10 +10,6 @@ Check the build prerequisites for your platform before proceeding
|
||||
* [Linux](build-instructions-linux.md#prerequisites)
|
||||
* [Windows](build-instructions-windows.md#prerequisites)
|
||||
|
||||
## Build Tools
|
||||
|
||||
[Electron's Build Tools](https://github.com/electron/build-tools) automate much of the setup for compiling Electron from source with different configurations and build targets. If you wish to set up the environment manually, the instructions are listed below.
|
||||
|
||||
## GN prerequisites
|
||||
|
||||
You'll need to install [`depot_tools`][depot-tools], the toolset
|
||||
|
||||
121
docs/development/upgrading-node.md
Normal file
121
docs/development/upgrading-node.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# Upgrading Node
|
||||
|
||||
## Discussion
|
||||
|
||||
Chromium and Node.js both depend on V8, and Electron contains
|
||||
only a single copy of V8, so it's important to ensure that the
|
||||
version of V8 chosen is compatible with the build's version of
|
||||
Node.js and Chromium.
|
||||
|
||||
Upgrading Node is much easier than upgrading Chromium, so fewer
|
||||
conflicts arise if one upgrades Chromium first, then chooses the
|
||||
upstream Node release whose version of V8 is closest to the one
|
||||
Chromium contains.
|
||||
|
||||
Electron has its own [Node fork](https://github.com/electron/node)
|
||||
with modifications for the V8 build details mentioned above
|
||||
and for exposing API needed by Electron. Once an upstream Node
|
||||
release is chosen, it's placed in a branch in Electron's Node fork
|
||||
and any Electron Node patches are applied there.
|
||||
|
||||
Another factor is that the Node project patches its version of V8.
|
||||
As mentioned above, Electron builds everything with a single copy
|
||||
of V8, so Node's V8 patches must be ported to that copy.
|
||||
|
||||
Once all of Electron's dependencies are building and using the same
|
||||
copy of V8, the next step is to fix any Electron code issues caused
|
||||
by the Node upgrade.
|
||||
|
||||
[FIXME] something about a Node debugger in Atom that we (e.g. deepak)
|
||||
use and need to confirm doesn't break with the Node upgrade?
|
||||
|
||||
So in short, the primary steps are:
|
||||
|
||||
1. Update Electron's Node fork to the desired version
|
||||
2. Backport Node's V8 patches to our copy of V8
|
||||
3. Update the GN build files, porting changes from node's GYP files
|
||||
4. Update Electron's DEPS to use new version of Node
|
||||
|
||||
## Updating Electron's Node [fork](https://github.com/electron/node)
|
||||
|
||||
1. Ensure that `master` on `electron/node` has updated release tags from `nodejs/node`
|
||||
2. Create a branch in https://github.com/electron/node: `electron-node-vX.X.X` where the base that you're branching from is the tag for the desired update
|
||||
- `vX.X.X` Must use a version of Node compatible with our current version of Chromium
|
||||
3. Re-apply our commits from the previous version of Node we were using (`vY.Y.Y`) to `v.X.X.X`
|
||||
- Check release tag and select the range of commits we need to re-apply
|
||||
- Cherry-pick commit range:
|
||||
1. Checkout both `vY.Y.Y` & `v.X.X.X`
|
||||
2. `git cherry-pick FIRST_COMMIT_HASH..LAST_COMMIT_HASH`
|
||||
- Resolve merge conflicts in each file encountered, then:
|
||||
1. `git add <conflict-file>`
|
||||
2. `git cherry-pick --continue`
|
||||
3. Repeat until finished
|
||||
|
||||
|
||||
## Updating [V8](https://github.com/electron/node/src/V8) Patches
|
||||
|
||||
We need to generate a patch file from each patch that Node
|
||||
applies to V8.
|
||||
|
||||
```sh
|
||||
$ cd third_party/electron_node
|
||||
$ CURRENT_NODE_VERSION=vX.Y.Z
|
||||
# Find the last commit with the message "deps: update V8 to <some version>"
|
||||
# This commit corresponds to Node resetting V8 to its pristine upstream
|
||||
# state at the stated version.
|
||||
$ LAST_V8_UPDATE="$(git log --grep='^deps: update V8' --format='%H' -1 deps/v8)"
|
||||
# This creates a patch file containing all changes in deps/v8 from
|
||||
# $LAST_V8_UPDATE up to the current Node version, formatted in a way that
|
||||
# it will apply cleanly to the V8 repository (i.e. with `deps/v8`
|
||||
# stripped off the path and excluding the v8/gypfiles directory, which
|
||||
# isn't present in V8.
|
||||
$ git format-patch \
|
||||
--relative=deps/v8 \
|
||||
$LAST_V8_UPDATE..$CURRENT_NODE_VERSION \
|
||||
deps/v8 \
|
||||
':(exclude)deps/v8/gypfiles' \
|
||||
--stdout \
|
||||
> ../../electron/common/patches/v8/node_v8_patches.patch
|
||||
```
|
||||
|
||||
This list of patches will probably include one that claims to
|
||||
make the V8 API backwards-compatible with a previous version of
|
||||
V8. Unfortunately, those patches almost always change the V8 API
|
||||
in a way that is incompatible with Chromium.
|
||||
|
||||
It's usually easier to update Node to work without the
|
||||
compatibility patch than to update Chromium to work with the
|
||||
compatibility patch, so it's recommended to revert the
|
||||
compatibility patch and fix any errors that arise when compiling
|
||||
Node.
|
||||
|
||||
## Update Electron's `DEPS` file
|
||||
|
||||
Update the `DEPS` file in the root of
|
||||
[electron/electron](https://github.com/electron/electron) to
|
||||
point to the git hash of the updated Node.
|
||||
|
||||
## Notes
|
||||
|
||||
- Node maintains its own fork of V8
|
||||
- They backport a small amount of things as needed
|
||||
- Documentation in Node about how [they work with V8](https://nodejs.org/api/v8.html)
|
||||
- We update code such that we only use one copy of V8 across all of Electron
|
||||
- E.g Electron, Chromium, and Node.js
|
||||
- We don’t track upstream closely due to logistics:
|
||||
- Upstream uses multiple repos and so merging into a single repo
|
||||
would result in lost history. So we only update when we’re planning
|
||||
a Node version bump in Electron.
|
||||
- Chromium is large and time-consuming to update, so we typically
|
||||
choose the Node version based on which of its releases has a version
|
||||
of V8 that’s closest to the version in Chromium that we’re using.
|
||||
- We sometimes have to wait for the next periodic Node release
|
||||
because it will sync more closely with the version of V8 in the new Chromium
|
||||
- Electron keeps all its patches in the repo because it’s simpler than
|
||||
maintaining different repos for patches for each upstream project.
|
||||
- Crashpad, Node.js, Chromium, Skia etc. patches are all kept in the same place
|
||||
- Building Node:
|
||||
- We maintain our own GN build files for Node.js to make it easier to ensure
|
||||
that eevrything is built with the same compiler flags.
|
||||
This means that every time we upgrade Node.js we have to do a modest amount of
|
||||
work to synchronize the GN files with the upstream GYP files.
|
||||
3
docs/tutorial/app-feedback-program.md
Normal file
3
docs/tutorial/app-feedback-program.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Electron App Feedback Program
|
||||
|
||||
Electron is working on building a streamlined release process and having faster releases. To help with that, we have the App Feedback Program for large-scale Electron apps to test our beta releases and report app-specific issues to the Electron team. We use this program to help us prioritize work and get applications upgraded to the next stable release as soon as possible. There are a few requirements we expect from participants, such as attending short, online weekly check-ins. Please visit the [blog post](https://electronjs.org/blog/app-feedback-program) for details and sign-up.
|
||||
@@ -100,25 +100,6 @@ To see all of this in action, check out Electron Fiddle's source code,
|
||||
[especially its `electron-forge` configuration
|
||||
file](https://github.com/electron/fiddle/blob/master/forge.config.js).
|
||||
|
||||
If you plan to access the microphone or camera within your app using Electron's APIs, you'll also
|
||||
need to add the following entitlements:
|
||||
|
||||
```xml
|
||||
<key>com.apple.security.device.audio-input</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
```
|
||||
|
||||
If these are not present in your app's entitlements when you invoke, for example:
|
||||
|
||||
```js
|
||||
const { systemPreferences } = require('electron')
|
||||
|
||||
const microphone = systemPreferences.askForMediaAccess('microphone')
|
||||
```
|
||||
|
||||
Your app may crash. See the Resource Access section in [Hardened Runtime](https://developer.apple.com/documentation/security/hardened_runtime) for more information and entitlements you may need.
|
||||
|
||||
## `electron-builder`
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ e.g. `2.0.1`.
|
||||
|
||||
Specifically, the above means:
|
||||
|
||||
1. Admitting non-breaking-API changes before Week 3 in the beta cycle is okay, even if those changes have the potential to cause moderate side-effects
|
||||
1. Admitting non-breaking-API changes before Week 3 in the beta cycle is okay, even if those changes have the potential to cause moderate side-affects
|
||||
2. Admitting feature-flagged changes, that do not otherwise alter existing code paths, at most points in the beta cycle is okay. Users can explicitly enable those flags in their apps.
|
||||
3. Admitting features of any sort after Week 3 in the beta cycle is 👎 without a very good reason.
|
||||
|
||||
@@ -139,7 +139,7 @@ We seek to increase clarity at all levels of the update and releases process. St
|
||||
* Commits that would result in a semver **minor** bump must start with `feat:`.
|
||||
* Commits that would result in a semver **patch** bump must start with `fix:`.
|
||||
|
||||
* We allow squashing of commits, provided that the squashed message adheres to the above message format.
|
||||
* We allow squashing of commits, provided that the squashed message adheres the the above message format.
|
||||
* It is acceptable for some commits in a pull request to not include a semantic prefix, as long as the pull request title contains a meaningful encompassing semantic message.
|
||||
|
||||
# Versioned `master`
|
||||
|
||||
@@ -44,10 +44,23 @@ run the main process. An example of your `package.json` might look like this:
|
||||
```
|
||||
|
||||
__Note__: If the `main` field is not present in `package.json`, Electron will
|
||||
attempt to load an `index.js` (as Node.js does).
|
||||
attempt to load an `index.js` (as Node.js does). If this was actually
|
||||
a simple Node application, you would add a `start` script that instructs `node`
|
||||
to execute the current package:
|
||||
|
||||
By default, `npm start` would run the main script with Node.js. in order to make
|
||||
it run with Electron, you can add a `start` script:
|
||||
```json
|
||||
{
|
||||
"name": "your-app",
|
||||
"version": "0.1.0",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"start": "node ."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Turning this Node application into an Electron application is quite simple - we
|
||||
merely replace the `node` runtime with the `electron` runtime.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -141,10 +154,10 @@ function createWindow () {
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
// Quit when all windows are closed, except on macOS. There, it's common
|
||||
// for applications and their menu bar to stay active until the user quits
|
||||
// explicitly with Cmd + Q.
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', () => {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
|
||||
@@ -75,4 +75,9 @@
|
||||
<message name="IDS_ACCEPT_LANGUAGES" use_name_for_id="true">
|
||||
en-US,en
|
||||
</message>
|
||||
<if expr="is_win">
|
||||
<message name="IDS_UTILITY_PROCESS_UTILITY_WIN_NAME" desc="The name of the utility process used to handle Windows utility operations.">
|
||||
Windows Utilities
|
||||
</message>
|
||||
</if>
|
||||
</grit-part>
|
||||
|
||||
@@ -135,12 +135,13 @@ auto_filenames = {
|
||||
|
||||
sandbox_bundle_deps = [
|
||||
"lib/browser/api/module-names.ts",
|
||||
"lib/common/api/clipboard.ts",
|
||||
"lib/common/api/clipboard.js",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
"lib/common/api/native-image.js",
|
||||
"lib/common/api/shell.js",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
@@ -148,7 +149,7 @@ auto_filenames = {
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/desktop-capturer.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/remote.ts",
|
||||
"lib/renderer/api/remote.js",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
@@ -165,65 +166,62 @@ auto_filenames = {
|
||||
"lib/renderer/window-setup.ts",
|
||||
"lib/sandboxed_renderer/api/exports/electron.ts",
|
||||
"lib/sandboxed_renderer/api/module-list.ts",
|
||||
"lib/sandboxed_renderer/init.ts",
|
||||
"lib/sandboxed_renderer/init.js",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
isolated_bundle_deps = [
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/isolated_renderer/init.js",
|
||||
"lib/renderer/web-view/web-view-constants.ts",
|
||||
"lib/renderer/web-view/web-view-element.ts",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
browser_bundle_deps = [
|
||||
"lib/browser/api/app.ts",
|
||||
"lib/browser/api/auto-updater.ts",
|
||||
"lib/browser/api/auto-updater/auto-updater-native.ts",
|
||||
"lib/browser/api/auto-updater/auto-updater-win.ts",
|
||||
"lib/browser/api/auto-updater/squirrel-update-win.ts",
|
||||
"lib/browser/api/base-window.ts",
|
||||
"lib/browser/api/browser-view.ts",
|
||||
"lib/browser/api/browser-window.ts",
|
||||
"lib/browser/api/content-tracing.ts",
|
||||
"lib/browser/api/auto-updater.js",
|
||||
"lib/browser/api/auto-updater/auto-updater-native.js",
|
||||
"lib/browser/api/auto-updater/auto-updater-win.js",
|
||||
"lib/browser/api/auto-updater/squirrel-update-win.js",
|
||||
"lib/browser/api/browser-view.js",
|
||||
"lib/browser/api/browser-window.js",
|
||||
"lib/browser/api/content-tracing.js",
|
||||
"lib/browser/api/crash-reporter.ts",
|
||||
"lib/browser/api/desktop-capturer.ts",
|
||||
"lib/browser/api/dialog.ts",
|
||||
"lib/browser/api/dialog.js",
|
||||
"lib/browser/api/exports/electron.ts",
|
||||
"lib/browser/api/global-shortcut.ts",
|
||||
"lib/browser/api/in-app-purchase.ts",
|
||||
"lib/browser/api/global-shortcut.js",
|
||||
"lib/browser/api/in-app-purchase.js",
|
||||
"lib/browser/api/ipc-main.ts",
|
||||
"lib/browser/api/menu-item-roles.ts",
|
||||
"lib/browser/api/menu-item.ts",
|
||||
"lib/browser/api/menu-utils.ts",
|
||||
"lib/browser/api/menu.ts",
|
||||
"lib/browser/api/menu-item-roles.js",
|
||||
"lib/browser/api/menu-item.js",
|
||||
"lib/browser/api/menu-utils.js",
|
||||
"lib/browser/api/menu.js",
|
||||
"lib/browser/api/message-channel.ts",
|
||||
"lib/browser/api/module-list.ts",
|
||||
"lib/browser/api/native-theme.ts",
|
||||
"lib/browser/api/net-log.ts",
|
||||
"lib/browser/api/net-log.js",
|
||||
"lib/browser/api/net.ts",
|
||||
"lib/browser/api/notification.ts",
|
||||
"lib/browser/api/notification.js",
|
||||
"lib/browser/api/power-monitor.ts",
|
||||
"lib/browser/api/power-save-blocker.ts",
|
||||
"lib/browser/api/power-save-blocker.js",
|
||||
"lib/browser/api/protocol.ts",
|
||||
"lib/browser/api/screen.ts",
|
||||
"lib/browser/api/session.ts",
|
||||
"lib/browser/api/session.js",
|
||||
"lib/browser/api/system-preferences.ts",
|
||||
"lib/browser/api/top-level-window.js",
|
||||
"lib/browser/api/touch-bar.js",
|
||||
"lib/browser/api/tray.ts",
|
||||
"lib/browser/api/view.ts",
|
||||
"lib/browser/api/views/image-view.ts",
|
||||
"lib/browser/api/web-contents-view.ts",
|
||||
"lib/browser/api/web-contents.ts",
|
||||
"lib/browser/chrome-extension-shim.ts",
|
||||
"lib/browser/api/tray.js",
|
||||
"lib/browser/api/view.js",
|
||||
"lib/browser/api/views/image-view.js",
|
||||
"lib/browser/api/web-contents-view.js",
|
||||
"lib/browser/api/web-contents.js",
|
||||
"lib/browser/chrome-extension-shim.js",
|
||||
"lib/browser/default-menu.ts",
|
||||
"lib/browser/desktop-capturer.ts",
|
||||
"lib/browser/devtools.ts",
|
||||
@@ -234,17 +232,18 @@ auto_filenames = {
|
||||
"lib/browser/ipc-main-internal-utils.ts",
|
||||
"lib/browser/ipc-main-internal.ts",
|
||||
"lib/browser/message-port-main.ts",
|
||||
"lib/browser/navigation-controller.ts",
|
||||
"lib/browser/navigation-controller.js",
|
||||
"lib/browser/remote/objects-registry.ts",
|
||||
"lib/browser/remote/server.ts",
|
||||
"lib/browser/rpc-server.ts",
|
||||
"lib/browser/rpc-server.js",
|
||||
"lib/browser/utils.ts",
|
||||
"lib/common/api/clipboard.ts",
|
||||
"lib/common/api/clipboard.js",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
"lib/common/api/native-image.js",
|
||||
"lib/common/api/shell.js",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/parse-features-string.ts",
|
||||
"lib/common/reset-search-paths.ts",
|
||||
@@ -256,18 +255,17 @@ auto_filenames = {
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
renderer_bundle_deps = [
|
||||
"lib/browser/api/module-names.ts",
|
||||
"lib/common/api/clipboard.ts",
|
||||
"lib/common/api/clipboard.js",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
"lib/common/api/native-image.js",
|
||||
"lib/common/api/shell.js",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/reset-search-paths.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
@@ -279,7 +277,7 @@ auto_filenames = {
|
||||
"lib/renderer/api/exports/electron.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.ts",
|
||||
"lib/renderer/api/remote.ts",
|
||||
"lib/renderer/api/remote.js",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/init.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
@@ -299,18 +297,17 @@ auto_filenames = {
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
worker_bundle_deps = [
|
||||
"lib/browser/api/module-names.ts",
|
||||
"lib/common/api/clipboard.ts",
|
||||
"lib/common/api/clipboard.js",
|
||||
"lib/common/api/deprecate.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
"lib/common/api/native-image.js",
|
||||
"lib/common/api/shell.js",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/electron-binding-setup.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/reset-search-paths.ts",
|
||||
"lib/common/type-utils.ts",
|
||||
@@ -321,7 +318,7 @@ auto_filenames = {
|
||||
"lib/renderer/api/exports/electron.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.ts",
|
||||
"lib/renderer/api/remote.ts",
|
||||
"lib/renderer/api/remote.js",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
@@ -331,7 +328,5 @@ auto_filenames = {
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -44,8 +44,6 @@ filenames = {
|
||||
"shell/browser/api/electron_api_app_mac.mm",
|
||||
"shell/browser/api/electron_api_auto_updater.cc",
|
||||
"shell/browser/api/electron_api_auto_updater.h",
|
||||
"shell/browser/api/electron_api_base_window.cc",
|
||||
"shell/browser/api/electron_api_base_window.h",
|
||||
"shell/browser/api/electron_api_browser_view.cc",
|
||||
"shell/browser/api/electron_api_browser_view.h",
|
||||
"shell/browser/api/electron_api_browser_window.cc",
|
||||
@@ -104,6 +102,8 @@ filenames = {
|
||||
"shell/browser/api/electron_api_system_preferences.h",
|
||||
"shell/browser/api/electron_api_system_preferences_mac.mm",
|
||||
"shell/browser/api/electron_api_system_preferences_win.cc",
|
||||
"shell/browser/api/electron_api_top_level_window.cc",
|
||||
"shell/browser/api/electron_api_top_level_window.h",
|
||||
"shell/browser/api/electron_api_tray.cc",
|
||||
"shell/browser/api/electron_api_tray.h",
|
||||
"shell/browser/api/electron_api_url_loader.cc",
|
||||
|
||||
@@ -4,8 +4,8 @@ import * as path from 'path';
|
||||
import { deprecate, Menu } from 'electron';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
const bindings = process._linkedBinding('electron_browser_app');
|
||||
const commandLine = process._linkedBinding('electron_common_command_line');
|
||||
const bindings = process.electronBinding('app');
|
||||
const commandLine = process.electronBinding('command_line');
|
||||
const { app, App } = bindings;
|
||||
|
||||
// Only one app object permitted.
|
||||
|
||||
7
lib/browser/api/auto-updater.js
Normal file
7
lib/browser/api/auto-updater.js
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
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');
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
if (process.platform === 'win32') {
|
||||
module.exports = require('./auto-updater/auto-updater-win');
|
||||
} else {
|
||||
module.exports = require('./auto-updater/auto-updater-native');
|
||||
}
|
||||
10
lib/browser/api/auto-updater/auto-updater-native.js
Normal file
10
lib/browser/api/auto-updater/auto-updater-native.js
Normal file
@@ -0,0 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
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);
|
||||
|
||||
module.exports = autoUpdater;
|
||||
@@ -1,8 +0,0 @@
|
||||
import { EventEmitter } from 'events';
|
||||
const { autoUpdater, AutoUpdater } = process._linkedBinding('electron_browser_auto_updater');
|
||||
|
||||
// AutoUpdater is an EventEmitter.
|
||||
Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(autoUpdater);
|
||||
|
||||
export default autoUpdater;
|
||||
@@ -1,14 +1,13 @@
|
||||
import { app } from 'electron';
|
||||
import { EventEmitter } from 'events';
|
||||
import * as squirrelUpdate from './squirrel-update-win';
|
||||
'use strict';
|
||||
|
||||
const { app } = require('electron');
|
||||
const { EventEmitter } = require('events');
|
||||
const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win');
|
||||
|
||||
class AutoUpdater extends EventEmitter {
|
||||
updateAvailable: boolean = false;
|
||||
updateURL: string | null = null;
|
||||
|
||||
quitAndInstall () {
|
||||
if (!this.updateAvailable) {
|
||||
return this.emitError(new Error('No update available, can\'t quit and install'));
|
||||
return this.emitError('No update available, can\'t quit and install');
|
||||
}
|
||||
squirrelUpdate.processStart();
|
||||
app.quit();
|
||||
@@ -18,8 +17,8 @@ class AutoUpdater extends EventEmitter {
|
||||
return this.updateURL;
|
||||
}
|
||||
|
||||
setFeedURL (options: { url: string } | string) {
|
||||
let updateURL: string;
|
||||
setFeedURL (options) {
|
||||
let updateURL;
|
||||
if (typeof options === 'object') {
|
||||
if (typeof options.url === 'string') {
|
||||
updateURL = options.url;
|
||||
@@ -35,15 +34,14 @@ class AutoUpdater extends EventEmitter {
|
||||
}
|
||||
|
||||
checkForUpdates () {
|
||||
const url = this.updateURL;
|
||||
if (!url) {
|
||||
return this.emitError(new Error('Update URL is not set'));
|
||||
if (!this.updateURL) {
|
||||
return this.emitError('Update URL is not set');
|
||||
}
|
||||
if (!squirrelUpdate.supported()) {
|
||||
return this.emitError(new Error('Can not find Squirrel'));
|
||||
return this.emitError('Can not find Squirrel');
|
||||
}
|
||||
this.emit('checking-for-update');
|
||||
squirrelUpdate.checkForUpdate(url, (error, update) => {
|
||||
squirrelUpdate.checkForUpdate(this.updateURL, (error, update) => {
|
||||
if (error != null) {
|
||||
return this.emitError(error);
|
||||
}
|
||||
@@ -52,7 +50,7 @@ class AutoUpdater extends EventEmitter {
|
||||
}
|
||||
this.updateAvailable = true;
|
||||
this.emit('update-available');
|
||||
squirrelUpdate.update(url, (error) => {
|
||||
squirrelUpdate.update(this.updateURL, (error) => {
|
||||
if (error != null) {
|
||||
return this.emitError(error);
|
||||
}
|
||||
@@ -68,9 +66,9 @@ class AutoUpdater extends EventEmitter {
|
||||
|
||||
// Private: Emit both error object and message, this is to keep compatibility
|
||||
// with Old APIs.
|
||||
emitError (error: Error) {
|
||||
this.emit('error', error, error.message);
|
||||
emitError (message) {
|
||||
this.emit('error', new Error(message), message);
|
||||
}
|
||||
}
|
||||
|
||||
export default new AutoUpdater();
|
||||
module.exports = new AutoUpdater();
|
||||
@@ -1,6 +1,8 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { spawn, ChildProcessWithoutNullStreams } from 'child_process';
|
||||
'use strict';
|
||||
|
||||
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);
|
||||
@@ -8,15 +10,15 @@ 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: string[] = [];
|
||||
let spawnedProcess: ChildProcessWithoutNullStreams | undefined;
|
||||
let spawnedArgs = [];
|
||||
let spawnedProcess;
|
||||
|
||||
const isSameArgs = (args: string[]) => 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: string[], detached: boolean, callback: Function) {
|
||||
let error: Error, errorEmitted: boolean, stderr: string, stdout: string;
|
||||
const spawnUpdate = function (args, detached, callback) {
|
||||
let error, errorEmitted, stderr, stdout;
|
||||
|
||||
try {
|
||||
// Ensure we don't spawn multiple squirrel processes
|
||||
@@ -77,13 +79,13 @@ const spawnUpdate = function (args: string[], detached: boolean, callback: Funct
|
||||
};
|
||||
|
||||
// Start an instance of the installed app.
|
||||
export function processStart () {
|
||||
exports.processStart = function () {
|
||||
return spawnUpdate(['--processStartAndWait', exeName], true, function () {});
|
||||
}
|
||||
};
|
||||
|
||||
// Download the releases specified by the URL and write new results to stdout.
|
||||
export function checkForUpdate (updateURL: string, callback: (error: Error | null, update?: any) => void) {
|
||||
return spawnUpdate(['--checkForUpdate', updateURL], false, function (error: Error, stdout: string) {
|
||||
exports.checkForUpdate = function (updateURL, callback) {
|
||||
return spawnUpdate(['--checkForUpdate', updateURL], false, function (error, stdout) {
|
||||
let ref, ref1, update;
|
||||
if (error != null) {
|
||||
return callback(error);
|
||||
@@ -91,27 +93,27 @@ export function checkForUpdate (updateURL: string, callback: (error: Error | nul
|
||||
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() : undefined : undefined : undefined;
|
||||
update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : undefined : undefined : undefined;
|
||||
} catch {
|
||||
// Disabled for backwards compatibility:
|
||||
// eslint-disable-next-line standard/no-callback-literal
|
||||
return callback(new Error(`Invalid result:\n${stdout}`));
|
||||
return callback(`Invalid result:\n${stdout}`);
|
||||
}
|
||||
return callback(null, update);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Update the application to the latest remote version specified by URL.
|
||||
export function update (updateURL: string, callback: (error: Error) => void) {
|
||||
exports.update = function (updateURL, callback) {
|
||||
return spawnUpdate(['--update', updateURL], false, callback);
|
||||
}
|
||||
};
|
||||
|
||||
// Is the Update.exe installed with the current application?
|
||||
export function supported () {
|
||||
exports.supported = function () {
|
||||
try {
|
||||
fs.accessSync(updateExe, fs.constants.R_OK);
|
||||
fs.accessSync(updateExe, fs.R_OK);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
16
lib/browser/api/browser-view.js
Normal file
16
lib/browser/api/browser-view.js
Normal file
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
const { EventEmitter } = require('events');
|
||||
const { BrowserView } = process.electronBinding('browser_view');
|
||||
|
||||
Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype);
|
||||
|
||||
BrowserView.fromWebContents = (webContents) => {
|
||||
for (const view of BrowserView.getAllViews()) {
|
||||
if (view.webContents.equal(webContents)) return view;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
module.exports = BrowserView;
|
||||
@@ -1,15 +0,0 @@
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
const { BrowserView } = process._linkedBinding('electron_browser_browser_view');
|
||||
|
||||
Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype);
|
||||
|
||||
BrowserView.fromWebContents = (webContents: Electron.WebContents) => {
|
||||
for (const view of BrowserView.getAllViews()) {
|
||||
if (view.webContents.equal(webContents)) return view;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default BrowserView;
|
||||
168
lib/browser/api/browser-window.js
Normal file
168
lib/browser/api/browser-window.js
Normal file
@@ -0,0 +1,168 @@
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const { TopLevelWindow, deprecate } = electron;
|
||||
const { BrowserWindow } = process.electronBinding('window');
|
||||
|
||||
Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype);
|
||||
|
||||
BrowserWindow.prototype._init = function () {
|
||||
// Call parent class's _init.
|
||||
TopLevelWindow.prototype._init.call(this);
|
||||
|
||||
// Avoid recursive require.
|
||||
const { app } = electron;
|
||||
|
||||
const nativeSetBounds = this.setBounds;
|
||||
this.setBounds = (bounds, ...opts) => {
|
||||
bounds = {
|
||||
...this.getBounds(),
|
||||
...bounds
|
||||
};
|
||||
nativeSetBounds.call(this, bounds, ...opts);
|
||||
};
|
||||
|
||||
// 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
|
||||
// focus it when we first start to load URL, if we do it earlier it won't
|
||||
// have effect, if we do it later we might move focus in the page.
|
||||
//
|
||||
// Though this hack is only needed on macOS when the app is launched from
|
||||
// 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();
|
||||
});
|
||||
|
||||
// Redirect focus/blur event to app instance too.
|
||||
this.on('blur', (event) => {
|
||||
app.emit('browser-window-blur', event, this);
|
||||
});
|
||||
this.on('focus', (event) => {
|
||||
app.emit('browser-window-focus', event, this);
|
||||
});
|
||||
|
||||
// Subscribe to visibilityState changes and pass to renderer process.
|
||||
let isVisible = this.isVisible() && !this.isMinimized();
|
||||
const visibilityChanged = () => {
|
||||
const newState = this.isVisible() && !this.isMinimized();
|
||||
if (isVisible !== newState) {
|
||||
isVisible = newState;
|
||||
const visibilityState = isVisible ? 'visible' : 'hidden';
|
||||
this.webContents.emit('-window-visibility-change', visibilityState);
|
||||
}
|
||||
};
|
||||
|
||||
const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore'];
|
||||
for (const event of visibilityEvents) {
|
||||
this.on(event, visibilityChanged);
|
||||
}
|
||||
|
||||
// Notify the creation of the window.
|
||||
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;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const isBrowserWindow = (win) => {
|
||||
return win && win.constructor.name === 'BrowserWindow';
|
||||
};
|
||||
|
||||
BrowserWindow.fromId = (id) => {
|
||||
const win = TopLevelWindow.fromId(id);
|
||||
return isBrowserWindow(win) ? win : null;
|
||||
};
|
||||
|
||||
BrowserWindow.getAllWindows = () => {
|
||||
return TopLevelWindow.getAllWindows().filter(isBrowserWindow);
|
||||
};
|
||||
|
||||
BrowserWindow.getFocusedWindow = () => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.isFocused() || window.isDevToolsFocused()) return window;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
BrowserWindow.fromWebContents = (webContents) => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.webContents && window.webContents.equal(webContents)) return window;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
BrowserWindow.fromBrowserView = (browserView) => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.getBrowserView() === browserView) return window;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// Helpers.
|
||||
Object.assign(BrowserWindow.prototype, {
|
||||
loadURL (...args) {
|
||||
return this.webContents.loadURL(...args);
|
||||
},
|
||||
getURL (...args) {
|
||||
return this.webContents.getURL();
|
||||
},
|
||||
loadFile (...args) {
|
||||
return this.webContents.loadFile(...args);
|
||||
},
|
||||
reload (...args) {
|
||||
return this.webContents.reload(...args);
|
||||
},
|
||||
send (...args) {
|
||||
return this.webContents.send(...args);
|
||||
},
|
||||
openDevTools (...args) {
|
||||
return this.webContents.openDevTools(...args);
|
||||
},
|
||||
closeDevTools () {
|
||||
return this.webContents.closeDevTools();
|
||||
},
|
||||
isDevToolsOpened () {
|
||||
return this.webContents.isDevToolsOpened();
|
||||
},
|
||||
isDevToolsFocused () {
|
||||
return this.webContents.isDevToolsFocused();
|
||||
},
|
||||
toggleDevTools () {
|
||||
return this.webContents.toggleDevTools();
|
||||
},
|
||||
inspectElement (...args) {
|
||||
return this.webContents.inspectElement(...args);
|
||||
},
|
||||
inspectSharedWorker () {
|
||||
return this.webContents.inspectSharedWorker();
|
||||
},
|
||||
inspectServiceWorker () {
|
||||
return this.webContents.inspectServiceWorker();
|
||||
},
|
||||
showDefinitionForSelection () {
|
||||
return this.webContents.showDefinitionForSelection();
|
||||
},
|
||||
capturePage (...args) {
|
||||
return this.webContents.capturePage(...args);
|
||||
},
|
||||
setTouchBar (touchBar) {
|
||||
electron.TouchBar._setOnWindow(touchBar, this);
|
||||
},
|
||||
getBackgroundThrottling () {
|
||||
return this.webContents.getBackgroundThrottling();
|
||||
},
|
||||
setBackgroundThrottling (allowed) {
|
||||
this.webContents.setBackgroundThrottling(allowed);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = BrowserWindow;
|
||||
@@ -1,182 +0,0 @@
|
||||
import { BaseWindow, WebContents, Event, BrowserView, TouchBar } from 'electron';
|
||||
import type { BrowserWindow as BWT } from 'electron';
|
||||
const { BrowserWindow } = process._linkedBinding('electron_browser_window') as { BrowserWindow: typeof BWT };
|
||||
|
||||
Object.setPrototypeOf(BrowserWindow.prototype, BaseWindow.prototype);
|
||||
|
||||
(BrowserWindow.prototype as any)._init = function (this: BWT) {
|
||||
// Call parent class's _init.
|
||||
(BaseWindow.prototype as any)._init.call(this);
|
||||
|
||||
// Avoid recursive require.
|
||||
const { app } = require('electron');
|
||||
|
||||
const nativeSetBounds = this.setBounds;
|
||||
this.setBounds = (bounds, ...opts) => {
|
||||
bounds = {
|
||||
...this.getBounds(),
|
||||
...bounds
|
||||
};
|
||||
nativeSetBounds.call(this, bounds, ...opts);
|
||||
};
|
||||
|
||||
// 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
|
||||
// focus it when we first start to load URL, if we do it earlier it won't
|
||||
// have effect, if we do it later we might move focus in the page.
|
||||
//
|
||||
// Though this hack is only needed on macOS when the app is launched from
|
||||
// Finder, we still do it on all platforms in case of other bugs we don't
|
||||
// know.
|
||||
this.webContents.once('load-url' as any, function (this: WebContents) {
|
||||
this.focus();
|
||||
});
|
||||
|
||||
// Redirect focus/blur event to app instance too.
|
||||
this.on('blur', (event: Event) => {
|
||||
app.emit('browser-window-blur', event, this);
|
||||
});
|
||||
this.on('focus', (event: Event) => {
|
||||
app.emit('browser-window-focus', event, this);
|
||||
});
|
||||
|
||||
// Subscribe to visibilityState changes and pass to renderer process.
|
||||
let isVisible = this.isVisible() && !this.isMinimized();
|
||||
const visibilityChanged = () => {
|
||||
const newState = this.isVisible() && !this.isMinimized();
|
||||
if (isVisible !== newState) {
|
||||
isVisible = newState;
|
||||
const visibilityState = isVisible ? 'visible' : 'hidden';
|
||||
this.webContents.emit('-window-visibility-change', visibilityState);
|
||||
}
|
||||
};
|
||||
|
||||
const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore'];
|
||||
for (const event of visibilityEvents) {
|
||||
this.on(event as any, visibilityChanged);
|
||||
}
|
||||
|
||||
// Notify the creation of the window.
|
||||
const event = process._linkedBinding('electron_browser_event').createEmpty();
|
||||
app.emit('browser-window-created', event, this);
|
||||
|
||||
Object.defineProperty(this, 'devToolsWebContents', {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get () {
|
||||
return this.webContents.devToolsWebContents;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const isBrowserWindow = (win: any) => {
|
||||
return win && win.constructor.name === 'BrowserWindow';
|
||||
};
|
||||
|
||||
BrowserWindow.fromId = (id: number) => {
|
||||
const win = BaseWindow.fromId(id);
|
||||
return isBrowserWindow(win) ? win as any as BWT : null;
|
||||
};
|
||||
|
||||
BrowserWindow.getAllWindows = () => {
|
||||
return BaseWindow.getAllWindows().filter(isBrowserWindow) as any[] as BWT[];
|
||||
};
|
||||
|
||||
BrowserWindow.getFocusedWindow = () => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.isFocused() || window.isDevToolsFocused()) return window;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
BrowserWindow.fromWebContents = (webContents: WebContents) => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.webContents && window.webContents.equal(webContents)) return window;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
BrowserWindow.fromBrowserView = (browserView: BrowserView) => {
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
if (window.getBrowserView() === browserView) return window;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.setTouchBar = function (touchBar) {
|
||||
(TouchBar as any)._setOnWindow(touchBar, this);
|
||||
};
|
||||
|
||||
// Forwarded to webContents:
|
||||
|
||||
BrowserWindow.prototype.loadURL = function (...args) {
|
||||
return this.webContents.loadURL(...args);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.getURL = function () {
|
||||
return this.webContents.getURL();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.loadFile = function (...args) {
|
||||
return this.webContents.loadFile(...args);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.reload = function (...args) {
|
||||
return this.webContents.reload(...args);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.send = function (...args) {
|
||||
return this.webContents.send(...args);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.openDevTools = function (...args) {
|
||||
return this.webContents.openDevTools(...args);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.closeDevTools = function () {
|
||||
return this.webContents.closeDevTools();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.isDevToolsOpened = function () {
|
||||
return this.webContents.isDevToolsOpened();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.isDevToolsFocused = function () {
|
||||
return this.webContents.isDevToolsFocused();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.toggleDevTools = function () {
|
||||
return this.webContents.toggleDevTools();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.inspectElement = function (...args) {
|
||||
return this.webContents.inspectElement(...args);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.inspectSharedWorker = function () {
|
||||
return this.webContents.inspectSharedWorker();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.inspectServiceWorker = function () {
|
||||
return this.webContents.inspectServiceWorker();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.showDefinitionForSelection = function () {
|
||||
return this.webContents.showDefinitionForSelection();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.capturePage = function (...args) {
|
||||
return this.webContents.capturePage(...args);
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.getBackgroundThrottling = function () {
|
||||
return this.webContents.getBackgroundThrottling();
|
||||
};
|
||||
|
||||
BrowserWindow.prototype.setBackgroundThrottling = function (allowed: boolean) {
|
||||
return this.webContents.setBackgroundThrottling(allowed);
|
||||
};
|
||||
|
||||
module.exports = BrowserWindow;
|
||||
2
lib/browser/api/content-tracing.js
Normal file
2
lib/browser/api/content-tracing.js
Normal file
@@ -0,0 +1,2 @@
|
||||
'use strict';
|
||||
module.exports = process.electronBinding('content_tracing');
|
||||
@@ -1 +0,0 @@
|
||||
export default process._linkedBinding('electron_browser_content_tracing');
|
||||
@@ -1,6 +1,6 @@
|
||||
import { app, deprecate } from 'electron';
|
||||
|
||||
const binding = process._linkedBinding('electron_browser_crash_reporter');
|
||||
const binding = process.electronBinding('crash_reporter');
|
||||
|
||||
class CrashReporter {
|
||||
start (options: Electron.CrashReporterStartOptions) {
|
||||
|
||||
270
lib/browser/api/dialog.js
Normal file
270
lib/browser/api/dialog.js
Normal file
@@ -0,0 +1,270 @@
|
||||
'use strict';
|
||||
|
||||
const { app, BrowserWindow, deprecate } = require('electron');
|
||||
const binding = process.electronBinding('dialog');
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
|
||||
const DialogType = {
|
||||
OPEN: 'OPEN',
|
||||
SAVE: 'SAVE'
|
||||
};
|
||||
|
||||
const saveFileDialogProperties = {
|
||||
createDirectory: 1 << 0,
|
||||
showHiddenFiles: 1 << 1,
|
||||
treatPackageAsDirectory: 1 << 2,
|
||||
showOverwriteConfirmation: 1 << 3,
|
||||
dontAddToRecent: 1 << 4
|
||||
};
|
||||
|
||||
const openFileDialogProperties = {
|
||||
openFile: 1 << 0,
|
||||
openDirectory: 1 << 1,
|
||||
multiSelections: 1 << 2,
|
||||
createDirectory: 1 << 3, // macOS
|
||||
showHiddenFiles: 1 << 4,
|
||||
promptToCreate: 1 << 5, // Windows
|
||||
noResolveAliases: 1 << 6, // macOS
|
||||
treatPackageAsDirectory: 1 << 7, // macOS
|
||||
dontAddToRecent: 1 << 8 // Windows
|
||||
};
|
||||
|
||||
const normalizeAccessKey = (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');
|
||||
}
|
||||
|
||||
// Linux uses a single underscore as an access key prefix so escape
|
||||
// existing single underscores with a second underscore, replace double
|
||||
// ampersands with a single ampersand, and replace a single ampersand with
|
||||
// a single underscore
|
||||
if (process.platform === 'linux') {
|
||||
return text.replace(/_/g, '__').replace(/&(.?)/g, (match, after) => {
|
||||
if (after === '&') return after;
|
||||
return `_${after}`;
|
||||
});
|
||||
}
|
||||
|
||||
return text;
|
||||
};
|
||||
|
||||
const checkAppInitialized = function () {
|
||||
if (!app.isReady()) {
|
||||
throw new Error('dialog module can only be used after app is ready');
|
||||
}
|
||||
};
|
||||
|
||||
const setupDialogProperties = (type, properties) => {
|
||||
const dialogPropertiesTypes = (type === DialogType.OPEN) ? openFileDialogProperties : saveFileDialogProperties;
|
||||
let dialogProperties = 0;
|
||||
for (const prop in dialogPropertiesTypes) {
|
||||
if (properties.includes(prop)) {
|
||||
dialogProperties |= dialogPropertiesTypes[prop];
|
||||
}
|
||||
}
|
||||
|
||||
return dialogProperties;
|
||||
};
|
||||
|
||||
const saveDialog = (sync, window, options) => {
|
||||
checkAppInitialized();
|
||||
|
||||
if (window && window.constructor !== BrowserWindow) {
|
||||
options = window;
|
||||
window = null;
|
||||
}
|
||||
|
||||
if (options == null) options = { title: 'Save' };
|
||||
|
||||
const {
|
||||
buttonLabel = '',
|
||||
defaultPath = '',
|
||||
filters = [],
|
||||
properties = [],
|
||||
title = '',
|
||||
message = '',
|
||||
securityScopedBookmarks = false,
|
||||
nameFieldLabel = '',
|
||||
showsTagField = true
|
||||
} = 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');
|
||||
|
||||
const settings = { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window };
|
||||
settings.properties = setupDialogProperties(DialogType.SAVE, properties);
|
||||
|
||||
return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings);
|
||||
};
|
||||
|
||||
const openDialog = (sync, window, options) => {
|
||||
checkAppInitialized();
|
||||
|
||||
if (window && window.constructor !== BrowserWindow) {
|
||||
options = window;
|
||||
window = null;
|
||||
}
|
||||
|
||||
if (options == null) {
|
||||
options = {
|
||||
title: 'Open',
|
||||
properties: ['openFile']
|
||||
};
|
||||
}
|
||||
|
||||
const {
|
||||
buttonLabel = '',
|
||||
defaultPath = '',
|
||||
filters = [],
|
||||
properties = ['openFile'],
|
||||
title = '',
|
||||
message = '',
|
||||
securityScopedBookmarks = false
|
||||
} = options;
|
||||
|
||||
if (!Array.isArray(properties)) throw new TypeError('Properties must be an array');
|
||||
|
||||
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 = setupDialogProperties(DialogType.OPEN, properties);
|
||||
|
||||
return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings);
|
||||
};
|
||||
|
||||
const messageBox = (sync, window, options) => {
|
||||
checkAppInitialized();
|
||||
|
||||
if (window && window.constructor !== BrowserWindow) {
|
||||
options = window;
|
||||
window = null;
|
||||
}
|
||||
|
||||
if (options == null) options = { type: 'none' };
|
||||
|
||||
const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'];
|
||||
const messageBoxOptions = { noLink: 1 << 0 };
|
||||
|
||||
let {
|
||||
buttons = [],
|
||||
cancelId,
|
||||
checkboxLabel = '',
|
||||
checkboxChecked,
|
||||
defaultId = -1,
|
||||
detail = '',
|
||||
icon = null,
|
||||
noLink = false,
|
||||
message = '',
|
||||
title = '',
|
||||
type = 'none'
|
||||
} = 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');
|
||||
|
||||
checkboxChecked = !!checkboxChecked;
|
||||
if (checkboxChecked && !checkboxLabel) {
|
||||
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;
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
const text = buttons[i].toLowerCase();
|
||||
if (text === 'cancel' || text === 'no') {
|
||||
cancelId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const settings = {
|
||||
window,
|
||||
messageBoxType,
|
||||
buttons,
|
||||
defaultId,
|
||||
cancelId,
|
||||
noLink,
|
||||
title,
|
||||
message,
|
||||
detail,
|
||||
checkboxLabel,
|
||||
checkboxChecked,
|
||||
icon
|
||||
};
|
||||
|
||||
if (sync) {
|
||||
return binding.showMessageBoxSync(settings);
|
||||
} else {
|
||||
return binding.showMessageBox(settings);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
showOpenDialog: function (window, options) {
|
||||
return openDialog(false, window, options);
|
||||
},
|
||||
|
||||
showOpenDialogSync: function (window, options) {
|
||||
return openDialog(true, window, options);
|
||||
},
|
||||
|
||||
showSaveDialog: function (window, options) {
|
||||
return saveDialog(false, window, options);
|
||||
},
|
||||
|
||||
showSaveDialogSync: function (window, options) {
|
||||
return saveDialog(true, window, options);
|
||||
},
|
||||
|
||||
showMessageBox: function (window, options) {
|
||||
return messageBox(false, window, options);
|
||||
},
|
||||
|
||||
showMessageBoxSync: function (window, options) {
|
||||
return messageBox(true, window, options);
|
||||
},
|
||||
|
||||
showErrorBox: function (...args) {
|
||||
return binding.showErrorBox(...args);
|
||||
},
|
||||
|
||||
showCertificateTrustDialog: function (window, options) {
|
||||
if (window && window.constructor !== BrowserWindow) {
|
||||
options = window;
|
||||
window = null;
|
||||
}
|
||||
|
||||
if (options == null || typeof options !== 'object') {
|
||||
throw new TypeError('options must be an object');
|
||||
}
|
||||
|
||||
const { certificate, message = '' } = options;
|
||||
if (certificate == null || typeof certificate !== 'object') {
|
||||
throw new TypeError('certificate must be an object');
|
||||
}
|
||||
|
||||
if (typeof message !== 'string') throw new TypeError('message must be a string');
|
||||
|
||||
return binding.showCertificateTrustDialog(window, certificate, message);
|
||||
}
|
||||
};
|
||||
@@ -1,306 +0,0 @@
|
||||
import { app, BrowserWindow } from 'electron';
|
||||
import { OpenDialogOptions, OpenDialogReturnValue, MessageBoxOptions, SaveDialogOptions, SaveDialogReturnValue, MessageBoxReturnValue, CertificateTrustDialogOptions } from 'electron/main';
|
||||
const dialogBinding = process._linkedBinding('electron_browser_dialog');
|
||||
|
||||
const DialogType = {
|
||||
OPEN: 'OPEN' as 'OPEN',
|
||||
SAVE: 'SAVE' as 'SAVE'
|
||||
};
|
||||
|
||||
enum SaveFileDialogProperties {
|
||||
createDirectory = 1 << 0,
|
||||
showHiddenFiles = 1 << 1,
|
||||
treatPackageAsDirectory = 1 << 2,
|
||||
showOverwriteConfirmation = 1 << 3,
|
||||
dontAddToRecent = 1 << 4
|
||||
}
|
||||
|
||||
enum OpenFileDialogProperties {
|
||||
openFile = 1 << 0,
|
||||
openDirectory = 1 << 1,
|
||||
multiSelections = 1 << 2,
|
||||
createDirectory = 1 << 3, // macOS
|
||||
showHiddenFiles = 1 << 4,
|
||||
promptToCreate = 1 << 5, // Windows
|
||||
noResolveAliases = 1 << 6, // macOS
|
||||
treatPackageAsDirectory = 1 << 7, // macOS
|
||||
dontAddToRecent = 1 << 8 // Windows
|
||||
}
|
||||
|
||||
const normalizeAccessKey = (text: string) => {
|
||||
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');
|
||||
}
|
||||
|
||||
// Linux uses a single underscore as an access key prefix so escape
|
||||
// existing single underscores with a second underscore, replace double
|
||||
// ampersands with a single ampersand, and replace a single ampersand with
|
||||
// a single underscore
|
||||
if (process.platform === 'linux') {
|
||||
return text.replace(/_/g, '__').replace(/&(.?)/g, (match, after) => {
|
||||
if (after === '&') return after;
|
||||
return `_${after}`;
|
||||
});
|
||||
}
|
||||
|
||||
return text;
|
||||
};
|
||||
|
||||
const checkAppInitialized = function () {
|
||||
if (!app.isReady()) {
|
||||
throw new Error('dialog module can only be used after app is ready');
|
||||
}
|
||||
};
|
||||
|
||||
const setupOpenDialogProperties = (properties: (keyof typeof OpenFileDialogProperties)[]): number => {
|
||||
let dialogProperties = 0;
|
||||
for (const property of properties) {
|
||||
if (Object.prototype.hasOwnProperty.call(OpenFileDialogProperties, property)) { dialogProperties |= OpenFileDialogProperties[property]; }
|
||||
}
|
||||
return dialogProperties;
|
||||
};
|
||||
|
||||
const setupSaveDialogProperties = (properties: (keyof typeof SaveFileDialogProperties)[]): number => {
|
||||
let dialogProperties = 0;
|
||||
for (const property of properties) {
|
||||
if (Object.prototype.hasOwnProperty.call(SaveFileDialogProperties, property)) { dialogProperties |= SaveFileDialogProperties[property]; }
|
||||
}
|
||||
return dialogProperties;
|
||||
};
|
||||
|
||||
const setupDialogProperties = (type: keyof typeof DialogType, properties: string[]): number => {
|
||||
if (type === DialogType.OPEN) {
|
||||
return setupOpenDialogProperties(properties as (keyof typeof OpenFileDialogProperties)[]);
|
||||
} else if (type === DialogType.SAVE) {
|
||||
return setupSaveDialogProperties(properties as (keyof typeof SaveFileDialogProperties)[]);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
const saveDialog = (sync: boolean, window: BrowserWindow | null, options?: SaveDialogOptions) => {
|
||||
checkAppInitialized();
|
||||
|
||||
if (options == null) options = { title: 'Save' };
|
||||
|
||||
const {
|
||||
buttonLabel = '',
|
||||
defaultPath = '',
|
||||
filters = [],
|
||||
properties = [],
|
||||
title = '',
|
||||
message = '',
|
||||
securityScopedBookmarks = false,
|
||||
nameFieldLabel = '',
|
||||
showsTagField = true
|
||||
} = 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');
|
||||
|
||||
const settings = {
|
||||
buttonLabel,
|
||||
defaultPath,
|
||||
filters,
|
||||
title,
|
||||
message,
|
||||
securityScopedBookmarks,
|
||||
nameFieldLabel,
|
||||
showsTagField,
|
||||
window,
|
||||
properties: setupDialogProperties(DialogType.SAVE, properties)
|
||||
};
|
||||
|
||||
return sync ? dialogBinding.showSaveDialogSync(settings) : dialogBinding.showSaveDialog(settings);
|
||||
};
|
||||
|
||||
const openDialog = (sync: boolean, window: BrowserWindow | null, options?: OpenDialogOptions) => {
|
||||
checkAppInitialized();
|
||||
|
||||
if (options == null) {
|
||||
options = {
|
||||
title: 'Open',
|
||||
properties: ['openFile']
|
||||
};
|
||||
}
|
||||
|
||||
const {
|
||||
buttonLabel = '',
|
||||
defaultPath = '',
|
||||
filters = [],
|
||||
properties = ['openFile'],
|
||||
title = '',
|
||||
message = '',
|
||||
securityScopedBookmarks = false
|
||||
} = options;
|
||||
|
||||
if (!Array.isArray(properties)) throw new TypeError('Properties must be an array');
|
||||
|
||||
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,
|
||||
properties: setupDialogProperties(DialogType.OPEN, properties)
|
||||
};
|
||||
|
||||
return (sync) ? dialogBinding.showOpenDialogSync(settings) : dialogBinding.showOpenDialog(settings);
|
||||
};
|
||||
|
||||
const messageBox = (sync: boolean, window: BrowserWindow | null, options?: MessageBoxOptions) => {
|
||||
checkAppInitialized();
|
||||
|
||||
if (options == null) options = { type: 'none', message: '' };
|
||||
|
||||
const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'];
|
||||
|
||||
let {
|
||||
buttons = [],
|
||||
cancelId,
|
||||
checkboxLabel = '',
|
||||
checkboxChecked,
|
||||
defaultId = -1,
|
||||
detail = '',
|
||||
icon = null,
|
||||
noLink = false,
|
||||
message = '',
|
||||
title = '',
|
||||
type = 'none'
|
||||
} = 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');
|
||||
|
||||
checkboxChecked = !!checkboxChecked;
|
||||
if (checkboxChecked && !checkboxLabel) {
|
||||
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;
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
const text = buttons[i].toLowerCase();
|
||||
if (text === 'cancel' || text === 'no') {
|
||||
cancelId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const settings = {
|
||||
window,
|
||||
messageBoxType,
|
||||
buttons,
|
||||
defaultId,
|
||||
cancelId,
|
||||
noLink,
|
||||
title,
|
||||
message,
|
||||
detail,
|
||||
checkboxLabel,
|
||||
checkboxChecked,
|
||||
icon
|
||||
};
|
||||
|
||||
if (sync) {
|
||||
return dialogBinding.showMessageBoxSync(settings);
|
||||
} else {
|
||||
return dialogBinding.showMessageBox(settings);
|
||||
}
|
||||
};
|
||||
|
||||
// eat dirt, eslint
|
||||
/* eslint-disable import/export */
|
||||
export function showOpenDialog(window: BrowserWindow, options: OpenDialogOptions): OpenDialogReturnValue;
|
||||
export function showOpenDialog(options: OpenDialogOptions): OpenDialogReturnValue;
|
||||
export function showOpenDialog (windowOrOptions: BrowserWindow | OpenDialogOptions, maybeOptions?: OpenDialogOptions): OpenDialogReturnValue {
|
||||
const window = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? null : windowOrOptions);
|
||||
const options = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? windowOrOptions : maybeOptions);
|
||||
return openDialog(false, window, options);
|
||||
}
|
||||
|
||||
export function showOpenDialogSync(window: BrowserWindow, options: OpenDialogOptions): OpenDialogReturnValue;
|
||||
export function showOpenDialogSync(options: OpenDialogOptions): OpenDialogReturnValue;
|
||||
export function showOpenDialogSync (windowOrOptions: BrowserWindow | OpenDialogOptions, maybeOptions?: OpenDialogOptions): OpenDialogReturnValue {
|
||||
const window = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? null : windowOrOptions);
|
||||
const options = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? windowOrOptions : maybeOptions);
|
||||
return openDialog(true, window, options);
|
||||
}
|
||||
|
||||
export function showSaveDialog(window: BrowserWindow, options: SaveDialogOptions): SaveDialogReturnValue;
|
||||
export function showSaveDialog(options: SaveDialogOptions): SaveDialogReturnValue;
|
||||
export function showSaveDialog (windowOrOptions: BrowserWindow | SaveDialogOptions, maybeOptions?: SaveDialogOptions): SaveDialogReturnValue {
|
||||
const window = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? null : windowOrOptions);
|
||||
const options = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? windowOrOptions : maybeOptions);
|
||||
return saveDialog(false, window, options);
|
||||
}
|
||||
|
||||
export function showSaveDialogSync(window: BrowserWindow, options: SaveDialogOptions): SaveDialogReturnValue;
|
||||
export function showSaveDialogSync(options: SaveDialogOptions): SaveDialogReturnValue;
|
||||
export function showSaveDialogSync (windowOrOptions: BrowserWindow | SaveDialogOptions, maybeOptions?: SaveDialogOptions): SaveDialogReturnValue {
|
||||
const window = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? null : windowOrOptions);
|
||||
const options = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? windowOrOptions : maybeOptions);
|
||||
return saveDialog(true, window, options);
|
||||
}
|
||||
|
||||
export function showMessageBox(window: BrowserWindow, options: MessageBoxOptions): MessageBoxReturnValue;
|
||||
export function showMessageBox(options: MessageBoxOptions): MessageBoxReturnValue;
|
||||
export function showMessageBox (windowOrOptions: BrowserWindow | MessageBoxOptions, maybeOptions?: MessageBoxOptions): MessageBoxReturnValue {
|
||||
const window = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? null : windowOrOptions);
|
||||
const options = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? windowOrOptions : maybeOptions);
|
||||
return messageBox(false, window, options);
|
||||
}
|
||||
|
||||
export function showMessageBoxSync(window: BrowserWindow, options: MessageBoxOptions): MessageBoxReturnValue;
|
||||
export function showMessageBoxSync(options: MessageBoxOptions): MessageBoxReturnValue;
|
||||
export function showMessageBoxSync (windowOrOptions: BrowserWindow | MessageBoxOptions, maybeOptions?: MessageBoxOptions): MessageBoxReturnValue {
|
||||
const window = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? null : windowOrOptions);
|
||||
const options = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? windowOrOptions : maybeOptions);
|
||||
return messageBox(true, window, options);
|
||||
}
|
||||
|
||||
export function showErrorBox (...args: any[]) {
|
||||
return dialogBinding.showErrorBox(...args);
|
||||
}
|
||||
|
||||
export function showCertificateTrustDialog (windowOrOptions: BrowserWindow | CertificateTrustDialogOptions, maybeOptions?: CertificateTrustDialogOptions) {
|
||||
const window = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? null : windowOrOptions);
|
||||
const options = (windowOrOptions && !(windowOrOptions instanceof BrowserWindow) ? windowOrOptions : maybeOptions);
|
||||
|
||||
if (options == null || typeof options !== 'object') {
|
||||
throw new TypeError('options must be an object');
|
||||
}
|
||||
|
||||
const { certificate, message = '' } = options;
|
||||
if (certificate == null || typeof certificate !== 'object') {
|
||||
throw new TypeError('certificate must be an object');
|
||||
}
|
||||
|
||||
if (typeof message !== 'string') throw new TypeError('message must be a string');
|
||||
|
||||
return dialogBinding.showCertificateTrustDialog(window, certificate, message);
|
||||
}
|
||||
3
lib/browser/api/global-shortcut.js
Normal file
3
lib/browser/api/global-shortcut.js
Normal file
@@ -0,0 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = process.electronBinding('global_shortcut').globalShortcut;
|
||||
@@ -1,2 +0,0 @@
|
||||
const { globalShortcut } = process._linkedBinding('electron_browser_global_shortcut');
|
||||
export default globalShortcut;
|
||||
@@ -1,24 +1,22 @@
|
||||
import { EventEmitter } from 'events';
|
||||
'use strict';
|
||||
|
||||
let _inAppPurchase;
|
||||
const { deprecate } = require('electron');
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
const { inAppPurchase, InAppPurchase } = process._linkedBinding('electron_browser_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);
|
||||
|
||||
_inAppPurchase = inAppPurchase;
|
||||
module.exports = inAppPurchase;
|
||||
} else {
|
||||
_inAppPurchase = new EventEmitter();
|
||||
Object.assign(_inAppPurchase, {
|
||||
purchaseProduct: () => {
|
||||
module.exports = {
|
||||
purchaseProduct: (productID, quantity, callback) => {
|
||||
throw new Error('The inAppPurchase module can only be used on macOS');
|
||||
},
|
||||
canMakePayments: () => false,
|
||||
getReceiptURL: () => ''
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default _inAppPurchase;
|
||||
@@ -1,56 +1,44 @@
|
||||
import { app, BrowserWindow, WebContents, MenuItemConstructorOptions } from 'electron';
|
||||
'use strict';
|
||||
|
||||
const { app } = require('electron');
|
||||
|
||||
const isMac = process.platform === 'darwin';
|
||||
const isWindows = process.platform === 'win32';
|
||||
const isLinux = process.platform === 'linux';
|
||||
|
||||
type RoleId = 'about' | 'close' | 'copy' | 'cut' | 'delete' | 'forcereload' | 'front' | 'help' | 'hide' | 'hideothers' | 'minimize' |
|
||||
'paste' | 'pasteandmatchstyle' | 'quit' | 'redo' | 'reload' | 'resetzoom' | 'selectall' | 'services' | 'recentdocuments' | 'clearrecentdocuments' | 'startspeaking' | 'stopspeaking' |
|
||||
'toggledevtools' | 'togglefullscreen' | 'undo' | 'unhide' | 'window' | 'zoom' | 'zoomin' | 'zoomout' | 'appmenu' | 'filemenu' | 'editmenu' | 'viewmenu' | 'windowmenu'
|
||||
interface Role {
|
||||
label: string;
|
||||
accelerator?: string;
|
||||
windowMethod?: ((window: BrowserWindow) => void);
|
||||
webContentsMethod?: ((webContents: WebContents) => void);
|
||||
appMethod?: () => void;
|
||||
registerAccelerator?: boolean;
|
||||
nonNativeMacOSRole?: boolean;
|
||||
submenu?: MenuItemConstructorOptions[];
|
||||
}
|
||||
|
||||
export const roleList: Record<RoleId, Role> = {
|
||||
const roles = {
|
||||
about: {
|
||||
get label () {
|
||||
return isLinux ? 'About' : `About ${app.name}`;
|
||||
},
|
||||
...(isWindows && { appMethod: () => app.showAboutPanel() })
|
||||
...(isWindows && { appMethod: 'showAboutPanel' })
|
||||
},
|
||||
close: {
|
||||
label: isMac ? 'Close Window' : 'Close',
|
||||
accelerator: 'CommandOrControl+W',
|
||||
windowMethod: w => w.close()
|
||||
windowMethod: 'close'
|
||||
},
|
||||
copy: {
|
||||
label: 'Copy',
|
||||
accelerator: 'CommandOrControl+C',
|
||||
webContentsMethod: wc => wc.copy(),
|
||||
webContentsMethod: 'copy',
|
||||
registerAccelerator: false
|
||||
},
|
||||
cut: {
|
||||
label: 'Cut',
|
||||
accelerator: 'CommandOrControl+X',
|
||||
webContentsMethod: wc => wc.cut(),
|
||||
webContentsMethod: 'cut',
|
||||
registerAccelerator: false
|
||||
},
|
||||
delete: {
|
||||
label: 'Delete',
|
||||
webContentsMethod: wc => wc.delete()
|
||||
webContentsMethod: 'delete'
|
||||
},
|
||||
forcereload: {
|
||||
label: 'Force Reload',
|
||||
accelerator: 'Shift+CmdOrCtrl+R',
|
||||
nonNativeMacOSRole: true,
|
||||
windowMethod: (window: BrowserWindow) => {
|
||||
windowMethod: (window) => {
|
||||
window.webContents.reloadIgnoringCache();
|
||||
}
|
||||
},
|
||||
@@ -73,18 +61,18 @@ export const roleList: Record<RoleId, Role> = {
|
||||
minimize: {
|
||||
label: 'Minimize',
|
||||
accelerator: 'CommandOrControl+M',
|
||||
windowMethod: w => w.minimize()
|
||||
windowMethod: 'minimize'
|
||||
},
|
||||
paste: {
|
||||
label: 'Paste',
|
||||
accelerator: 'CommandOrControl+V',
|
||||
webContentsMethod: wc => wc.paste(),
|
||||
webContentsMethod: 'paste',
|
||||
registerAccelerator: false
|
||||
},
|
||||
pasteandmatchstyle: {
|
||||
label: 'Paste and Match Style',
|
||||
accelerator: isMac ? 'Cmd+Option+Shift+V' : 'Shift+CommandOrControl+V',
|
||||
webContentsMethod: wc => wc.pasteAndMatchStyle(),
|
||||
webContentsMethod: 'pasteAndMatchStyle',
|
||||
registerAccelerator: false
|
||||
},
|
||||
quit: {
|
||||
@@ -96,31 +84,31 @@ export const roleList: Record<RoleId, Role> = {
|
||||
}
|
||||
},
|
||||
accelerator: isWindows ? undefined : 'CommandOrControl+Q',
|
||||
appMethod: () => app.quit()
|
||||
appMethod: 'quit'
|
||||
},
|
||||
redo: {
|
||||
label: 'Redo',
|
||||
accelerator: isWindows ? 'Control+Y' : 'Shift+CommandOrControl+Z',
|
||||
webContentsMethod: wc => wc.redo()
|
||||
webContentsMethod: 'redo'
|
||||
},
|
||||
reload: {
|
||||
label: 'Reload',
|
||||
accelerator: 'CmdOrCtrl+R',
|
||||
nonNativeMacOSRole: true,
|
||||
windowMethod: w => w.reload()
|
||||
windowMethod: 'reload'
|
||||
},
|
||||
resetzoom: {
|
||||
label: 'Actual Size',
|
||||
accelerator: 'CommandOrControl+0',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents: WebContents) => {
|
||||
webContentsMethod: (webContents) => {
|
||||
webContents.zoomLevel = 0;
|
||||
}
|
||||
},
|
||||
selectall: {
|
||||
label: 'Select All',
|
||||
accelerator: 'CommandOrControl+A',
|
||||
webContentsMethod: wc => wc.selectAll()
|
||||
webContentsMethod: 'selectAll'
|
||||
},
|
||||
services: {
|
||||
label: 'Services'
|
||||
@@ -141,19 +129,19 @@ export const roleList: Record<RoleId, Role> = {
|
||||
label: 'Toggle Developer Tools',
|
||||
accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I',
|
||||
nonNativeMacOSRole: true,
|
||||
windowMethod: w => w.webContents.toggleDevTools()
|
||||
windowMethod: 'toggleDevTools'
|
||||
},
|
||||
togglefullscreen: {
|
||||
label: 'Toggle Full Screen',
|
||||
accelerator: isMac ? 'Control+Command+F' : 'F11',
|
||||
windowMethod: (window: BrowserWindow) => {
|
||||
windowMethod: (window) => {
|
||||
window.setFullScreen(!window.isFullScreen());
|
||||
}
|
||||
},
|
||||
undo: {
|
||||
label: 'Undo',
|
||||
accelerator: 'CommandOrControl+Z',
|
||||
webContentsMethod: wc => wc.undo()
|
||||
webContentsMethod: 'undo'
|
||||
},
|
||||
unhide: {
|
||||
label: 'Show All'
|
||||
@@ -168,7 +156,7 @@ export const roleList: Record<RoleId, Role> = {
|
||||
label: 'Zoom In',
|
||||
accelerator: 'CommandOrControl+Plus',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents: WebContents) => {
|
||||
webContentsMethod: (webContents) => {
|
||||
webContents.zoomLevel += 0.5;
|
||||
}
|
||||
},
|
||||
@@ -176,7 +164,7 @@ export const roleList: Record<RoleId, Role> = {
|
||||
label: 'Zoom Out',
|
||||
accelerator: 'CommandOrControl+-',
|
||||
nonNativeMacOSRole: true,
|
||||
webContentsMethod: (webContents: WebContents) => {
|
||||
webContentsMethod: (webContents) => {
|
||||
webContents.zoomLevel -= 0.5;
|
||||
}
|
||||
},
|
||||
@@ -226,11 +214,11 @@ export const roleList: Record<RoleId, Role> = {
|
||||
{ role: 'stopSpeaking' }
|
||||
]
|
||||
}
|
||||
] as MenuItemConstructorOptions[] : [
|
||||
] : [
|
||||
{ role: 'delete' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'selectAll' }
|
||||
] as MenuItemConstructorOptions[])
|
||||
])
|
||||
]
|
||||
},
|
||||
// View submenu
|
||||
@@ -257,38 +245,40 @@ export const roleList: Record<RoleId, Role> = {
|
||||
...(isMac ? [
|
||||
{ type: 'separator' },
|
||||
{ role: 'front' }
|
||||
] as MenuItemConstructorOptions[] : [
|
||||
] : [
|
||||
{ role: 'close' }
|
||||
] as MenuItemConstructorOptions[])
|
||||
])
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const canExecuteRole = (role: keyof typeof roleList) => {
|
||||
if (!Object.prototype.hasOwnProperty.call(roleList, role)) return false;
|
||||
exports.roleList = roles;
|
||||
|
||||
const canExecuteRole = (role) => {
|
||||
if (!Object.prototype.hasOwnProperty.call(roles, role)) return false;
|
||||
if (!isMac) return true;
|
||||
|
||||
// macOS handles all roles natively except for a few
|
||||
return roleList[role].nonNativeMacOSRole;
|
||||
return roles[role].nonNativeMacOSRole;
|
||||
};
|
||||
|
||||
export function getDefaultLabel (role: RoleId) {
|
||||
return Object.prototype.hasOwnProperty.call(roleList, role) ? roleList[role].label : '';
|
||||
}
|
||||
exports.getDefaultLabel = (role) => {
|
||||
return Object.prototype.hasOwnProperty.call(roles, role) ? roles[role].label : '';
|
||||
};
|
||||
|
||||
export function getDefaultAccelerator (role: RoleId) {
|
||||
if (Object.prototype.hasOwnProperty.call(roleList, role)) return roleList[role].accelerator;
|
||||
}
|
||||
exports.getDefaultAccelerator = (role) => {
|
||||
if (Object.prototype.hasOwnProperty.call(roles, role)) return roles[role].accelerator;
|
||||
};
|
||||
|
||||
export function shouldRegisterAccelerator (role: RoleId) {
|
||||
const hasRoleRegister = Object.prototype.hasOwnProperty.call(roleList, role) && roleList[role].registerAccelerator !== undefined;
|
||||
return hasRoleRegister ? roleList[role].registerAccelerator : true;
|
||||
}
|
||||
exports.shouldRegisterAccelerator = (role) => {
|
||||
const hasRoleRegister = Object.prototype.hasOwnProperty.call(roles, role) && roles[role].registerAccelerator !== undefined;
|
||||
return hasRoleRegister ? roles[role].registerAccelerator : true;
|
||||
};
|
||||
|
||||
export function getDefaultSubmenu (role: RoleId) {
|
||||
if (!Object.prototype.hasOwnProperty.call(roleList, role)) return;
|
||||
exports.getDefaultSubmenu = (role) => {
|
||||
if (!Object.prototype.hasOwnProperty.call(roles, role)) return;
|
||||
|
||||
let { submenu } = roleList[role];
|
||||
let { submenu } = roles[role];
|
||||
|
||||
// remove null items from within the submenu
|
||||
if (Array.isArray(submenu)) {
|
||||
@@ -296,27 +286,35 @@ export function getDefaultSubmenu (role: RoleId) {
|
||||
}
|
||||
|
||||
return submenu;
|
||||
}
|
||||
};
|
||||
|
||||
export function execute (role: RoleId, focusedWindow: BrowserWindow, focusedWebContents: WebContents) {
|
||||
exports.execute = (role, focusedWindow, focusedWebContents) => {
|
||||
if (!canExecuteRole(role)) return false;
|
||||
|
||||
const { appMethod, webContentsMethod, windowMethod } = roleList[role];
|
||||
const { appMethod, webContentsMethod, windowMethod } = roles[role];
|
||||
|
||||
if (appMethod) {
|
||||
appMethod();
|
||||
app[appMethod]();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (windowMethod && focusedWindow != null) {
|
||||
windowMethod(focusedWindow);
|
||||
if (typeof windowMethod === 'function') {
|
||||
windowMethod(focusedWindow);
|
||||
} else {
|
||||
focusedWindow[windowMethod]();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (webContentsMethod && focusedWebContents != null) {
|
||||
webContentsMethod(focusedWebContents);
|
||||
if (typeof webContentsMethod === 'function') {
|
||||
webContentsMethod(focusedWebContents);
|
||||
} else {
|
||||
focusedWebContents[webContentsMethod]();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -1,9 +1,12 @@
|
||||
import * as roles from './menu-item-roles';
|
||||
import { Menu, Event, BrowserWindow, WebContents } from 'electron';
|
||||
'use strict';
|
||||
|
||||
const roles = require('@electron/internal/browser/api/menu-item-roles');
|
||||
|
||||
let nextCommandId = 0;
|
||||
|
||||
const MenuItem = function (this: any, options: any) {
|
||||
const MenuItem = function (options) {
|
||||
const { Menu } = require('electron');
|
||||
|
||||
// Preserve extra fields specified by user
|
||||
for (const key in options) {
|
||||
if (!(key in this)) this[key] = options[key];
|
||||
@@ -44,7 +47,7 @@ const MenuItem = function (this: any, options: any) {
|
||||
this.overrideReadOnlyProperty('commandId', ++nextCommandId);
|
||||
|
||||
const click = options.click;
|
||||
this.click = (event: Event, focusedWindow: BrowserWindow, focusedWebContents: WebContents) => {
|
||||
this.click = (event, focusedWindow, focusedWebContents) => {
|
||||
// Manually flip the checked flags when clicked.
|
||||
if (this.type === 'checkbox' || this.type === 'radio') {
|
||||
this.checked = !this.checked;
|
||||
@@ -66,13 +69,13 @@ MenuItem.prototype.getDefaultRoleAccelerator = function () {
|
||||
return roles.getDefaultAccelerator(this.role);
|
||||
};
|
||||
|
||||
MenuItem.prototype.overrideProperty = function (name: string, defaultValue: any = null) {
|
||||
MenuItem.prototype.overrideProperty = function (name, defaultValue = null) {
|
||||
if (this[name] == null) {
|
||||
this[name] = defaultValue;
|
||||
}
|
||||
};
|
||||
|
||||
MenuItem.prototype.overrideReadOnlyProperty = function (name: string, defaultValue: any) {
|
||||
MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) {
|
||||
this.overrideProperty(name, defaultValue);
|
||||
Object.defineProperty(this, name, {
|
||||
enumerable: true,
|
||||
@@ -1,4 +1,6 @@
|
||||
function splitArray<T> (arr: T[], predicate: (x: T) => boolean) {
|
||||
'use strict';
|
||||
|
||||
function splitArray (arr, predicate) {
|
||||
const result = arr.reduce((multi, item) => {
|
||||
const current = multi[multi.length - 1];
|
||||
if (predicate(item)) {
|
||||
@@ -7,7 +9,7 @@ function splitArray<T> (arr: T[], predicate: (x: T) => boolean) {
|
||||
current.push(item);
|
||||
}
|
||||
return multi;
|
||||
}, [[]] as T[][]);
|
||||
}, [[]]);
|
||||
|
||||
if (result[result.length - 1].length === 0) {
|
||||
return result.slice(0, result.length - 1);
|
||||
@@ -15,7 +17,7 @@ function splitArray<T> (arr: T[], predicate: (x: T) => boolean) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function joinArrays (arrays: any[][], joinIDs: any[]) {
|
||||
function joinArrays (arrays, joinIDs) {
|
||||
return arrays.reduce((joined, arr, i) => {
|
||||
if (i > 0 && arr.length) {
|
||||
if (joinIDs.length > 0) {
|
||||
@@ -29,14 +31,14 @@ function joinArrays (arrays: any[][], joinIDs: any[]) {
|
||||
}, []);
|
||||
}
|
||||
|
||||
function pushOntoMultiMap<K, V> (map: Map<K, V[]>, key: K, value: V) {
|
||||
function pushOntoMultiMap (map, key, value) {
|
||||
if (!map.has(key)) {
|
||||
map.set(key, []);
|
||||
}
|
||||
map.get(key)!.push(value);
|
||||
map.get(key).push(value);
|
||||
}
|
||||
|
||||
function indexOfGroupContainingID<T> (groups: {id?: T}[][], id: T, ignoreGroup: {id?: T}[]) {
|
||||
function indexOfGroupContainingID (groups, id, ignoreGroup) {
|
||||
return groups.findIndex(
|
||||
candidateGroup =>
|
||||
candidateGroup !== ignoreGroup &&
|
||||
@@ -48,11 +50,11 @@ function indexOfGroupContainingID<T> (groups: {id?: T}[][], id: T, ignoreGroup:
|
||||
|
||||
// Sort nodes topologically using a depth-first approach. Encountered cycles
|
||||
// are broken.
|
||||
function sortTopologically<T> (originalOrder: T[], edgesById: Map<T, T[]>) {
|
||||
const sorted = [] as T[];
|
||||
const marked = new Set<T>();
|
||||
function sortTopologically (originalOrder, edgesById) {
|
||||
const sorted = [];
|
||||
const marked = new Set();
|
||||
|
||||
const visit = (mark: T) => {
|
||||
const visit = (mark) => {
|
||||
if (marked.has(mark)) return;
|
||||
marked.add(mark);
|
||||
const edges = edgesById.get(mark);
|
||||
@@ -66,7 +68,7 @@ function sortTopologically<T> (originalOrder: T[], edgesById: Map<T, T[]>) {
|
||||
return sorted;
|
||||
}
|
||||
|
||||
function attemptToMergeAGroup<T> (groups: {before?: T[], after?: T[], id?: T}[][]) {
|
||||
function attemptToMergeAGroup (groups) {
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
const group = groups[i];
|
||||
for (const item of group) {
|
||||
@@ -85,7 +87,7 @@ function attemptToMergeAGroup<T> (groups: {before?: T[], after?: T[], id?: T}[][
|
||||
return false;
|
||||
}
|
||||
|
||||
function mergeGroups<T> (groups: {before?: T[], after?: T[], id?: T}[][]) {
|
||||
function mergeGroups (groups) {
|
||||
let merged = true;
|
||||
while (merged) {
|
||||
merged = attemptToMergeAGroup(groups);
|
||||
@@ -93,7 +95,7 @@ function mergeGroups<T> (groups: {before?: T[], after?: T[], id?: T}[][]) {
|
||||
return groups;
|
||||
}
|
||||
|
||||
function sortItemsInGroup<T> (group: {before?: T[], after?: T[], id?: T}[]) {
|
||||
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]));
|
||||
@@ -121,7 +123,7 @@ function sortItemsInGroup<T> (group: {before?: T[], after?: T[], id?: T}[]) {
|
||||
return sortedNodes.map(i => group[i]);
|
||||
}
|
||||
|
||||
function findEdgesInGroup<T> (groups: {beforeGroupContaining?: T[], afterGroupContaining?: T[], id?: T}[][], i: number, edges: Map<any, any>) {
|
||||
function findEdgesInGroup (groups, i, edges) {
|
||||
const group = groups[i];
|
||||
for (const item of group) {
|
||||
if (item.beforeGroupContaining) {
|
||||
@@ -145,7 +147,7 @@ function findEdgesInGroup<T> (groups: {beforeGroupContaining?: T[], afterGroupCo
|
||||
}
|
||||
}
|
||||
|
||||
function sortGroups<T> (groups: {id?: T}[][]) {
|
||||
function sortGroups (groups) {
|
||||
const originalOrder = groups.map((item, i) => i);
|
||||
const edges = new Map();
|
||||
|
||||
@@ -157,8 +159,8 @@ function sortGroups<T> (groups: {id?: T}[][]) {
|
||||
return sortedGroupIndexes.map(i => groups[i]);
|
||||
}
|
||||
|
||||
export function sortMenuItems (menuItems: {type?: string, id?: string}[]) {
|
||||
const isSeparator = (item: {type?: string}) => item.type === 'separator';
|
||||
function sortMenuItems (menuItems) {
|
||||
const isSeparator = (item) => item.type === 'separator';
|
||||
const separators = menuItems.filter(i => i.type === 'separator');
|
||||
|
||||
// Split the items into their implicit groups based upon separators.
|
||||
@@ -170,3 +172,5 @@ export function sortMenuItems (menuItems: {type?: string, id?: string}[]) {
|
||||
const joined = joinArrays(sortedGroups, separators);
|
||||
return joined;
|
||||
}
|
||||
|
||||
module.exports = { sortMenuItems };
|
||||
@@ -1,11 +1,13 @@
|
||||
import { BaseWindow, MenuItem, webContents, Menu as MenuType, BrowserWindow, MenuItemConstructorOptions } from 'electron';
|
||||
import { sortMenuItems } from './menu-utils';
|
||||
'use strict';
|
||||
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
const bindings = process._linkedBinding('electron_browser_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 as { Menu: typeof MenuType };
|
||||
let applicationMenu: MenuType | null = null;
|
||||
const { Menu } = bindings;
|
||||
let applicationMenu = null;
|
||||
let groupIdIndex = 0;
|
||||
|
||||
/* Instance Methods */
|
||||
@@ -17,17 +19,17 @@ Menu.prototype._init = function () {
|
||||
};
|
||||
|
||||
Menu.prototype._isCommandIdChecked = function (id) {
|
||||
return this.commandsMap[id] ? this.commandsMap[id].checked : false;
|
||||
return this.commandsMap[id] ? this.commandsMap[id].checked : undefined;
|
||||
};
|
||||
|
||||
Menu.prototype._isCommandIdEnabled = function (id) {
|
||||
return this.commandsMap[id] ? this.commandsMap[id].enabled : false;
|
||||
return this.commandsMap[id] ? this.commandsMap[id].enabled : undefined;
|
||||
};
|
||||
Menu.prototype._shouldCommandIdWorkWhenHidden = function (id) {
|
||||
return this.commandsMap[id] ? !!this.commandsMap[id].acceleratorWorksWhenHidden : false;
|
||||
return this.commandsMap[id] ? this.commandsMap[id].acceleratorWorksWhenHidden : undefined;
|
||||
};
|
||||
Menu.prototype._isCommandIdVisible = function (id) {
|
||||
return this.commandsMap[id] ? this.commandsMap[id].visible : false;
|
||||
return this.commandsMap[id] ? this.commandsMap[id].visible : undefined;
|
||||
};
|
||||
|
||||
Menu.prototype._getAcceleratorForCommandId = function (id, useDefaultAccelerator) {
|
||||
@@ -38,14 +40,13 @@ Menu.prototype._getAcceleratorForCommandId = function (id, useDefaultAccelerator
|
||||
};
|
||||
|
||||
Menu.prototype._shouldRegisterAcceleratorForCommandId = function (id) {
|
||||
return this.commandsMap[id] ? this.commandsMap[id].registerAccelerator : false;
|
||||
return this.commandsMap[id] ? this.commandsMap[id].registerAccelerator : undefined;
|
||||
};
|
||||
|
||||
Menu.prototype._executeCommand = function (event, id) {
|
||||
const command = this.commandsMap[id];
|
||||
if (!command) return;
|
||||
const focusedWindow = BaseWindow.getFocusedWindow();
|
||||
command.click(event, focusedWindow instanceof BrowserWindow ? focusedWindow : undefined, webContents.getFocusedWebContents());
|
||||
command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents());
|
||||
};
|
||||
|
||||
Menu.prototype._menuWillShow = function () {
|
||||
@@ -71,23 +72,23 @@ Menu.prototype.popup = function (options = {}) {
|
||||
if (typeof positioningItem !== 'number') positioningItem = -1;
|
||||
|
||||
// find which window to use
|
||||
const wins = BaseWindow.getAllWindows();
|
||||
if (!wins || wins.indexOf(window as any) === -1) {
|
||||
window = BaseWindow.getFocusedWindow() as any;
|
||||
const wins = TopLevelWindow.getAllWindows();
|
||||
if (!wins || wins.indexOf(window) === -1) {
|
||||
window = TopLevelWindow.getFocusedWindow();
|
||||
if (!window && wins && wins.length > 0) {
|
||||
window = wins[0] as any;
|
||||
window = wins[0];
|
||||
}
|
||||
if (!window) {
|
||||
throw new Error('Cannot open Menu without a BaseWindow present');
|
||||
throw new Error('Cannot open Menu without a TopLevelWindow present');
|
||||
}
|
||||
}
|
||||
|
||||
this.popupAt(window as unknown as BaseWindow, x, y, positioningItem, callback);
|
||||
this.popupAt(window, x, y, positioningItem, callback);
|
||||
return { browserWindow: window, x, y, position: positioningItem };
|
||||
};
|
||||
|
||||
Menu.prototype.closePopup = function (window) {
|
||||
if (window instanceof BaseWindow) {
|
||||
if (window instanceof TopLevelWindow) {
|
||||
this.closePopupAt(window.id);
|
||||
} else {
|
||||
// Passing -1 (invalid) would make closePopupAt close the all menu runners
|
||||
@@ -101,9 +102,8 @@ Menu.prototype.getMenuItemById = function (id) {
|
||||
|
||||
let found = items.find(item => item.id === id) || null;
|
||||
for (let i = 0; !found && i < items.length; i++) {
|
||||
const { submenu } = items[i];
|
||||
if (submenu) {
|
||||
found = submenu.getMenuItemById(id);
|
||||
if (items[i].submenu) {
|
||||
found = items[i].submenu.getMenuItemById(id);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
@@ -155,7 +155,7 @@ Menu.getApplicationMenu = () => applicationMenu;
|
||||
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder;
|
||||
|
||||
// set application menu with a preexisting menu
|
||||
Menu.setApplicationMenu = function (menu: MenuType) {
|
||||
Menu.setApplicationMenu = function (menu) {
|
||||
if (menu && menu.constructor !== Menu) {
|
||||
throw new TypeError('Invalid menu');
|
||||
}
|
||||
@@ -168,7 +168,7 @@ Menu.setApplicationMenu = function (menu: MenuType) {
|
||||
menu._callMenuWillShow();
|
||||
bindings.setApplicationMenu(menu);
|
||||
} else {
|
||||
const windows = BaseWindow.getAllWindows();
|
||||
const windows = TopLevelWindow.getAllWindows();
|
||||
return windows.map(w => w.setMenu(menu));
|
||||
}
|
||||
};
|
||||
@@ -200,7 +200,7 @@ Menu.buildFromTemplate = function (template) {
|
||||
/* Helper Functions */
|
||||
|
||||
// validate the template against having the wrong attribute
|
||||
function areValidTemplateItems (template: (MenuItemConstructorOptions | MenuItem)[]) {
|
||||
function areValidTemplateItems (template) {
|
||||
return template.every(item =>
|
||||
item != null &&
|
||||
typeof item === 'object' &&
|
||||
@@ -209,7 +209,7 @@ function areValidTemplateItems (template: (MenuItemConstructorOptions | MenuItem
|
||||
item.type === 'separator'));
|
||||
}
|
||||
|
||||
function sortTemplate (template: (MenuItemConstructorOptions | MenuItem)[]) {
|
||||
function sortTemplate (template) {
|
||||
const sorted = sortMenuItems(template);
|
||||
for (const item of sorted) {
|
||||
if (Array.isArray(item.submenu)) {
|
||||
@@ -220,15 +220,15 @@ function sortTemplate (template: (MenuItemConstructorOptions | MenuItem)[]) {
|
||||
}
|
||||
|
||||
// Search between separators to find a radio menu item and return its group id
|
||||
function generateGroupId (items: (MenuItemConstructorOptions | MenuItem)[], pos: number) {
|
||||
function generateGroupId (items, pos) {
|
||||
if (pos > 0) {
|
||||
for (let idx = pos - 1; idx >= 0; idx--) {
|
||||
if (items[idx].type === 'radio') return (items[idx] as any).groupId;
|
||||
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] as any).groupId;
|
||||
if (items[idx].type === 'radio') return items[idx].groupId;
|
||||
if (items[idx].type === 'separator') break;
|
||||
}
|
||||
}
|
||||
@@ -236,7 +236,7 @@ function generateGroupId (items: (MenuItemConstructorOptions | MenuItem)[], pos:
|
||||
return groupIdIndex;
|
||||
}
|
||||
|
||||
function removeExtraSeparators (items: (MenuItemConstructorOptions | MenuItem)[]) {
|
||||
function removeExtraSeparators (items) {
|
||||
// fold adjacent separators together
|
||||
let ret = items.filter((e, idx, arr) => {
|
||||
if (e.visible === false) return true;
|
||||
@@ -252,7 +252,7 @@ function removeExtraSeparators (items: (MenuItemConstructorOptions | MenuItem)[]
|
||||
return ret;
|
||||
}
|
||||
|
||||
function insertItemByType (this: MenuType, item: MenuItem, pos: number) {
|
||||
function insertItemByType (item, pos) {
|
||||
const types = {
|
||||
normal: () => this.insertItem(pos, item.commandId, item.label),
|
||||
checkbox: () => this.insertCheckItem(pos, item.commandId, item.label),
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MessagePortMain } from '@electron/internal/browser/message-port-main';
|
||||
const { createPair } = process._linkedBinding('electron_browser_message_port');
|
||||
const { createPair } = process.electronBinding('message_port');
|
||||
|
||||
export default class MessageChannelMain {
|
||||
port1: MessagePortMain;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
export const browserModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
{ name: 'app', loader: () => require('./app') },
|
||||
{ name: 'autoUpdater', loader: () => require('./auto-updater') },
|
||||
{ name: 'BaseWindow', loader: () => require('./base-window') },
|
||||
{ name: 'BrowserView', loader: () => require('./browser-view') },
|
||||
{ name: 'BrowserWindow', loader: () => require('./browser-window') },
|
||||
{ name: 'contentTracing', loader: () => require('./content-tracing') },
|
||||
@@ -26,6 +25,7 @@ export const browserModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
{ name: 'screen', loader: () => require('./screen') },
|
||||
{ name: 'session', loader: () => require('./session') },
|
||||
{ name: 'systemPreferences', loader: () => require('./system-preferences') },
|
||||
{ name: 'TopLevelWindow', loader: () => require('./top-level-window') },
|
||||
{ name: 'TouchBar', loader: () => require('./touch-bar') },
|
||||
{ name: 'Tray', loader: () => require('./tray') },
|
||||
{ name: 'View', loader: () => require('./view') },
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
export const browserModuleNames = [
|
||||
'app',
|
||||
'autoUpdater',
|
||||
'BaseWindow',
|
||||
'BrowserView',
|
||||
'BrowserWindow',
|
||||
'contentTracing',
|
||||
@@ -29,6 +28,7 @@ export const browserModuleNames = [
|
||||
'screen',
|
||||
'session',
|
||||
'systemPreferences',
|
||||
'TopLevelWindow',
|
||||
'TouchBar',
|
||||
'Tray',
|
||||
'View',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
const { NativeTheme, nativeTheme } = process._linkedBinding('electron_common_native_theme');
|
||||
const { NativeTheme, nativeTheme } = process.electronBinding('native_theme');
|
||||
|
||||
Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype);
|
||||
EventEmitter.call(nativeTheme as any);
|
||||
|
||||
32
lib/browser/api/net-log.js
Normal file
32
lib/browser/api/net-log.js
Normal file
@@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
// TODO(deepak1556): Deprecate and remove standalone netLog module,
|
||||
// it is now a property of session module.
|
||||
const { app, session } = require('electron');
|
||||
|
||||
// Fallback to default session.
|
||||
Object.setPrototypeOf(module.exports, new Proxy({}, {
|
||||
get (target, property) {
|
||||
if (!app.isReady()) return;
|
||||
|
||||
const netLog = session.defaultSession.netLog;
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(Object.getPrototypeOf(netLog), property)) return;
|
||||
|
||||
// check for properties on the prototype chain that aren't functions
|
||||
if (typeof netLog[property] !== 'function') return netLog[property];
|
||||
|
||||
// Returning a native function directly would throw error.
|
||||
return (...args) => netLog[property](...args);
|
||||
},
|
||||
|
||||
ownKeys () {
|
||||
if (!app.isReady()) return [];
|
||||
|
||||
return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog));
|
||||
},
|
||||
|
||||
getOwnPropertyDescriptor (target) {
|
||||
return { configurable: true, enumerable: true };
|
||||
}
|
||||
}));
|
||||
@@ -1,22 +0,0 @@
|
||||
// TODO(deepak1556): Deprecate and remove standalone netLog module,
|
||||
// it is now a property of session module.
|
||||
import { app, session } from 'electron';
|
||||
|
||||
const startLogging: typeof session.defaultSession.netLog.startLogging = async (path, options) => {
|
||||
if (!app.isReady()) return;
|
||||
return session.defaultSession.netLog.startLogging(path, options);
|
||||
};
|
||||
|
||||
const stopLogging: typeof session.defaultSession.netLog.stopLogging = async () => {
|
||||
if (!app.isReady()) return;
|
||||
return session.defaultSession.netLog.stopLogging();
|
||||
};
|
||||
|
||||
export default {
|
||||
startLogging,
|
||||
stopLogging,
|
||||
get currentlyLogging (): boolean {
|
||||
if (!app.isReady()) return false;
|
||||
return session.defaultSession.netLog.currentlyLogging;
|
||||
}
|
||||
};
|
||||
@@ -2,13 +2,7 @@ import * as url from 'url';
|
||||
import { Readable, Writable } from 'stream';
|
||||
import { app } from 'electron';
|
||||
import { ClientRequestConstructorOptions, UploadProgress } from 'electron/main';
|
||||
const {
|
||||
net,
|
||||
Net,
|
||||
isValidHeaderName,
|
||||
isValidHeaderValue,
|
||||
createURLLoader
|
||||
} = process._linkedBinding('electron_browser_net');
|
||||
const { net, Net, isValidHeaderName, isValidHeaderValue, createURLLoader } = process.electronBinding('net');
|
||||
|
||||
const kSupportedProtocols = new Set(['http:', 'https:']);
|
||||
|
||||
@@ -300,10 +294,6 @@ class ClientRequest extends Writable implements Electron.ClientRequest {
|
||||
this._redirectPolicy = redirectPolicy;
|
||||
}
|
||||
|
||||
get chunkedEncoding () {
|
||||
return this._chunkedEncoding || false;
|
||||
}
|
||||
|
||||
set chunkedEncoding (value: boolean) {
|
||||
if (this._started) {
|
||||
throw new Error('chunkedEncoding can only be set before the request is started');
|
||||
|
||||
7
lib/browser/api/notification.js
Normal file
7
lib/browser/api/notification.js
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { Notification, isSupported } = process.electronBinding('notification');
|
||||
|
||||
Notification.isSupported = isSupported;
|
||||
|
||||
module.exports = Notification;
|
||||
@@ -1,8 +0,0 @@
|
||||
const {
|
||||
Notification: ElectronNotification,
|
||||
isSupported
|
||||
} = process._linkedBinding('electron_common_notification');
|
||||
|
||||
ElectronNotification.isSupported = isSupported;
|
||||
|
||||
export default ElectronNotification;
|
||||
@@ -1,11 +1,7 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { app } from 'electron';
|
||||
|
||||
const {
|
||||
createPowerMonitor,
|
||||
getSystemIdleState,
|
||||
getSystemIdleTime
|
||||
} = process._linkedBinding('electron_browser_power_monitor');
|
||||
const { createPowerMonitor, getSystemIdleState, getSystemIdleTime } = process.electronBinding('power_monitor');
|
||||
|
||||
class PowerMonitor extends EventEmitter {
|
||||
constructor () {
|
||||
|
||||
3
lib/browser/api/power-save-blocker.js
Normal file
3
lib/browser/api/power-save-blocker.js
Normal file
@@ -0,0 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = process.electronBinding('power_save_blocker').powerSaveBlocker;
|
||||
@@ -1,2 +0,0 @@
|
||||
const { powerSaveBlocker } = process._linkedBinding('electron_browser_power_save_blocker');
|
||||
export default powerSaveBlocker;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { app, session } from 'electron';
|
||||
|
||||
// Global protocol APIs.
|
||||
const protocol = process._linkedBinding('electron_browser_protocol');
|
||||
const protocol = process.electronBinding('protocol');
|
||||
|
||||
// Fallback protocol APIs of default session.
|
||||
Object.setPrototypeOf(protocol, new Proxy({}, {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { createLazyInstance } from '../utils';
|
||||
const { EventEmitter } = require('events');
|
||||
const { Screen, createScreen } = process._linkedBinding('electron_common_screen');
|
||||
const { Screen, createScreen } = process.electronBinding('screen');
|
||||
|
||||
// Screen is an EventEmitter.
|
||||
Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype);
|
||||
|
||||
17
lib/browser/api/session.js
Normal file
17
lib/browser/api/session.js
Normal file
@@ -0,0 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
const { EventEmitter } = require('events');
|
||||
const { app, deprecate } = require('electron');
|
||||
const { fromPartition } = process.electronBinding('session');
|
||||
|
||||
// Public API.
|
||||
Object.defineProperties(exports, {
|
||||
defaultSession: {
|
||||
enumerable: true,
|
||||
get () { return fromPartition(''); }
|
||||
},
|
||||
fromPartition: {
|
||||
enumerable: true,
|
||||
value: fromPartition
|
||||
}
|
||||
});
|
||||
@@ -1,8 +0,0 @@
|
||||
const { fromPartition } = process._linkedBinding('electron_browser_session');
|
||||
|
||||
export default {
|
||||
fromPartition,
|
||||
get defaultSession () {
|
||||
return fromPartition('');
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { deprecate } from 'electron';
|
||||
const { systemPreferences, SystemPreferences } = process._linkedBinding('electron_browser_system_preferences');
|
||||
const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences');
|
||||
|
||||
// SystemPreferences is an EventEmitter.
|
||||
Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import type { BaseWindow as TLWT } from 'electron';
|
||||
const { BaseWindow } = process._linkedBinding('electron_browser_base_window') as { BaseWindow: typeof TLWT };
|
||||
'use strict';
|
||||
|
||||
Object.setPrototypeOf(BaseWindow.prototype, EventEmitter.prototype);
|
||||
const electron = require('electron');
|
||||
const { EventEmitter } = require('events');
|
||||
const { TopLevelWindow } = process.electronBinding('top_level_window');
|
||||
|
||||
(BaseWindow.prototype as any)._init = function () {
|
||||
Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype);
|
||||
|
||||
TopLevelWindow.prototype._init = function () {
|
||||
// Avoid recursive require.
|
||||
const { app } = require('electron');
|
||||
const { app } = electron;
|
||||
|
||||
// Simulate the application menu on platforms other than macOS.
|
||||
if (process.platform !== 'darwin') {
|
||||
@@ -17,88 +19,88 @@ Object.setPrototypeOf(BaseWindow.prototype, EventEmitter.prototype);
|
||||
|
||||
// Properties
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'autoHideMenuBar', {
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'autoHideMenuBar', {
|
||||
get: function () { return this.isMenuBarAutoHide(); },
|
||||
set: function (autoHide) { this.setAutoHideMenuBar(autoHide); }
|
||||
});
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'visibleOnAllWorkspaces', {
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'visibleOnAllWorkspaces', {
|
||||
get: function () { return this.isVisibleOnAllWorkspaces(); },
|
||||
set: function (visible) { this.setVisibleOnAllWorkspaces(visible); }
|
||||
});
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'fullScreen', {
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'fullScreen', {
|
||||
get: function () { return this.isFullScreen(); },
|
||||
set: function (full) { this.setFullScreen(full); }
|
||||
});
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'simpleFullScreen', {
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'simpleFullScreen', {
|
||||
get: function () { return this.isSimpleFullScreen(); },
|
||||
set: function (simple) { this.setSimpleFullScreen(simple); }
|
||||
});
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'kiosk', {
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'kiosk', {
|
||||
get: function () { return this.isKiosk(); },
|
||||
set: function (kiosk) { this.setKiosk(kiosk); }
|
||||
});
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'documentEdited', {
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'documentEdited', {
|
||||
get: function () { return this.isFullscreen(); },
|
||||
set: function (edited) { this.setDocumentEdited(edited); }
|
||||
});
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'shadow', {
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'shadow', {
|
||||
get: function () { return this.hasShadow(); },
|
||||
set: function (shadow) { this.setHasShadow(shadow); }
|
||||
});
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'representedFilename', {
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'representedFilename', {
|
||||
get: function () { return this.getRepresentedFilename(); },
|
||||
set: function (filename) { this.setRepresentedFilename(filename); }
|
||||
});
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'minimizable', {
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'minimizable', {
|
||||
get: function () { return this.isMinimizable(); },
|
||||
set: function (min) { this.setMinimizable(min); }
|
||||
});
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'title', {
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'title', {
|
||||
get: function () { return this.getTitle(); },
|
||||
set: function (title) { this.setTitle(title); }
|
||||
});
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'maximizable', {
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'maximizable', {
|
||||
get: function () { return this.isMaximizable(); },
|
||||
set: function (max) { this.setMaximizable(max); }
|
||||
});
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'resizable', {
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'resizable', {
|
||||
get: function () { return this.isResizable(); },
|
||||
set: function (res) { this.setResizable(res); }
|
||||
});
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'menuBarVisible', {
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'menuBarVisible', {
|
||||
get: function () { return this.isMenuBarVisible(); },
|
||||
set: function (visible) { this.setMenuBarVisibility(visible); }
|
||||
});
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'fullScreenable', {
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'fullScreenable', {
|
||||
get: function () { return this.isFullScreenable(); },
|
||||
set: function (full) { this.setFullScreenable(full); }
|
||||
});
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'closable', {
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'closable', {
|
||||
get: function () { return this.isClosable(); },
|
||||
set: function (close) { this.setClosable(close); }
|
||||
});
|
||||
|
||||
Object.defineProperty(BaseWindow.prototype, 'movable', {
|
||||
Object.defineProperty(TopLevelWindow.prototype, 'movable', {
|
||||
get: function () { return this.isMovable(); },
|
||||
set: function (move) { this.setMovable(move); }
|
||||
});
|
||||
|
||||
BaseWindow.getFocusedWindow = () => {
|
||||
return BaseWindow.getAllWindows().find((win) => win.isFocused());
|
||||
TopLevelWindow.getFocusedWindow = () => {
|
||||
return TopLevelWindow.getAllWindows().find((win) => win.isFocused());
|
||||
};
|
||||
|
||||
module.exports = BaseWindow;
|
||||
module.exports = TopLevelWindow;
|
||||
5
lib/browser/api/tray.js
Normal file
5
lib/browser/api/tray.js
Normal file
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
const { Tray } = process.electronBinding('tray');
|
||||
|
||||
module.exports = Tray;
|
||||
@@ -1,3 +0,0 @@
|
||||
const { Tray } = process._linkedBinding('electron_browser_tray');
|
||||
|
||||
export default Tray;
|
||||
5
lib/browser/api/view.js
Normal file
5
lib/browser/api/view.js
Normal file
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
const { View } = process.electronBinding('view');
|
||||
|
||||
module.exports = View;
|
||||
@@ -1,3 +0,0 @@
|
||||
const { View } = process._linkedBinding('electron_browser_view');
|
||||
|
||||
export default View;
|
||||
8
lib/browser/api/views/image-view.js
Normal file
8
lib/browser/api/views/image-view.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const electron = require('electron');
|
||||
|
||||
const { View } = electron;
|
||||
const { ImageView } = process.electronBinding('image_view');
|
||||
|
||||
Object.setPrototypeOf(ImageView.prototype, View.prototype);
|
||||
|
||||
module.exports = ImageView;
|
||||
@@ -1,7 +0,0 @@
|
||||
import { View } from 'electron';
|
||||
|
||||
const { ImageView } = process._linkedBinding('electron_browser_image_view');
|
||||
|
||||
Object.setPrototypeOf(ImageView.prototype, View.prototype);
|
||||
|
||||
export default ImageView;
|
||||
10
lib/browser/api/web-contents-view.js
Normal file
10
lib/browser/api/web-contents-view.js
Normal file
@@ -0,0 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
|
||||
const { View } = electron;
|
||||
const { WebContentsView } = process.electronBinding('web_contents_view');
|
||||
|
||||
Object.setPrototypeOf(WebContentsView.prototype, View.prototype);
|
||||
|
||||
module.exports = WebContentsView;
|
||||
@@ -1,7 +0,0 @@
|
||||
import { View } from 'electron';
|
||||
|
||||
const { WebContentsView } = process._linkedBinding('electron_browser_web_contents_view');
|
||||
|
||||
Object.setPrototypeOf(WebContentsView.prototype, View.prototype);
|
||||
|
||||
export default WebContentsView;
|
||||
@@ -1,15 +1,17 @@
|
||||
import { app, ipcMain, session, deprecate } from 'electron';
|
||||
import type { MenuItem, MenuItemConstructorOptions, WebContentsInternal } from 'electron';
|
||||
'use strict';
|
||||
|
||||
import * as url from 'url';
|
||||
import * as path from 'path';
|
||||
import { internalWindowOpen } from '../guest-window-manager';
|
||||
import { NavigationController } from '../navigation-controller';
|
||||
import { ipcMainInternal } from '../ipc-main-internal';
|
||||
import * as ipcMainUtils from '../ipc-main-internal-utils';
|
||||
import { parseFeatures } from '../../common/parse-features-string';
|
||||
import { MessagePortMain } from '../message-port-main';
|
||||
import { EventEmitter } from 'events';
|
||||
const { EventEmitter } = require('events');
|
||||
const electron = require('electron');
|
||||
const path = require('path');
|
||||
const url = require('url');
|
||||
const { app, ipcMain, session } = 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 { parseFeatures } = require('@electron/internal/common/parse-features-string');
|
||||
const { MessagePortMain } = require('@electron/internal/browser/message-port-main');
|
||||
|
||||
// session is not used here, the purpose is to make sure session is initalized
|
||||
// before the webContents module.
|
||||
@@ -21,18 +23,8 @@ const getNextId = function () {
|
||||
return ++nextId;
|
||||
};
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
type MediaSize = {
|
||||
name: string,
|
||||
custom_display_name: string,
|
||||
height_microns: number,
|
||||
width_microns: number,
|
||||
is_default?: 'true',
|
||||
}
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
// Stock page sizes
|
||||
const PDFPageSizes: Record<string, MediaSize> = {
|
||||
const PDFPageSizes = {
|
||||
A5: {
|
||||
custom_display_name: 'A5',
|
||||
height_microns: 210000,
|
||||
@@ -75,8 +67,8 @@ const PDFPageSizes: Record<string, MediaSize> = {
|
||||
// Default printing setting
|
||||
const defaultPrintingSetting = {
|
||||
// Customizable.
|
||||
pageRange: [] as {from: number, to: number}[],
|
||||
mediaSize: {} as MediaSize,
|
||||
pageRange: [],
|
||||
mediaSize: {},
|
||||
landscape: false,
|
||||
headerFooterEnabled: false,
|
||||
marginsType: 0,
|
||||
@@ -101,18 +93,18 @@ const defaultPrintingSetting = {
|
||||
copies: 1,
|
||||
// 2 = color - see ColorModel in //printing/print_job_constants.h
|
||||
color: 2,
|
||||
collate: true,
|
||||
printerType: 2,
|
||||
title: undefined as string | undefined,
|
||||
url: undefined as string | undefined
|
||||
collate: true
|
||||
};
|
||||
|
||||
// JavaScript implementations of WebContents.
|
||||
const binding = process._linkedBinding('electron_browser_web_contents');
|
||||
const { WebContents } = binding as { WebContents: { prototype: WebContentsInternal } };
|
||||
const binding = process.electronBinding('web_contents');
|
||||
const { WebContents } = binding;
|
||||
|
||||
Object.setPrototypeOf(WebContents.prototype, EventEmitter.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');
|
||||
@@ -131,6 +123,17 @@ WebContents.prototype.postMessage = function (...args) {
|
||||
this._postMessage(...args);
|
||||
};
|
||||
|
||||
WebContents.prototype.sendToAll = function (channel, ...args) {
|
||||
if (typeof channel !== 'string') {
|
||||
throw new Error('Missing required channel argument');
|
||||
}
|
||||
|
||||
const internal = false;
|
||||
const sendToAll = true;
|
||||
|
||||
return this._send(internal, sendToAll, channel, args);
|
||||
};
|
||||
|
||||
WebContents.prototype._sendInternal = function (channel, ...args) {
|
||||
if (typeof channel !== 'string') {
|
||||
throw new Error('Missing required channel argument');
|
||||
@@ -182,15 +185,15 @@ const webFrameMethods = [
|
||||
'insertText',
|
||||
'removeInsertedCSS',
|
||||
'setVisualZoomLevelLimits'
|
||||
] as ('insertCSS' | 'insertText' | 'removeInsertedCSS' | 'setVisualZoomLevelLimits')[];
|
||||
];
|
||||
|
||||
for (const method of webFrameMethods) {
|
||||
WebContents.prototype[method] = function (...args: any[]): Promise<any> {
|
||||
WebContents.prototype[method] = function (...args) {
|
||||
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, ...args);
|
||||
};
|
||||
}
|
||||
|
||||
const waitTillCanExecuteJavaScript = async (webContents: WebContentsInternal) => {
|
||||
const waitTillCanExecuteJavaScript = async (webContents) => {
|
||||
if (webContents.getURL() && !webContents.isLoadingMainFrame()) return;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
@@ -204,15 +207,17 @@ const waitTillCanExecuteJavaScript = async (webContents: WebContentsInternal) =>
|
||||
// WebContents has been loaded.
|
||||
WebContents.prototype.executeJavaScript = async function (code, hasUserGesture) {
|
||||
await waitTillCanExecuteJavaScript(this);
|
||||
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScript', String(code), !!hasUserGesture);
|
||||
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScript', code, hasUserGesture);
|
||||
};
|
||||
WebContents.prototype.executeJavaScriptInIsolatedWorld = async function (worldId, code, hasUserGesture) {
|
||||
WebContents.prototype.executeJavaScriptInIsolatedWorld = async function (code, hasUserGesture) {
|
||||
await waitTillCanExecuteJavaScript(this);
|
||||
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScriptInIsolatedWorld', worldId, code, !!hasUserGesture);
|
||||
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScriptInIsolatedWorld', code, hasUserGesture);
|
||||
};
|
||||
|
||||
// Translate the options of printToPDF.
|
||||
WebContents.prototype.printToPDF = function (options) {
|
||||
|
||||
let pendingPromise;
|
||||
WebContents.prototype.printToPDF = async function (options) {
|
||||
const printSettings = {
|
||||
...defaultPrintingSetting,
|
||||
requestID: getNextId()
|
||||
@@ -323,7 +328,7 @@ WebContents.prototype.printToPDF = function (options) {
|
||||
height_microns: Math.ceil(pageSize.height),
|
||||
width_microns: Math.ceil(pageSize.width)
|
||||
};
|
||||
} else if (Object.prototype.hasOwnProperty.call(PDFPageSizes, pageSize)) {
|
||||
} else if (PDFPageSizes[pageSize]) {
|
||||
printSettings.mediaSize = PDFPageSizes[pageSize];
|
||||
} else {
|
||||
const error = new Error(`Unsupported pageSize: ${pageSize}`);
|
||||
@@ -338,7 +343,12 @@ WebContents.prototype.printToPDF = function (options) {
|
||||
// PrinterType enum from //printing/print_job_constants.h
|
||||
printSettings.printerType = 2;
|
||||
if (this._printToPDF) {
|
||||
return this._printToPDF(printSettings);
|
||||
if (pendingPromise) {
|
||||
pendingPromise = pendingPromise.then(() => this._printToPDF(printSettings));
|
||||
} else {
|
||||
pendingPromise = this._printToPDF(printSettings);
|
||||
}
|
||||
return pendingPromise;
|
||||
} else {
|
||||
const error = new Error('Printing feature is disabled');
|
||||
return Promise.reject(error);
|
||||
@@ -357,14 +367,14 @@ WebContents.prototype.print = function (options = {}, callback) {
|
||||
throw new Error('height and width properties are required for pageSize');
|
||||
}
|
||||
// Dimensions in Microns - 1 meter = 10^6 microns
|
||||
(options as any).mediaSize = {
|
||||
options.mediaSize = {
|
||||
name: 'CUSTOM',
|
||||
custom_display_name: 'Custom',
|
||||
height_microns: Math.ceil(pageSize.height),
|
||||
width_microns: Math.ceil(pageSize.width)
|
||||
};
|
||||
} else if (PDFPageSizes[pageSize]) {
|
||||
(options as any).mediaSize = PDFPageSizes[pageSize];
|
||||
options.mediaSize = PDFPageSizes[pageSize];
|
||||
} else {
|
||||
throw new Error(`Unsupported pageSize: ${pageSize}`);
|
||||
}
|
||||
@@ -407,23 +417,23 @@ WebContents.prototype.loadFile = function (filePath, options = {}) {
|
||||
}));
|
||||
};
|
||||
|
||||
const addReplyToEvent = (event: any) => {
|
||||
event.reply = (...args: any[]) => {
|
||||
const addReplyToEvent = (event) => {
|
||||
event.reply = (...args) => {
|
||||
event.sender.sendToFrame(event.frameId, ...args);
|
||||
};
|
||||
};
|
||||
|
||||
const addReplyInternalToEvent = (event: any) => {
|
||||
const addReplyInternalToEvent = (event) => {
|
||||
Object.defineProperty(event, '_replyInternal', {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
value: (...args: any[]) => {
|
||||
value: (...args) => {
|
||||
event.sender._sendToFrameInternal(event.frameId, ...args);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const addReturnValueToEvent = (event: any) => {
|
||||
const addReturnValueToEvent = (event) => {
|
||||
Object.defineProperty(event, 'returnValue', {
|
||||
set: (value) => event.sendReply([value]),
|
||||
get: () => {}
|
||||
@@ -433,30 +443,14 @@ const addReturnValueToEvent = (event: any) => {
|
||||
// Add JavaScript wrappers for WebContents class.
|
||||
WebContents.prototype._init = function () {
|
||||
// The navigation controller.
|
||||
const navigationController = new NavigationController(this);
|
||||
this.loadURL = navigationController.loadURL.bind(navigationController);
|
||||
this.getURL = navigationController.getURL.bind(navigationController);
|
||||
this.stop = navigationController.stop.bind(navigationController);
|
||||
this.reload = navigationController.reload.bind(navigationController);
|
||||
this.reloadIgnoringCache = navigationController.reloadIgnoringCache.bind(navigationController);
|
||||
this.canGoBack = navigationController.canGoBack.bind(navigationController);
|
||||
this.canGoForward = navigationController.canGoForward.bind(navigationController);
|
||||
this.canGoToIndex = navigationController.canGoToIndex.bind(navigationController);
|
||||
this.canGoToOffset = navigationController.canGoToOffset.bind(navigationController);
|
||||
this.clearHistory = navigationController.clearHistory.bind(navigationController);
|
||||
this.goBack = navigationController.goBack.bind(navigationController);
|
||||
this.goForward = navigationController.goForward.bind(navigationController);
|
||||
this.goToIndex = navigationController.goToIndex.bind(navigationController);
|
||||
this.goToOffset = navigationController.goToOffset.bind(navigationController);
|
||||
this.getActiveIndex = navigationController.getActiveIndex.bind(navigationController);
|
||||
this.length = navigationController.length.bind(navigationController);
|
||||
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);
|
||||
|
||||
// Dispatch IPC messages to the ipc module.
|
||||
this.on('-ipc-message' as any, function (this: WebContentsInternal, event: any, internal: boolean, channel: string, args: any[]) {
|
||||
this.on('-ipc-message', function (event, internal, channel, args) {
|
||||
if (internal) {
|
||||
addReplyInternalToEvent(event);
|
||||
ipcMainInternal.emit(channel, event, ...args);
|
||||
@@ -467,21 +461,21 @@ WebContents.prototype._init = function () {
|
||||
}
|
||||
});
|
||||
|
||||
this.on('-ipc-invoke' as any, function (event: any, internal: boolean, channel: string, args: any[]) {
|
||||
event._reply = (result: any) => event.sendReply({ result });
|
||||
event._throw = (error: Error) => {
|
||||
this.on('-ipc-invoke', function (event, internal, channel, args) {
|
||||
event._reply = (result) => event.sendReply({ result });
|
||||
event._throw = (error) => {
|
||||
console.error(`Error occurred in handler for '${channel}':`, error);
|
||||
event.sendReply({ error: error.toString() });
|
||||
};
|
||||
const target = internal ? ipcMainInternal : ipcMain;
|
||||
if ((target as any)._invokeHandlers.has(channel)) {
|
||||
(target as any)._invokeHandlers.get(channel)(event, ...args);
|
||||
if (target._invokeHandlers.has(channel)) {
|
||||
target._invokeHandlers.get(channel)(event, ...args);
|
||||
} else {
|
||||
event._throw(`No handler registered for '${channel}'`);
|
||||
}
|
||||
});
|
||||
|
||||
this.on('-ipc-message-sync' as any, function (this: WebContentsInternal, event: any, internal: boolean, channel: string, args: any[]) {
|
||||
this.on('-ipc-message-sync', function (event, internal, channel, args) {
|
||||
addReturnValueToEvent(event);
|
||||
if (internal) {
|
||||
addReplyInternalToEvent(event);
|
||||
@@ -493,15 +487,15 @@ WebContents.prototype._init = function () {
|
||||
}
|
||||
});
|
||||
|
||||
this.on('-ipc-ports' as any, function (event: any, internal: boolean, channel: string, message: any, ports: any[]) {
|
||||
this.on('-ipc-ports', function (event, internal, channel, message, ports) {
|
||||
event.ports = ports.map(p => new MessagePortMain(p));
|
||||
ipcMain.emit(channel, event, message);
|
||||
});
|
||||
|
||||
// Handle context menu action request from pepper plugin.
|
||||
this.on('pepper-context-menu' as any, function (event: any, params: {x: number, y: number, menu: Array<(MenuItemConstructorOptions) | (MenuItem)>}, callback: () => void) {
|
||||
this.on('pepper-context-menu', function (event, params, callback) {
|
||||
// Access Menu via electron.Menu to prevent circular require.
|
||||
const menu = require('electron').Menu.buildFromTemplate(params.menu);
|
||||
const menu = electron.Menu.buildFromTemplate(params.menu);
|
||||
menu.popup({
|
||||
window: event.sender.getOwnerBrowserWindow(),
|
||||
x: params.x,
|
||||
@@ -519,14 +513,14 @@ WebContents.prototype._init = function () {
|
||||
});
|
||||
|
||||
// The devtools requests the webContents to reload.
|
||||
this.on('devtools-reload-page', function (this: WebContentsInternal) {
|
||||
this.on('devtools-reload-page', function () {
|
||||
this.reload();
|
||||
});
|
||||
|
||||
if (this.getType() !== 'remote') {
|
||||
// Make new windows requested by links behave like "window.open".
|
||||
this.on('-new-window' as any, (event: any, url: string, frameName: string, disposition: string,
|
||||
rawFeatures: string, referrer: string, postData: string) => {
|
||||
this.on('-new-window', (event, url, frameName, disposition,
|
||||
rawFeatures, referrer, postData) => {
|
||||
const { options, webPreferences, additionalFeatures } = parseFeatures(rawFeatures);
|
||||
const mergedOptions = {
|
||||
show: true,
|
||||
@@ -542,9 +536,9 @@ WebContents.prototype._init = function () {
|
||||
|
||||
// Create a new browser window for the native implementation of
|
||||
// "window.open", used in sandbox and nativeWindowOpen mode.
|
||||
this.on('-add-new-contents' as any, (event: any, webContents: WebContentsInternal, disposition: string,
|
||||
userGesture: boolean, left: number, top: number, width: number, height: number, url: string, frameName: string,
|
||||
referrer: string, rawFeatures: string, postData: string) => {
|
||||
this.on('-add-new-contents', (event, webContents, disposition,
|
||||
userGesture, left, top, width, height, url, frameName,
|
||||
referrer, rawFeatures, postData) => {
|
||||
if ((disposition !== 'foreground-tab' && disposition !== 'new-window' &&
|
||||
disposition !== 'background-tab')) {
|
||||
event.preventDefault();
|
||||
@@ -567,7 +561,7 @@ WebContents.prototype._init = function () {
|
||||
|
||||
const prefs = this.getWebPreferences() || {};
|
||||
if (prefs.webviewTag && prefs.contextIsolation) {
|
||||
deprecate.log('Security Warning: A WebContents was just created with both webviewTag and contextIsolation enabled. This combination is fundamentally less secure and effectively bypasses the protections of contextIsolation. We strongly recommend you move away from webviews to OOPIF or BrowserView in order for your app to be more secure');
|
||||
electron.deprecate.log('Security Warning: A WebContents was just created with both webviewTag and contextIsolation enabled. This combination is fundamentally less secure and effectively bypasses the protections of contextIsolation. We strongly recommend you move away from webviews to OOPIF or BrowserView in order for your app to be more secure');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,7 +569,7 @@ WebContents.prototype._init = function () {
|
||||
app.emit('login', event, this, ...args);
|
||||
});
|
||||
|
||||
const event = process._linkedBinding('electron_browser_event').createEmpty();
|
||||
const event = process.electronBinding('event').createEmpty();
|
||||
app.emit('web-contents-created', event, this);
|
||||
|
||||
// Properties
|
||||
@@ -612,25 +606,28 @@ WebContents.prototype._init = function () {
|
||||
};
|
||||
|
||||
// Public APIs.
|
||||
export function create (options = {}) {
|
||||
return binding.create(options);
|
||||
}
|
||||
module.exports = {
|
||||
create (options = {}) {
|
||||
return binding.create(options);
|
||||
},
|
||||
|
||||
export function fromId (id: string) {
|
||||
return binding.fromId(id);
|
||||
}
|
||||
fromId (id) {
|
||||
return binding.fromId(id);
|
||||
},
|
||||
|
||||
export function getFocusedWebContents () {
|
||||
let focused = null;
|
||||
for (const contents of binding.getAllWebContents()) {
|
||||
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;
|
||||
getFocusedWebContents () {
|
||||
let focused = null;
|
||||
for (const contents of binding.getAllWebContents()) {
|
||||
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;
|
||||
}
|
||||
return focused;
|
||||
},
|
||||
|
||||
getAllWebContents () {
|
||||
return binding.getAllWebContents();
|
||||
}
|
||||
return focused;
|
||||
}
|
||||
export function getAllWebContents () {
|
||||
return binding.getAllWebContents();
|
||||
}
|
||||
};
|
||||
@@ -1,21 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
// This is a temporary shim to aid in transition from the old
|
||||
// BrowserWindow-based extensions stuff to the new native-backed extensions
|
||||
// API.
|
||||
|
||||
import { app, session, BrowserWindow, deprecate } from 'electron';
|
||||
const { app, session, BrowserWindow, deprecate } = require('electron');
|
||||
|
||||
app.whenReady().then(function () {
|
||||
const addExtension = function (srcDirectory: string) {
|
||||
const addExtension = function (srcDirectory) {
|
||||
return session.defaultSession.loadExtension(srcDirectory);
|
||||
};
|
||||
|
||||
const removeExtension = function (name: string) {
|
||||
const removeExtension = function (name) {
|
||||
const extension = session.defaultSession.getAllExtensions().find(e => e.name === name);
|
||||
if (extension) { session.defaultSession.removeExtension(extension.id); }
|
||||
};
|
||||
|
||||
const getExtensions = function () {
|
||||
const extensions: Record<string, any> = {};
|
||||
const extensions = {};
|
||||
session.defaultSession.getAllExtensions().forEach(e => {
|
||||
extensions[e.name] = e;
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import { shell, Menu } from 'electron';
|
||||
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
|
||||
const isMac = process.platform === 'darwin';
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
const {
|
||||
createDesktopCapturer,
|
||||
getMediaSourceIdForWebContents: getMediaSourceIdForWebContentsBinding
|
||||
} = process._linkedBinding('electron_browser_desktop_capturer');
|
||||
const { createDesktopCapturer } = process.electronBinding('desktop_capturer');
|
||||
|
||||
const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b);
|
||||
|
||||
@@ -80,7 +77,3 @@ export const getSourcesImpl = (event: Electron.IpcMainEvent | null, args: Electr
|
||||
|
||||
return getSources;
|
||||
};
|
||||
|
||||
export const getMediaSourceIdForWebContents = (event: Electron.IpcMainEvent, webContentsId: number) => {
|
||||
return getMediaSourceIdForWebContentsBinding(event.sender.id, webContentsId);
|
||||
};
|
||||
|
||||
@@ -59,7 +59,7 @@ function sanitizeOptionsForGuest (options) {
|
||||
// Create a new guest instance.
|
||||
const createGuest = function (embedder, params) {
|
||||
if (webViewManager == null) {
|
||||
webViewManager = process._linkedBinding('electron_browser_web_view_manager');
|
||||
webViewManager = process.electronBinding('web_view_manager');
|
||||
}
|
||||
|
||||
const guest = webContents.create({
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
const electron = require('electron');
|
||||
const { BrowserWindow } = electron;
|
||||
const { isSameOrigin } = process._linkedBinding('electron_common_v8_util');
|
||||
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 { parseFeatures } = require('@electron/internal/common/parse-features-string');
|
||||
@@ -56,7 +56,7 @@ const mergeBrowserWindowOptions = function (embedder, options) {
|
||||
let parentOptions = embedder.browserWindowOptions;
|
||||
|
||||
// if parent's visibility is available, that overrides 'show' flag (#12125)
|
||||
const win = BrowserWindow.fromWebContents(embedder);
|
||||
const win = BrowserWindow.fromWebContents(embedder.webContents);
|
||||
if (win != null) {
|
||||
parentOptions = {
|
||||
...win.getBounds(),
|
||||
|
||||
@@ -17,7 +17,7 @@ require('../common/reset-search-paths');
|
||||
// Import common settings.
|
||||
require('@electron/internal/common/init');
|
||||
|
||||
process._linkedBinding('electron_browser_event_emitter').setEventEmitterPrototype(EventEmitter.prototype);
|
||||
process.electronBinding('event_emitter').setEventEmitterPrototype(EventEmitter.prototype);
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
// Redirect node's console to use our own implementations, since node can not
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ipcMainInternal } from './ipc-main-internal';
|
||||
import type { WebContents, LoadURLOptions } from 'electron/main';
|
||||
import { EventEmitter } from 'events';
|
||||
'use strict';
|
||||
|
||||
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) {
|
||||
@@ -16,7 +16,7 @@ ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', function (even
|
||||
});
|
||||
|
||||
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) {
|
||||
event.returnValue = (event.sender as any).length();
|
||||
event.returnValue = event.sender.length();
|
||||
});
|
||||
|
||||
// JavaScript implementation of Chromium's NavigationController.
|
||||
@@ -24,14 +24,9 @@ ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) {
|
||||
// control on user land, and only rely on WebContents.loadURL for navigation.
|
||||
// This helps us avoid Chromium's various optimizations so we can ensure renderer
|
||||
// process is restarted everytime.
|
||||
export class NavigationController extends EventEmitter {
|
||||
currentIndex: number = -1;
|
||||
inPageIndex: number = -1;
|
||||
pendingIndex: number = -1;
|
||||
history: string[] = [];
|
||||
|
||||
constructor (private webContents: WebContents) {
|
||||
super();
|
||||
const NavigationController = (function () {
|
||||
function NavigationController (webContents) {
|
||||
this.webContents = webContents;
|
||||
this.clearHistory();
|
||||
|
||||
// webContents may have already navigated to a page.
|
||||
@@ -39,7 +34,7 @@ export class NavigationController extends EventEmitter {
|
||||
this.currentIndex++;
|
||||
this.history.push(this.webContents._getURL());
|
||||
}
|
||||
this.webContents.on('navigation-entry-committed' as any, (event: any, url: string, inPage: boolean, replaceEntry: boolean) => {
|
||||
this.webContents.on('navigation-entry-committed', (event, url, inPage, replaceEntry) => {
|
||||
if (this.inPageIndex > -1 && !inPage) {
|
||||
// Navigated to a new page, clear in-page mark.
|
||||
this.inPageIndex = -1;
|
||||
@@ -64,16 +59,16 @@ export class NavigationController extends EventEmitter {
|
||||
});
|
||||
}
|
||||
|
||||
loadURL (url: string, options?: LoadURLOptions): Promise<void> {
|
||||
NavigationController.prototype.loadURL = function (url, options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
const p = new Promise<void>((resolve, reject) => {
|
||||
const p = new Promise((resolve, reject) => {
|
||||
const resolveAndCleanup = () => {
|
||||
removeListeners();
|
||||
resolve();
|
||||
};
|
||||
const rejectAndCleanup = (errorCode: number, errorDescription: string, url: string) => {
|
||||
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();
|
||||
@@ -82,14 +77,14 @@ export class NavigationController extends EventEmitter {
|
||||
const finishListener = () => {
|
||||
resolveAndCleanup();
|
||||
};
|
||||
const failListener = (event: any, errorCode: number, errorDescription: string, validatedURL: string, isMainFrame: boolean) => {
|
||||
const failListener = (event, errorCode, errorDescription, validatedURL, isMainFrame, frameProcessId, frameRoutingId) => {
|
||||
if (isMainFrame) {
|
||||
rejectAndCleanup(errorCode, errorDescription, validatedURL);
|
||||
}
|
||||
};
|
||||
|
||||
let navigationStarted = false;
|
||||
const navigationListener = (event: any, url: string, isSameDocument: boolean, isMainFrame: boolean) => {
|
||||
const navigationListener = (event, url, isSameDocument, isMainFrame, frameProcessId, frameRoutingId, navigationId) => {
|
||||
if (isMainFrame) {
|
||||
if (navigationStarted && !isSameDocument) {
|
||||
// the webcontents has started another unrelated navigation in the
|
||||
@@ -134,58 +129,58 @@ export class NavigationController extends EventEmitter {
|
||||
this.webContents._loadURL(url, options);
|
||||
this.webContents.emit('load-url', url, options);
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
getURL () {
|
||||
NavigationController.prototype.getURL = function () {
|
||||
if (this.currentIndex === -1) {
|
||||
return '';
|
||||
} else {
|
||||
return this.history[this.currentIndex];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
stop () {
|
||||
NavigationController.prototype.stop = function () {
|
||||
this.pendingIndex = -1;
|
||||
return this.webContents._stop();
|
||||
}
|
||||
};
|
||||
|
||||
reload () {
|
||||
NavigationController.prototype.reload = function () {
|
||||
this.pendingIndex = this.currentIndex;
|
||||
return this.webContents._loadURL(this.getURL(), {});
|
||||
}
|
||||
};
|
||||
|
||||
reloadIgnoringCache () {
|
||||
NavigationController.prototype.reloadIgnoringCache = function () {
|
||||
this.pendingIndex = this.currentIndex;
|
||||
return this.webContents._loadURL(this.getURL(), {
|
||||
extraHeaders: 'pragma: no-cache\n',
|
||||
reloadIgnoringCache: true
|
||||
} as any);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
canGoBack () {
|
||||
NavigationController.prototype.canGoBack = function () {
|
||||
return this.getActiveIndex() > 0;
|
||||
}
|
||||
};
|
||||
|
||||
canGoForward () {
|
||||
NavigationController.prototype.canGoForward = function () {
|
||||
return this.getActiveIndex() < this.history.length - 1;
|
||||
}
|
||||
};
|
||||
|
||||
canGoToIndex (index: number) {
|
||||
NavigationController.prototype.canGoToIndex = function (index) {
|
||||
return index >= 0 && index < this.history.length;
|
||||
}
|
||||
};
|
||||
|
||||
canGoToOffset (offset: number) {
|
||||
NavigationController.prototype.canGoToOffset = function (offset) {
|
||||
return this.canGoToIndex(this.currentIndex + offset);
|
||||
}
|
||||
};
|
||||
|
||||
clearHistory () {
|
||||
NavigationController.prototype.clearHistory = function () {
|
||||
this.history = [];
|
||||
this.currentIndex = -1;
|
||||
this.pendingIndex = -1;
|
||||
this.inPageIndex = -1;
|
||||
}
|
||||
};
|
||||
|
||||
goBack () {
|
||||
NavigationController.prototype.goBack = function () {
|
||||
if (!this.canGoBack()) {
|
||||
return;
|
||||
}
|
||||
@@ -195,9 +190,9 @@ export class NavigationController extends EventEmitter {
|
||||
} else {
|
||||
return this.webContents._loadURL(this.history[this.pendingIndex], {});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
goForward () {
|
||||
NavigationController.prototype.goForward = function () {
|
||||
if (!this.canGoForward()) {
|
||||
return;
|
||||
}
|
||||
@@ -207,17 +202,17 @@ export class NavigationController extends EventEmitter {
|
||||
} else {
|
||||
return this.webContents._loadURL(this.history[this.pendingIndex], {});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
goToIndex (index: number) {
|
||||
NavigationController.prototype.goToIndex = function (index) {
|
||||
if (!this.canGoToIndex(index)) {
|
||||
return;
|
||||
}
|
||||
this.pendingIndex = index;
|
||||
return this.webContents._loadURL(this.history[this.pendingIndex], {});
|
||||
}
|
||||
};
|
||||
|
||||
goToOffset (offset: number) {
|
||||
NavigationController.prototype.goToOffset = function (offset) {
|
||||
if (!this.canGoToOffset(offset)) {
|
||||
return;
|
||||
}
|
||||
@@ -228,17 +223,21 @@ export class NavigationController extends EventEmitter {
|
||||
} else {
|
||||
return this.goToIndex(pendingIndex);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getActiveIndex () {
|
||||
NavigationController.prototype.getActiveIndex = function () {
|
||||
if (this.pendingIndex === -1) {
|
||||
return this.currentIndex;
|
||||
} else {
|
||||
return this.pendingIndex;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
length () {
|
||||
NavigationController.prototype.length = function () {
|
||||
return this.history.length;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return NavigationController;
|
||||
})();
|
||||
|
||||
module.exports = NavigationController;
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { WebContents } from 'electron';
|
||||
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
|
||||
const getOwnerKey = (webContents: WebContents, contextId: string) => {
|
||||
return `${webContents.id}-${contextId}`;
|
||||
@@ -51,11 +51,19 @@ class ObjectsRegistry {
|
||||
// Dereference an object according to its ID.
|
||||
// Note that an object may be double-freed (cleared when page is reloaded, and
|
||||
// then garbage collected in old page).
|
||||
remove (webContents: WebContents, contextId: string, id: number) {
|
||||
// rendererSideRefCount is the ref count that the renderer process reported
|
||||
// at time of GC if this is different to the number of references we sent to
|
||||
// the given owner then a GC occurred between a ref being sent and the value
|
||||
// being pulled out of the weak map.
|
||||
// In this case we decrement out ref count and do not delete the stored
|
||||
// object
|
||||
// For more details on why we do renderer side ref counting see
|
||||
// https://github.com/electron/electron/pull/17464
|
||||
remove (webContents: WebContents, contextId: string, id: number, rendererSideRefCount: number) {
|
||||
const ownerKey = getOwnerKey(webContents, contextId);
|
||||
const owner = this.owners[ownerKey];
|
||||
if (owner && owner.has(id)) {
|
||||
const newRefCount = owner.get(id)! - 1;
|
||||
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
|
||||
|
||||
@@ -1,60 +1,38 @@
|
||||
'use strict';
|
||||
|
||||
import * as electron from 'electron';
|
||||
import { EventEmitter } from 'events';
|
||||
import objectsRegistry from './objects-registry';
|
||||
import { ipcMainInternal } from '../ipc-main-internal';
|
||||
import { isPromise, isSerializableObject, deserialize, serialize } from '../../common/type-utils';
|
||||
import type { MetaTypeFromRenderer, ObjectMember, MetaType, ObjProtoDescriptor } from '../../common/remote/types';
|
||||
import { isPromise, isSerializableObject, deserialize, serialize } from '@electron/internal/common/type-utils';
|
||||
import { Size } from 'electron/main';
|
||||
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
const eventBinding = process._linkedBinding('electron_browser_event');
|
||||
const features = process._linkedBinding('electron_common_features');
|
||||
const { NativeImage } = process._linkedBinding('electron_common_native_image');
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
const eventBinding = process.electronBinding('event');
|
||||
const features = process.electronBinding('features');
|
||||
const { NativeImage } = process.electronBinding('native_image');
|
||||
|
||||
if (!features.isRemoteModuleEnabled()) {
|
||||
throw new Error('remote module is disabled');
|
||||
}
|
||||
|
||||
const hasProp = {}.hasOwnProperty;
|
||||
|
||||
// The internal properties of Function.
|
||||
const FUNCTION_PROPERTIES = [
|
||||
'length', 'name', 'arguments', 'caller', 'prototype'
|
||||
];
|
||||
|
||||
type RendererFunctionId = [string, number] // [contextId, funcId]
|
||||
type FinalizerInfo = { id: RendererFunctionId, webContents: electron.WebContents, frameId: number };
|
||||
type WeakRef<T> = { deref(): T | undefined }
|
||||
type CallIntoRenderer = (...args: any[]) => void
|
||||
|
||||
// The remote functions in renderer processes.
|
||||
const rendererFunctionCache = new Map<string, WeakRef<CallIntoRenderer>>();
|
||||
// eslint-disable-next-line no-undef
|
||||
const finalizationRegistry = new (globalThis as any).FinalizationRegistry((fi: FinalizerInfo) => {
|
||||
const mapKey = fi.id[0] + '~' + fi.id[1];
|
||||
const ref = rendererFunctionCache.get(mapKey);
|
||||
if (ref !== undefined && ref.deref() === undefined) {
|
||||
rendererFunctionCache.delete(mapKey);
|
||||
if (!fi.webContents.isDestroyed()) { fi.webContents.sendToFrame(fi.frameId, 'ELECTRON_RENDERER_RELEASE_CALLBACK', fi.id[0], fi.id[1]); }
|
||||
}
|
||||
});
|
||||
// id => Function
|
||||
const rendererFunctions = v8Util.createDoubleIDWeakMap<(...args: any[]) => void>();
|
||||
|
||||
function getCachedRendererFunction (id: RendererFunctionId): CallIntoRenderer | undefined {
|
||||
const mapKey = id[0] + '~' + id[1];
|
||||
const ref = rendererFunctionCache.get(mapKey);
|
||||
if (ref !== undefined) {
|
||||
const deref = ref.deref();
|
||||
if (deref !== undefined) return deref;
|
||||
}
|
||||
}
|
||||
function setCachedRendererFunction (id: RendererFunctionId, wc: electron.WebContents, frameId: number, value: CallIntoRenderer) {
|
||||
// eslint-disable-next-line no-undef
|
||||
const wr = new (globalThis as any).WeakRef(value) as WeakRef<CallIntoRenderer>;
|
||||
const mapKey = id[0] + '~' + id[1];
|
||||
rendererFunctionCache.set(mapKey, wr);
|
||||
finalizationRegistry.register(value, {
|
||||
id,
|
||||
webContents: wc,
|
||||
frameId
|
||||
} as FinalizerInfo);
|
||||
return value;
|
||||
type ObjectMember = {
|
||||
name: string,
|
||||
value?: any,
|
||||
enumerable?: boolean,
|
||||
writable?: boolean,
|
||||
type?: 'method' | 'get'
|
||||
}
|
||||
|
||||
// Return the description of object's members:
|
||||
@@ -82,6 +60,11 @@ const getObjectMembers = function (object: any): ObjectMember[] {
|
||||
});
|
||||
};
|
||||
|
||||
type ObjProtoDescriptor = {
|
||||
members: ObjectMember[],
|
||||
proto: ObjProtoDescriptor
|
||||
} | null
|
||||
|
||||
// Return the description of object's prototype.
|
||||
const getObjectPrototype = function (object: any): ObjProtoDescriptor {
|
||||
const proto = Object.getPrototypeOf(object);
|
||||
@@ -92,42 +75,76 @@ const getObjectPrototype = function (object: any): ObjProtoDescriptor {
|
||||
};
|
||||
};
|
||||
|
||||
type MetaType = {
|
||||
type: 'number',
|
||||
value: number
|
||||
} | {
|
||||
type: 'boolean',
|
||||
value: boolean
|
||||
} | {
|
||||
type: 'string',
|
||||
value: string
|
||||
} | {
|
||||
type: 'bigint',
|
||||
value: bigint
|
||||
} | {
|
||||
type: 'symbol',
|
||||
value: symbol
|
||||
} | {
|
||||
type: 'undefined',
|
||||
value: undefined
|
||||
} | {
|
||||
type: 'object' | 'function',
|
||||
name: string,
|
||||
members: ObjectMember[],
|
||||
proto: ObjProtoDescriptor,
|
||||
id: number,
|
||||
} | {
|
||||
type: 'value',
|
||||
value: any,
|
||||
} | {
|
||||
type: 'buffer',
|
||||
value: Uint8Array,
|
||||
} | {
|
||||
type: 'array',
|
||||
members: MetaType[]
|
||||
} | {
|
||||
type: 'error',
|
||||
value: Error,
|
||||
members: ObjectMember[]
|
||||
} | {
|
||||
type: 'promise',
|
||||
then: MetaType
|
||||
} | {
|
||||
type: 'nativeimage'
|
||||
value: electron.NativeImage
|
||||
}
|
||||
|
||||
// Convert a real value into meta data.
|
||||
const valueToMeta = function (sender: electron.WebContents, contextId: string, value: any, optimizeSimpleObject = false): MetaType {
|
||||
// Determine the type of value.
|
||||
let type: MetaType['type'];
|
||||
|
||||
switch (typeof value) {
|
||||
case 'object':
|
||||
// Recognize certain types of objects.
|
||||
if (value instanceof Buffer) {
|
||||
type = 'buffer';
|
||||
} else if (value instanceof NativeImage) {
|
||||
type = 'nativeimage';
|
||||
} else if (Array.isArray(value)) {
|
||||
type = 'array';
|
||||
} else if (value instanceof Error) {
|
||||
type = 'error';
|
||||
} else if (isSerializableObject(value)) {
|
||||
type = 'value';
|
||||
} else if (isPromise(value)) {
|
||||
type = 'promise';
|
||||
} else if (Object.prototype.hasOwnProperty.call(value, 'callee') && value.length != null) {
|
||||
// Treat the arguments object as array.
|
||||
type = 'array';
|
||||
} else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) {
|
||||
// Treat simple objects as value.
|
||||
type = 'value';
|
||||
} else {
|
||||
type = 'object';
|
||||
}
|
||||
break;
|
||||
case 'function':
|
||||
type = 'function';
|
||||
break;
|
||||
default:
|
||||
let type: MetaType['type'] = typeof value;
|
||||
if (type === 'object') {
|
||||
// Recognize certain types of objects.
|
||||
if (value instanceof Buffer) {
|
||||
type = 'buffer';
|
||||
} else if (value instanceof NativeImage) {
|
||||
type = 'nativeimage';
|
||||
} else if (Array.isArray(value)) {
|
||||
type = 'array';
|
||||
} else if (value instanceof Error) {
|
||||
type = 'error';
|
||||
} else if (isSerializableObject(value)) {
|
||||
type = 'value';
|
||||
break;
|
||||
} else if (isPromise(value)) {
|
||||
type = 'promise';
|
||||
} else if (hasProp.call(value, 'callee') && value.length != null) {
|
||||
// Treat the arguments object as array.
|
||||
type = 'array';
|
||||
} else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) {
|
||||
// Treat simple objects as value.
|
||||
type = 'value';
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the meta object according to value's type.
|
||||
@@ -207,6 +224,46 @@ const removeRemoteListenersAndLogWarning = (sender: any, callIntoRenderer: (...a
|
||||
console.warn(message);
|
||||
};
|
||||
|
||||
type MetaTypeFromRenderer = {
|
||||
type: 'value',
|
||||
value: any
|
||||
} | {
|
||||
type: 'remote-object',
|
||||
id: number
|
||||
} | {
|
||||
type: 'array',
|
||||
value: MetaTypeFromRenderer[]
|
||||
} | {
|
||||
type: 'buffer',
|
||||
value: Uint8Array
|
||||
} | {
|
||||
type: 'promise',
|
||||
then: MetaTypeFromRenderer
|
||||
} | {
|
||||
type: 'object',
|
||||
name: string,
|
||||
members: {
|
||||
name: string,
|
||||
value: MetaTypeFromRenderer
|
||||
}[]
|
||||
} | {
|
||||
type: 'function-with-return-value',
|
||||
value: MetaTypeFromRenderer
|
||||
} | {
|
||||
type: 'function',
|
||||
id: number,
|
||||
location: string,
|
||||
length: number
|
||||
} | {
|
||||
type: 'nativeimage',
|
||||
value: {
|
||||
size: Size,
|
||||
buffer: Buffer,
|
||||
scaleFactor: number,
|
||||
dataURL: string
|
||||
}[]
|
||||
}
|
||||
|
||||
const fakeConstructor = (constructor: Function, name: string) =>
|
||||
new Proxy(Object, {
|
||||
get (target, prop, receiver) {
|
||||
@@ -258,8 +315,9 @@ const unwrapArgs = function (sender: electron.WebContents, frameId: number, cont
|
||||
const objectId: [string, number] = [contextId, meta.id];
|
||||
|
||||
// Cache the callbacks in renderer.
|
||||
const cachedFunction = getCachedRendererFunction(objectId);
|
||||
if (cachedFunction !== undefined) { return cachedFunction; }
|
||||
if (rendererFunctions.has(objectId)) {
|
||||
return rendererFunctions.get(objectId);
|
||||
}
|
||||
|
||||
const callIntoRenderer = function (this: any, ...args: any[]) {
|
||||
let succeed = false;
|
||||
@@ -273,7 +331,8 @@ const unwrapArgs = function (sender: electron.WebContents, frameId: number, cont
|
||||
v8Util.setHiddenValue(callIntoRenderer, 'location', meta.location);
|
||||
Object.defineProperty(callIntoRenderer, 'length', { value: meta.length });
|
||||
|
||||
setCachedRendererFunction(objectId, sender, frameId, callIntoRenderer);
|
||||
v8Util.setRemoteCallbackFreer(callIntoRenderer, frameId, contextId, meta.id, sender);
|
||||
rendererFunctions.set(objectId, callIntoRenderer);
|
||||
return callIntoRenderer;
|
||||
}
|
||||
default:
|
||||
@@ -284,13 +343,13 @@ const unwrapArgs = function (sender: electron.WebContents, frameId: number, cont
|
||||
};
|
||||
|
||||
const isRemoteModuleEnabledImpl = function (contents: electron.WebContents) {
|
||||
const webPreferences = contents.getLastWebPreferences() || {};
|
||||
const webPreferences = (contents as any).getLastWebPreferences() || {};
|
||||
return webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : false;
|
||||
};
|
||||
|
||||
const isRemoteModuleEnabledCache = new WeakMap();
|
||||
|
||||
export const isRemoteModuleEnabled = function (contents: electron.WebContents) {
|
||||
const isRemoteModuleEnabled = function (contents: electron.WebContents) {
|
||||
if (!isRemoteModuleEnabledCache.has(contents)) {
|
||||
isRemoteModuleEnabledCache.set(contents, isRemoteModuleEnabledImpl(contents));
|
||||
}
|
||||
@@ -338,12 +397,11 @@ const logStack = function (contents: electron.WebContents, code: string, stack:
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', function (event, contextId, passedContextId, id) {
|
||||
const objectId: [string, number] = [passedContextId, id];
|
||||
const cachedFunction = getCachedRendererFunction(objectId);
|
||||
if (cachedFunction === undefined) {
|
||||
if (!rendererFunctions.has(objectId)) {
|
||||
// Do nothing if the error has already been reported before.
|
||||
return;
|
||||
}
|
||||
removeRemoteListenersAndLogWarning(event.sender, cachedFunction);
|
||||
removeRemoteListenersAndLogWarning(event.sender, rendererFunctions.get(objectId)!);
|
||||
});
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_REQUIRE', function (event, contextId, moduleName, stack) {
|
||||
@@ -499,10 +557,14 @@ handleRemoteCommand('ELECTRON_BROWSER_MEMBER_GET', function (event, contextId, i
|
||||
return valueToMeta(event.sender, contextId, obj[name]);
|
||||
});
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_DEREFERENCE', function (event, contextId, id) {
|
||||
objectsRegistry.remove(event.sender, contextId, id);
|
||||
handleRemoteCommand('ELECTRON_BROWSER_DEREFERENCE', function (event, contextId, id, rendererSideRefCount) {
|
||||
objectsRegistry.remove(event.sender, contextId, id, rendererSideRefCount);
|
||||
});
|
||||
|
||||
handleRemoteCommand('ELECTRON_BROWSER_CONTEXT_RELEASE', (event, contextId) => {
|
||||
objectsRegistry.clear(event.sender, contextId);
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
isRemoteModuleEnabled
|
||||
};
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import * as electron from 'electron';
|
||||
import * as fs from 'fs';
|
||||
import { ipcMainInternal } from './ipc-main-internal';
|
||||
import * as ipcMainUtils from './ipc-main-internal-utils';
|
||||
import * as guestViewManager from './guest-view-manager';
|
||||
import * as typeUtils from '../common/type-utils';
|
||||
import { IpcMainInvokeEvent } from 'electron/main';
|
||||
'use strict';
|
||||
|
||||
const eventBinding = process._linkedBinding('electron_browser_event');
|
||||
const clipboard = process._linkedBinding('electron_common_clipboard');
|
||||
const electron = require('electron');
|
||||
const fs = require('fs');
|
||||
|
||||
const emitCustomEvent = function (contents: electron.WebContents, eventName: string, ...args: any[]) {
|
||||
const eventBinding = process.electronBinding('event');
|
||||
const clipboard = process.electronBinding('clipboard');
|
||||
|
||||
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
|
||||
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
|
||||
const guestViewManager = require('@electron/internal/browser/guest-view-manager');
|
||||
const typeUtils = require('@electron/internal/common/type-utils');
|
||||
|
||||
const emitCustomEvent = function (contents, eventName, ...args) {
|
||||
const event = eventBinding.createWithSender(contents);
|
||||
|
||||
electron.app.emit(eventName, event, contents, ...args);
|
||||
@@ -18,14 +20,14 @@ const emitCustomEvent = function (contents: electron.WebContents, eventName: str
|
||||
return event;
|
||||
};
|
||||
|
||||
const logStack = function (contents: electron.WebContents, code: string, stack: string) {
|
||||
const logStack = function (contents, code, stack) {
|
||||
if (stack) {
|
||||
console.warn(`WebContents (${contents.id}): ${code}`, stack);
|
||||
}
|
||||
};
|
||||
|
||||
// Implements window.close()
|
||||
ipcMainInternal.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event: ElectronInternal.IpcMainInternalEvent) {
|
||||
ipcMainInternal.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
|
||||
const window = event.sender.getOwnerBrowserWindow();
|
||||
if (window) {
|
||||
window.close();
|
||||
@@ -33,7 +35,7 @@ ipcMainInternal.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event: ElectronInt
|
||||
event.returnValue = null;
|
||||
});
|
||||
|
||||
ipcMainInternal.handle('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event: IpcMainInvokeEvent) {
|
||||
ipcMainInternal.handle('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event) {
|
||||
return event.sender.getLastWebPreferences();
|
||||
});
|
||||
|
||||
@@ -49,18 +51,18 @@ const allowedClipboardMethods = (() => {
|
||||
}
|
||||
})();
|
||||
|
||||
ipcMainUtils.handleSync('ELECTRON_BROWSER_CLIPBOARD_SYNC', function (event: IpcMainInvokeEvent, method: string, ...args: any[]) {
|
||||
ipcMainUtils.handleSync('ELECTRON_BROWSER_CLIPBOARD_SYNC', function (event, method, ...args) {
|
||||
if (!allowedClipboardMethods.has(method)) {
|
||||
throw new Error(`Invalid method: ${method}`);
|
||||
}
|
||||
|
||||
return typeUtils.serialize((electron.clipboard as any)[method](...typeUtils.deserialize(args)));
|
||||
return typeUtils.serialize(electron.clipboard[method](...typeUtils.deserialize(args)));
|
||||
});
|
||||
|
||||
if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
||||
const desktopCapturer = require('@electron/internal/browser/desktop-capturer');
|
||||
|
||||
ipcMainInternal.handle('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', async function (event: IpcMainInvokeEvent, options: Electron.SourcesOptions, stack: string) {
|
||||
ipcMainInternal.handle('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', async function (event, options, stack) {
|
||||
logStack(event.sender, 'desktopCapturer.getSources()', stack);
|
||||
const customEvent = emitCustomEvent(event.sender, 'desktop-capturer-get-sources');
|
||||
|
||||
@@ -71,17 +73,13 @@ if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
||||
|
||||
return typeUtils.serialize(await desktopCapturer.getSourcesImpl(event, options));
|
||||
});
|
||||
|
||||
ipcMainInternal.handle('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_MEDIA_SOURCE_ID_FOR_WEB_CONTENTS', function (event: IpcMainInvokeEvent, webContentsId: number) {
|
||||
return desktopCapturer.getMediaSourceIdForWebContents(event, webContentsId);
|
||||
});
|
||||
}
|
||||
|
||||
const isRemoteModuleEnabled = BUILDFLAG(ENABLE_REMOTE_MODULE)
|
||||
? require('@electron/internal/browser/remote/server').isRemoteModuleEnabled
|
||||
: () => false;
|
||||
|
||||
const getPreloadScript = async function (preloadPath: string) {
|
||||
const getPreloadScript = async function (preloadPath) {
|
||||
let preloadSrc = null;
|
||||
let preloadError = null;
|
||||
try {
|
||||
@@ -92,7 +90,7 @@ const getPreloadScript = async function (preloadPath: string) {
|
||||
return { preloadPath, preloadSrc, preloadError };
|
||||
};
|
||||
|
||||
ipcMainUtils.handleSync('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event: IpcMainInvokeEvent) {
|
||||
ipcMainUtils.handleSync('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event) {
|
||||
const preloadPaths = event.sender._getPreloadPaths();
|
||||
const webPreferences = event.sender.getLastWebPreferences() || {};
|
||||
|
||||
@@ -113,7 +111,7 @@ ipcMainUtils.handleSync('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event:
|
||||
};
|
||||
});
|
||||
|
||||
ipcMainInternal.on('ELECTRON_BROWSER_PRELOAD_ERROR', function (event: ElectronInternal.IpcMainInternalEvent, preloadPath: string, error: Error) {
|
||||
ipcMainInternal.on('ELECTRON_BROWSER_PRELOAD_ERROR', function (event, preloadPath, error) {
|
||||
event.sender.emit('preload-error', event, preloadPath, error);
|
||||
});
|
||||
|
||||
@@ -129,7 +127,7 @@ ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_GET_UPLOAD_TO_SERVER', () => {
|
||||
return electron.crashReporter.getUploadToServer();
|
||||
});
|
||||
|
||||
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_SET_UPLOAD_TO_SERVER', (event: IpcMainInvokeEvent, uploadToServer: boolean) => {
|
||||
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_SET_UPLOAD_TO_SERVER', (event, uploadToServer) => {
|
||||
return electron.crashReporter.setUploadToServer(uploadToServer);
|
||||
});
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
const clipboard = process._linkedBinding('electron_common_clipboard');
|
||||
'use strict';
|
||||
|
||||
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 makeRemoteMethod = function (method: keyof Electron.Clipboard) {
|
||||
return (...args: any[]) => {
|
||||
const makeRemoteMethod = function (method) {
|
||||
return (...args) => {
|
||||
args = typeUtils.serialize(args);
|
||||
const result = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_CLIPBOARD_SYNC', method, ...args);
|
||||
return typeUtils.deserialize(result);
|
||||
@@ -14,7 +16,7 @@ if (process.type === 'renderer') {
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
// On Linux we could not access clipboard in renderer process.
|
||||
for (const method of Object.keys(clipboard) as (keyof Electron.Clipboard)[]) {
|
||||
for (const method of Object.keys(clipboard)) {
|
||||
clipboard[method] = makeRemoteMethod(method);
|
||||
}
|
||||
} else if (process.platform === 'darwin') {
|
||||
@@ -24,4 +26,4 @@ if (process.type === 'renderer') {
|
||||
}
|
||||
}
|
||||
|
||||
export default clipboard;
|
||||
module.exports = clipboard;
|
||||
@@ -55,12 +55,12 @@ const deprecate: ElectronInternal.DeprecationUtil = {
|
||||
};
|
||||
},
|
||||
|
||||
moveAPI<T extends Function> (fn: T, oldUsage: string, newUsage: string): T {
|
||||
moveAPI: (fn: Function, oldUsage: string, newUsage: string) => {
|
||||
const warn = warnOnce(oldUsage, newUsage);
|
||||
return function (this: any) {
|
||||
warn();
|
||||
return fn.apply(this, arguments);
|
||||
} as any;
|
||||
};
|
||||
},
|
||||
|
||||
// change the name of an event
|
||||
@@ -76,6 +76,23 @@ const deprecate: ElectronInternal.DeprecationUtil = {
|
||||
});
|
||||
},
|
||||
|
||||
// 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];
|
||||
return function (this: any, ...args: any) {
|
||||
warn();
|
||||
return method.apply(this, args);
|
||||
};
|
||||
};
|
||||
|
||||
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`);
|
||||
}
|
||||
},
|
||||
|
||||
// remove a property with no replacement
|
||||
removeProperty: (o, removedName, onlyForValues) => {
|
||||
// if the property's already been removed, warn about it
|
||||
|
||||
5
lib/common/api/native-image.js
Normal file
5
lib/common/api/native-image.js
Normal file
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
const { nativeImage } = process.electronBinding('native_image');
|
||||
|
||||
module.exports = nativeImage;
|
||||
@@ -1,3 +0,0 @@
|
||||
const { nativeImage } = process._linkedBinding('electron_common_native_image');
|
||||
|
||||
export default nativeImage;
|
||||
3
lib/common/api/shell.js
Normal file
3
lib/common/api/shell.js
Normal file
@@ -0,0 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = process.electronBinding('shell');
|
||||
@@ -1 +0,0 @@
|
||||
export default process._linkedBinding('electron_common_shell');
|
||||
@@ -1,4 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
/* global require */
|
||||
|
||||
// Monkey-patch the fs module.
|
||||
require('electron/js2c/asar').wrapFsWithAsar(require('fs'));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user