mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
6 Commits
replace-we
...
sam/quiete
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2da0fe3fa1 | ||
|
|
4d8fd31e5f | ||
|
|
96486a4102 | ||
|
|
3f8238b92c | ||
|
|
64c5440eec | ||
|
|
30cf60a935 |
14
.github/actions/build-electron/action.yml
vendored
14
.github/actions/build-electron/action.yml
vendored
@@ -75,9 +75,9 @@ runs:
|
||||
fi
|
||||
|
||||
if [ "${{ inputs.is-release }}" = "true" ]; then
|
||||
NINJA_SUMMARIZE_BUILD=1 e build --target electron:release_build
|
||||
NINJA_SUMMARIZE_BUILD=1 e build --target electron:release_build -- --quiet --batch=false --heartbeat_period=30s
|
||||
else
|
||||
NINJA_SUMMARIZE_BUILD=1 e build --target electron:testing_build
|
||||
NINJA_SUMMARIZE_BUILD=1 e build --target electron:testing_build -- --quiet --batch=false --heartbeat_period=30s
|
||||
fi
|
||||
cp out/Default/.ninja_log out/electron_ninja_log
|
||||
node electron/script/check-symlinks.js
|
||||
@@ -102,10 +102,10 @@ runs:
|
||||
cd ..
|
||||
|
||||
$env:NINJA_SUMMARIZE_BUILD = 1
|
||||
if ("${{ inputs.is-release }}" -eq "true") {
|
||||
e build --target electron:release_build
|
||||
if ("${{ inputs.is-release }}" -eq "true") {
|
||||
e build --target electron:release_build -- --quiet --batch=false --heartbeat_period=30s
|
||||
} else {
|
||||
e build --target electron:testing_build
|
||||
e build --target electron:testing_build -- --quiet --batch=false --heartbeat_period=30s
|
||||
}
|
||||
Copy-Item out\Default\.ninja_log out\electron_ninja_log
|
||||
node electron\script\check-symlinks.js
|
||||
@@ -185,7 +185,7 @@ runs:
|
||||
shell: bash
|
||||
run: |
|
||||
cd src
|
||||
e build --target electron:electron_chromedriver_zip
|
||||
e build --target electron:electron_chromedriver_zip -- --quiet --batch=false --heartbeat_period=30s
|
||||
|
||||
if [ "${{ inputs.is-asan }}" != "true" ]; then
|
||||
target_os=${{ inputs.target-platform == 'macos' && 'mac' || inputs.target-platform }}
|
||||
@@ -229,7 +229,7 @@ runs:
|
||||
run: |
|
||||
cd src
|
||||
gn gen out/ffmpeg --args="import(\"//electron/build/args/ffmpeg.gn\") use_remoteexec=true use_siso=true $GN_EXTRA_ARGS"
|
||||
e build --target electron:electron_ffmpeg_zip -C ../../out/ffmpeg
|
||||
e build --target electron:electron_ffmpeg_zip -C ../../out/ffmpeg --quiet --batch=false --heartbeat_period=30s
|
||||
- name: Remove Clang problem matcher
|
||||
shell: bash
|
||||
run: echo "::remove-matcher owner=clang::"
|
||||
|
||||
2
.github/actions/build-git-cache/action.yml
vendored
2
.github/actions/build-git-cache/action.yml
vendored
@@ -58,7 +58,7 @@ runs:
|
||||
echo "target_os=['$TARGET_OS']" >> ./.gclient
|
||||
fi
|
||||
|
||||
ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 e d gclient sync --with_branch_heads --with_tags --nohooks -vv
|
||||
ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 e d gclient sync --with_branch_heads --with_tags --nohooks
|
||||
- name: Compress Git Cache Directory
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
2
.github/actions/checkout/action.yml
vendored
2
.github/actions/checkout/action.yml
vendored
@@ -109,7 +109,7 @@ runs:
|
||||
echo "target_os=['$TARGET_OS']" >> ./.gclient
|
||||
fi
|
||||
|
||||
ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 e d gclient sync --with_branch_heads --with_tags -vv
|
||||
ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 e d gclient sync --with_branch_heads --with_tags
|
||||
if [[ "${{ inputs.is-release }}" != "true" ]]; then
|
||||
# Re-export all the patches to check if there were changes.
|
||||
python3 src/electron/script/export_all_patches.py src/electron/patches/config.json
|
||||
|
||||
41
BUILD.gn
41
BUILD.gn
@@ -18,12 +18,12 @@ import("//tools/v8_context_snapshot/v8_context_snapshot.gni")
|
||||
import("//v8/gni/snapshot_toolchain.gni")
|
||||
import("build/asar.gni")
|
||||
import("build/electron_paks.gni")
|
||||
import("build/esbuild/esbuild.gni")
|
||||
import("build/extract_symbols.gni")
|
||||
import("build/js2c_toolchain.gni")
|
||||
import("build/npm.gni")
|
||||
import("build/templated_file.gni")
|
||||
import("build/tsc.gni")
|
||||
import("build/webpack/webpack.gni")
|
||||
import("buildflags/buildflags.gni")
|
||||
import("filenames.auto.gni")
|
||||
import("filenames.gni")
|
||||
@@ -162,81 +162,75 @@ npm_action("build_electron_definitions") {
|
||||
outputs = [ "$target_gen_dir/tsc/typings/electron.d.ts" ]
|
||||
}
|
||||
|
||||
typescript_check("electron_lib_typecheck") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
tsconfig = "//electron/tsconfig.electron.json"
|
||||
sources = auto_filenames.typecheck_sources
|
||||
}
|
||||
|
||||
esbuild_build("electron_browser_bundle") {
|
||||
webpack_build("electron_browser_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.browser_bundle_deps
|
||||
|
||||
config_file = "//electron/build/esbuild/configs/browser.js"
|
||||
config_file = "//electron/build/webpack/webpack.config.browser.js"
|
||||
out_file = "$target_gen_dir/js2c/browser_init.js"
|
||||
}
|
||||
|
||||
esbuild_build("electron_renderer_bundle") {
|
||||
webpack_build("electron_renderer_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.renderer_bundle_deps
|
||||
|
||||
config_file = "//electron/build/esbuild/configs/renderer.js"
|
||||
config_file = "//electron/build/webpack/webpack.config.renderer.js"
|
||||
out_file = "$target_gen_dir/js2c/renderer_init.js"
|
||||
}
|
||||
|
||||
esbuild_build("electron_worker_bundle") {
|
||||
webpack_build("electron_worker_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.worker_bundle_deps
|
||||
|
||||
config_file = "//electron/build/esbuild/configs/worker.js"
|
||||
config_file = "//electron/build/webpack/webpack.config.worker.js"
|
||||
out_file = "$target_gen_dir/js2c/worker_init.js"
|
||||
}
|
||||
|
||||
esbuild_build("electron_sandboxed_renderer_bundle") {
|
||||
webpack_build("electron_sandboxed_renderer_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.sandbox_bundle_deps
|
||||
|
||||
config_file = "//electron/build/esbuild/configs/sandboxed_renderer.js"
|
||||
config_file = "//electron/build/webpack/webpack.config.sandboxed_renderer.js"
|
||||
out_file = "$target_gen_dir/js2c/sandbox_bundle.js"
|
||||
}
|
||||
|
||||
esbuild_build("electron_isolated_renderer_bundle") {
|
||||
webpack_build("electron_isolated_renderer_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.isolated_bundle_deps
|
||||
|
||||
config_file = "//electron/build/esbuild/configs/isolated_renderer.js"
|
||||
config_file = "//electron/build/webpack/webpack.config.isolated_renderer.js"
|
||||
out_file = "$target_gen_dir/js2c/isolated_bundle.js"
|
||||
}
|
||||
|
||||
esbuild_build("electron_node_bundle") {
|
||||
webpack_build("electron_node_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.node_bundle_deps
|
||||
|
||||
config_file = "//electron/build/esbuild/configs/node.js"
|
||||
config_file = "//electron/build/webpack/webpack.config.node.js"
|
||||
out_file = "$target_gen_dir/js2c/node_init.js"
|
||||
}
|
||||
|
||||
esbuild_build("electron_utility_bundle") {
|
||||
webpack_build("electron_utility_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.utility_bundle_deps
|
||||
|
||||
config_file = "//electron/build/esbuild/configs/utility.js"
|
||||
config_file = "//electron/build/webpack/webpack.config.utility.js"
|
||||
out_file = "$target_gen_dir/js2c/utility_init.js"
|
||||
}
|
||||
|
||||
esbuild_build("electron_preload_realm_bundle") {
|
||||
webpack_build("electron_preload_realm_bundle") {
|
||||
deps = [ ":build_electron_definitions" ]
|
||||
|
||||
inputs = auto_filenames.preload_realm_bundle_deps
|
||||
|
||||
config_file = "//electron/build/esbuild/configs/preload_realm.js"
|
||||
config_file = "//electron/build/webpack/webpack.config.preload_realm.js"
|
||||
out_file = "$target_gen_dir/js2c/preload_realm_bundle.js"
|
||||
}
|
||||
|
||||
@@ -244,7 +238,6 @@ action("electron_js2c") {
|
||||
deps = [
|
||||
":electron_browser_bundle",
|
||||
":electron_isolated_renderer_bundle",
|
||||
":electron_lib_typecheck",
|
||||
":electron_node_bundle",
|
||||
":electron_preload_realm_bundle",
|
||||
":electron_renderer_bundle",
|
||||
|
||||
@@ -1,326 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
// Driver script that replaces webpack for building Electron's internal
|
||||
// JS bundles. Each bundle is a single esbuild invocation parameterized by
|
||||
// the per-target configuration files under build/esbuild/configs.
|
||||
//
|
||||
// Invoked by the GN `esbuild_build` template via `npm run bundle -- …`.
|
||||
|
||||
'use strict';
|
||||
|
||||
const esbuild = require('esbuild');
|
||||
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const electronRoot = path.resolve(__dirname, '../..');
|
||||
|
||||
function parseArgs (argv) {
|
||||
const args = {};
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
const a = argv[i];
|
||||
if (a.startsWith('--')) {
|
||||
const key = a.slice(2);
|
||||
const next = argv[i + 1];
|
||||
if (next === undefined || next.startsWith('--')) {
|
||||
args[key] = true;
|
||||
} else {
|
||||
args[key] = next;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
// Parse $target_gen_dir/buildflags/buildflags.h (a C++ header containing
|
||||
// `#define BUILDFLAG_INTERNAL_NAME() (0|1)` lines) into a map of flag name
|
||||
// to boolean. Used to seed the `define` table so that `BUILDFLAG(NAME)` call
|
||||
// sites can be statically folded to `true`/`false` at build time.
|
||||
function parseBuildflags (buildflagsPath) {
|
||||
const flags = {};
|
||||
if (!buildflagsPath) return flags;
|
||||
const source = fs.readFileSync(buildflagsPath, 'utf8');
|
||||
const re = /#define BUILDFLAG_INTERNAL_(.+?)\(\) \(([01])\)/g;
|
||||
let match;
|
||||
while ((match = re.exec(source)) !== null) {
|
||||
const [, name, value] = match;
|
||||
flags[name] = value === '1';
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
// Return the list of esbuild `alias` entries used by every bundle. esbuild's
|
||||
// alias matches the full module specifier (no `$` suffix trickery like
|
||||
// webpack), so the bare `electron` alias also matches `electron/main`, etc.,
|
||||
// because esbuild matches the leftmost segment first.
|
||||
function buildAliases (electronAPIFile, { aliasTimers }) {
|
||||
const aliases = {
|
||||
electron: electronAPIFile,
|
||||
'electron/main': electronAPIFile,
|
||||
'electron/renderer': electronAPIFile,
|
||||
'electron/common': electronAPIFile,
|
||||
'electron/utility': electronAPIFile
|
||||
};
|
||||
// Only browser-platform bundles (sandboxed_renderer, isolated_renderer,
|
||||
// preload_realm) need the timers shim — Node's `timers` builtin is not
|
||||
// available there. For node-platform bundles (browser, renderer, worker,
|
||||
// utility, node) the alias MUST NOT apply: lib/common/init.ts wraps the
|
||||
// real Node timers and then assigns the wrappers onto globalThis. If
|
||||
// those bundles saw the shim, the wrappers would recursively call back
|
||||
// into globalThis.setTimeout and blow the stack.
|
||||
if (aliasTimers) {
|
||||
aliases.timers = path.resolve(electronRoot, 'lib', 'common', 'timers-shim.ts');
|
||||
}
|
||||
return aliases;
|
||||
}
|
||||
|
||||
// esbuild's `alias` does not support wildcard prefixes like `@electron/internal/*`.
|
||||
// We instead install a tiny resolve plugin that rewrites any import starting
|
||||
// with that prefix to an absolute path under `lib/`. The plugin must also
|
||||
// replicate esbuild's extension/index resolution because returning a path
|
||||
// from onResolve bypasses the default resolver.
|
||||
function internalAliasPlugin () {
|
||||
const candidates = (base) => [
|
||||
base,
|
||||
`${base}.ts`,
|
||||
`${base}.js`,
|
||||
path.join(base, 'index.ts'),
|
||||
path.join(base, 'index.js')
|
||||
];
|
||||
return {
|
||||
name: 'electron-internal-alias',
|
||||
setup (build) {
|
||||
build.onResolve({ filter: /^@electron\/internal(\/|$)/ }, (args) => {
|
||||
// Tolerate stray double slashes in import paths (webpack was lenient).
|
||||
const rel = args.path.replace(/^@electron\/internal\/?/, '').replace(/^\/+/, '');
|
||||
const base = path.resolve(electronRoot, 'lib', rel);
|
||||
for (const c of candidates(base)) {
|
||||
try {
|
||||
if (fs.statSync(c).isFile()) return { path: c };
|
||||
} catch { /* keep looking */ }
|
||||
}
|
||||
return { errors: [{ text: `Cannot resolve @electron/internal path: ${args.path}` }] };
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Rewrites `BUILDFLAG(NAME)` call-sites to `(true)` or `(false)` at load
|
||||
// time, equivalent to the combination of webpack's DefinePlugin substitution
|
||||
// (BUILDFLAG -> "" and NAME -> "true"/"false") that the old config used.
|
||||
// Doing it in a single regex pass keeps the semantics identical and avoids
|
||||
// fighting with esbuild's AST-level `define` quoting rules.
|
||||
function buildflagPlugin (buildflags, { allowUnknown = false } = {}) {
|
||||
return {
|
||||
name: 'electron-buildflag',
|
||||
setup (build) {
|
||||
build.onLoad({ filter: /\.(ts|js)$/ }, async (args) => {
|
||||
const source = await fs.promises.readFile(args.path, 'utf8');
|
||||
if (!source.includes('BUILDFLAG(')) {
|
||||
return { contents: source, loader: args.path.endsWith('.ts') ? 'ts' : 'js' };
|
||||
}
|
||||
const rewritten = source.replace(/BUILDFLAG\(([A-Z0-9_]+)\)/g, (_, name) => {
|
||||
if (!Object.prototype.hasOwnProperty.call(buildflags, name)) {
|
||||
if (allowUnknown) return '(false)';
|
||||
throw new Error(`Unknown BUILDFLAG: ${name} (in ${args.path})`);
|
||||
}
|
||||
return `(${buildflags[name]})`;
|
||||
});
|
||||
return { contents: rewritten, loader: args.path.endsWith('.ts') ? 'ts' : 'js' };
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO(MarshallOfSound): drop this patch once evanw/esbuild#4441 lands and
|
||||
// we bump esbuild — that PR adds a `__toCommonJSCached` helper for the
|
||||
// inline-require path so identity is preserved upstream. Tracked at
|
||||
// https://github.com/evanw/esbuild/issues/4440.
|
||||
//
|
||||
// esbuild's runtime emits `__toCommonJS = (mod) => __copyProps(__defProp({},
|
||||
// "__esModule", { value: true }), mod)`, which allocates a fresh wrapper
|
||||
// object every time `require()` resolves to a bundled ESM module. That
|
||||
// breaks identity expectations our code relies on (e.g. sandboxed preloads
|
||||
// expecting `require('timers') === require('node:timers')`, and the
|
||||
// defineProperties getters in lib/common/define-properties.ts expecting
|
||||
// stable namespaces). A cached WeakMap-backed version of __toCommonJS
|
||||
// existed in older esbuild releases (see evanw/esbuild#2126) but was
|
||||
// removed in evanw/esbuild@f4ff26d3 (0.14.27). Substitute a memoized
|
||||
// variant in post-processing so every call site returns the same wrapper
|
||||
// for the same underlying namespace, matching webpack's
|
||||
// `__webpack_require__` cache semantics.
|
||||
const ESBUILD_TO_COMMONJS_PATTERN =
|
||||
/var __toCommonJS = \(mod\) => __copyProps\(__defProp\(\{\}, "__esModule", \{ value: true \}\), mod\);/;
|
||||
const ESBUILD_TO_COMMONJS_REPLACEMENT =
|
||||
'var __toCommonJS = /* @__PURE__ */ ((cache) => (mod) => {\n' +
|
||||
' var cached = cache.get(mod);\n' +
|
||||
' if (cached) return cached;\n' +
|
||||
' var result = __copyProps(__defProp({}, "__esModule", { value: true }), mod);\n' +
|
||||
' cache.set(mod, result);\n' +
|
||||
' return result;\n' +
|
||||
' })(new WeakMap());';
|
||||
|
||||
function patchToCommonJS (source) {
|
||||
// Once evanw/esbuild#4441 lands, esbuild will emit `__toCommonJSCached`
|
||||
// for inline require() — when we see that helper in the output, the
|
||||
// upstream fix is active and this whole patch is a no-op (and should be
|
||||
// deleted on the next esbuild bump).
|
||||
if (source.includes('__toCommonJSCached')) {
|
||||
return source;
|
||||
}
|
||||
if (!ESBUILD_TO_COMMONJS_PATTERN.test(source)) {
|
||||
// Some bundles may not contain any ESM-shaped modules, in which case
|
||||
// esbuild omits the helper entirely and there is nothing to patch.
|
||||
if (source.includes('__toCommonJS')) {
|
||||
throw new Error(
|
||||
'esbuild bundle contains __toCommonJS but did not match the ' +
|
||||
'expected pattern; the runtime helper has likely changed upstream. ' +
|
||||
'Update ESBUILD_TO_COMMONJS_PATTERN in build/esbuild/bundle.js, or ' +
|
||||
'delete patchToCommonJS entirely if evanw/esbuild#4441 has landed.'
|
||||
);
|
||||
}
|
||||
return source;
|
||||
}
|
||||
return source.replace(ESBUILD_TO_COMMONJS_PATTERN, ESBUILD_TO_COMMONJS_REPLACEMENT);
|
||||
}
|
||||
|
||||
// Wrap bundle source text in the same header/footer pairs webpack's
|
||||
// wrapper-webpack-plugin used. The try/catch wrapper is load-bearing:
|
||||
// shell/common/node_util.cc's CompileAndCall relies on it to prevent
|
||||
// exceptions from tearing down bootstrap.
|
||||
function applyWrappers (source, opts, outputFilename) {
|
||||
let wrapped = patchToCommonJS(source);
|
||||
if (opts.wrapInitWithProfilingTimeout) {
|
||||
const header = 'function ___electron_webpack_init__() {';
|
||||
const footer = '\n};\nif ((globalThis.process || binding.process).argv.includes("--profile-electron-init")) {\n setTimeout(___electron_webpack_init__, 0);\n} else {\n ___electron_webpack_init__();\n}';
|
||||
wrapped = header + wrapped + footer;
|
||||
}
|
||||
if (opts.wrapInitWithTryCatch) {
|
||||
const header = 'try {';
|
||||
const footer = `\n} catch (err) {\n console.error('Electron ${outputFilename} script failed to run');\n console.error(err);\n}`;
|
||||
wrapped = header + wrapped + footer;
|
||||
}
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
async function buildBundle (opts, cliArgs) {
|
||||
const {
|
||||
target,
|
||||
alwaysHasNode,
|
||||
loadElectronFromAlternateTarget,
|
||||
wrapInitWithProfilingTimeout,
|
||||
wrapInitWithTryCatch
|
||||
} = opts;
|
||||
|
||||
const outputFilename = cliArgs['output-filename'] || `${target}.bundle.js`;
|
||||
const outputPath = cliArgs['output-path'] || path.resolve(electronRoot, 'out');
|
||||
const mode = cliArgs.mode || 'development';
|
||||
const minify = mode === 'production';
|
||||
const printGraph = !!cliArgs['print-graph'];
|
||||
|
||||
let entry = path.resolve(electronRoot, 'lib', target, 'init.ts');
|
||||
if (!fs.existsSync(entry)) {
|
||||
entry = path.resolve(electronRoot, 'lib', target, 'init.js');
|
||||
}
|
||||
|
||||
const electronAPIFile = path.resolve(
|
||||
electronRoot,
|
||||
'lib',
|
||||
loadElectronFromAlternateTarget || target,
|
||||
'api',
|
||||
'exports',
|
||||
'electron.ts'
|
||||
);
|
||||
|
||||
const buildflags = parseBuildflags(cliArgs.buildflags);
|
||||
|
||||
// Shims that stand in for webpack ProvidePlugin. Each target gets the
|
||||
// minimum set of globals it needs; the capture files mirror the originals
|
||||
// under lib/common so the behavior (grab globals before user code can
|
||||
// delete them) is preserved exactly.
|
||||
const inject = [];
|
||||
if (opts.targetDeletesNodeGlobals) {
|
||||
inject.push(path.resolve(__dirname, 'shims', 'node-globals-shim.js'));
|
||||
}
|
||||
if (!alwaysHasNode) {
|
||||
inject.push(path.resolve(__dirname, 'shims', 'browser-globals-shim.js'));
|
||||
}
|
||||
inject.push(path.resolve(__dirname, 'shims', 'promise-shim.js'));
|
||||
|
||||
const result = await esbuild.build({
|
||||
entryPoints: [entry],
|
||||
bundle: true,
|
||||
format: 'iife',
|
||||
platform: alwaysHasNode ? 'node' : 'browser',
|
||||
target: 'es2022',
|
||||
minify,
|
||||
// Preserve class/function names in both development and production so
|
||||
// gin_helper-surfaced constructor names and stack traces stay readable.
|
||||
// (Under webpack this only mattered when terser ran in is_official_build;
|
||||
// esbuild applies the same rename pressure in dev too, so keep it on
|
||||
// unconditionally for consistency.)
|
||||
keepNames: true,
|
||||
sourcemap: false,
|
||||
logLevel: 'warning',
|
||||
metafile: true,
|
||||
write: false,
|
||||
resolveExtensions: ['.ts', '.js'],
|
||||
alias: buildAliases(electronAPIFile, { aliasTimers: !alwaysHasNode }),
|
||||
inject,
|
||||
define: {
|
||||
__non_webpack_require__: 'require'
|
||||
},
|
||||
// Node internal modules we pull through __non_webpack_require__ at runtime.
|
||||
// These must not be bundled — esbuild should leave the literal require()
|
||||
// call alone so the outer Node scope resolves them.
|
||||
external: [
|
||||
'internal/modules/helpers',
|
||||
'internal/modules/run_main',
|
||||
'internal/fs/utils',
|
||||
'internal/util',
|
||||
'internal/validators',
|
||||
'internal/url'
|
||||
],
|
||||
plugins: [
|
||||
internalAliasPlugin(),
|
||||
buildflagPlugin(buildflags, { allowUnknown: printGraph })
|
||||
]
|
||||
});
|
||||
|
||||
if (printGraph) {
|
||||
const inputs = Object.keys(result.metafile.inputs)
|
||||
.filter((p) => !p.includes('node_modules') && !p.startsWith('..'))
|
||||
.map((p) => path.relative(electronRoot, path.resolve(electronRoot, p)));
|
||||
process.stdout.write(JSON.stringify(inputs) + '\n');
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.outputFiles.length !== 1) {
|
||||
throw new Error(`Expected exactly one output file, got ${result.outputFiles.length}`);
|
||||
}
|
||||
|
||||
const wrapped = applyWrappers(
|
||||
result.outputFiles[0].text,
|
||||
{ wrapInitWithProfilingTimeout, wrapInitWithTryCatch },
|
||||
outputFilename
|
||||
);
|
||||
|
||||
await fs.promises.mkdir(outputPath, { recursive: true });
|
||||
await fs.promises.writeFile(path.join(outputPath, outputFilename), wrapped);
|
||||
}
|
||||
|
||||
async function main () {
|
||||
const cliArgs = parseArgs(process.argv.slice(2));
|
||||
if (!cliArgs.config) {
|
||||
console.error('Usage: bundle.js --config <path> [--output-filename X] [--output-path Y] [--mode development|production] [--buildflags path/to/buildflags.h] [--print-graph]');
|
||||
process.exit(1);
|
||||
}
|
||||
const configPath = path.resolve(cliArgs.config);
|
||||
const opts = require(configPath);
|
||||
await buildBundle(opts, cliArgs);
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,4 +0,0 @@
|
||||
module.exports = {
|
||||
target: 'browser',
|
||||
alwaysHasNode: true
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
module.exports = {
|
||||
target: 'node',
|
||||
alwaysHasNode: true
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
module.exports = {
|
||||
target: 'utility',
|
||||
alwaysHasNode: true
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
// Injected into browser-platform bundles (sandboxed_renderer, isolated_renderer,
|
||||
// preload_realm) where Node globals are not implicitly available. Supplies
|
||||
// `Buffer`, `process`, and `global` — replacing webpack's ProvidePlugin
|
||||
// polyfill injection plus webpack 5's built-in `global -> globalThis` rewrite
|
||||
// that `target: 'web'` performed automatically.
|
||||
//
|
||||
// The `buffer` and `process/browser` imports below intentionally use the
|
||||
// npm polyfill packages, not Node's built-in `node:buffer` / `node:process`
|
||||
// modules, because these shims ship into browser-platform bundles that do
|
||||
// not have Node globals available at runtime.
|
||||
|
||||
/* eslint-disable import/order, import/enforce-node-protocol-usage */
|
||||
import { Buffer as _Buffer } from 'buffer';
|
||||
import _process from 'process/browser';
|
||||
|
||||
const _global = globalThis;
|
||||
|
||||
export { _Buffer as Buffer, _process as process, _global as global };
|
||||
@@ -1,14 +0,0 @@
|
||||
// Injected into renderer/worker bundles to replace webpack's ProvidePlugin
|
||||
// that captured `Buffer`, `global`, and `process` before user code could
|
||||
// delete them from the global scope. The Module.wrapper override in
|
||||
// lib/renderer/init.ts re-injects these into user preload scripts later.
|
||||
|
||||
// Rip globals off of globalThis/self/window so they are captured in this
|
||||
// module's closure and retained even if the caller later deletes them.
|
||||
const _global = typeof globalThis !== 'undefined'
|
||||
? globalThis.global
|
||||
: (self || window).global;
|
||||
const _process = _global.process;
|
||||
const _Buffer = _global.Buffer;
|
||||
|
||||
export { _global as global, _process as process, _Buffer as Buffer };
|
||||
@@ -1,7 +0,0 @@
|
||||
// Captures the original `Promise` constructor so that userland mutations of
|
||||
// `global.Promise.resolve` do not affect Electron's internal code. Mirrors
|
||||
// webpack's ProvidePlugin reference to lib/common/webpack-globals-provider.
|
||||
|
||||
const _Promise = globalThis.Promise;
|
||||
|
||||
export { _Promise as Promise };
|
||||
@@ -1,42 +1,5 @@
|
||||
import("npm.gni")
|
||||
|
||||
# Runs `tsgo --noEmit` over a tsconfig via the `tsc-check` npm script (which
|
||||
# wraps script/typecheck.js) and writes a stamp on success. Use this to gate
|
||||
# downstream targets on a successful typecheck without emitting JS.
|
||||
template("typescript_check") {
|
||||
assert(defined(invoker.tsconfig), "Need tsconfig name to run")
|
||||
assert(defined(invoker.sources), "Need tsc sources to run")
|
||||
|
||||
npm_action(target_name) {
|
||||
forward_variables_from(invoker,
|
||||
[
|
||||
"deps",
|
||||
"public_deps",
|
||||
])
|
||||
script = "tsc-check"
|
||||
|
||||
sources = invoker.sources
|
||||
inputs = [
|
||||
invoker.tsconfig,
|
||||
"//electron/tsconfig.json",
|
||||
"//electron/yarn.lock",
|
||||
"//electron/script/typecheck.js",
|
||||
"//electron/typings/internal-ambient.d.ts",
|
||||
"//electron/typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
stamp_file = "$target_gen_dir/$target_name.stamp"
|
||||
outputs = [ stamp_file ]
|
||||
|
||||
args = [
|
||||
"--tsconfig",
|
||||
rebase_path(invoker.tsconfig),
|
||||
"--stamp",
|
||||
rebase_path(stamp_file),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
template("typescript_build") {
|
||||
assert(defined(invoker.tsconfig), "Need tsconfig name to run")
|
||||
assert(defined(invoker.sources), "Need tsc sources to run")
|
||||
|
||||
172
build/webpack/webpack.config.base.js
Normal file
172
build/webpack/webpack.config.base.js
Normal file
@@ -0,0 +1,172 @@
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const webpack = require('webpack');
|
||||
const WrapperPlugin = require('wrapper-webpack-plugin');
|
||||
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const electronRoot = path.resolve(__dirname, '../..');
|
||||
|
||||
class AccessDependenciesPlugin {
|
||||
apply (compiler) {
|
||||
compiler.hooks.compilation.tap('AccessDependenciesPlugin', compilation => {
|
||||
compilation.hooks.finishModules.tap('AccessDependenciesPlugin', modules => {
|
||||
const filePaths = modules.map(m => m.resource).filter(p => p).map(p => path.relative(electronRoot, p));
|
||||
console.info(JSON.stringify(filePaths));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ({
|
||||
alwaysHasNode,
|
||||
loadElectronFromAlternateTarget,
|
||||
targetDeletesNodeGlobals,
|
||||
target,
|
||||
wrapInitWithProfilingTimeout,
|
||||
wrapInitWithTryCatch
|
||||
}) => {
|
||||
let entry = path.resolve(electronRoot, 'lib', target, 'init.ts');
|
||||
if (!fs.existsSync(entry)) {
|
||||
entry = path.resolve(electronRoot, 'lib', target, 'init.js');
|
||||
}
|
||||
|
||||
const electronAPIFile = path.resolve(electronRoot, 'lib', loadElectronFromAlternateTarget || target, 'api', 'exports', 'electron.ts');
|
||||
|
||||
return (env = {}, argv = {}) => {
|
||||
const onlyPrintingGraph = !!env.PRINT_WEBPACK_GRAPH;
|
||||
const outputFilename = argv['output-filename'] || `${target}.bundle.js`;
|
||||
|
||||
const defines = {
|
||||
BUILDFLAG: onlyPrintingGraph ? '(a => a)' : ''
|
||||
};
|
||||
|
||||
if (env.buildflags) {
|
||||
const flagFile = fs.readFileSync(env.buildflags, 'utf8');
|
||||
for (const line of flagFile.split(/(\r\n|\r|\n)/g)) {
|
||||
const flagMatch = line.match(/#define BUILDFLAG_INTERNAL_(.+?)\(\) \(([01])\)/);
|
||||
if (flagMatch) {
|
||||
const [, flagName, flagValue] = flagMatch;
|
||||
defines[flagName] = JSON.stringify(Boolean(parseInt(flagValue, 10)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ignoredModules = [];
|
||||
|
||||
const plugins = [];
|
||||
|
||||
if (onlyPrintingGraph) {
|
||||
plugins.push(new AccessDependenciesPlugin());
|
||||
}
|
||||
|
||||
if (targetDeletesNodeGlobals) {
|
||||
plugins.push(new webpack.ProvidePlugin({
|
||||
Buffer: ['@electron/internal/common/webpack-provider', 'Buffer'],
|
||||
global: ['@electron/internal/common/webpack-provider', '_global'],
|
||||
process: ['@electron/internal/common/webpack-provider', 'process']
|
||||
}));
|
||||
}
|
||||
|
||||
// Webpack 5 no longer polyfills process or Buffer.
|
||||
if (!alwaysHasNode) {
|
||||
plugins.push(new webpack.ProvidePlugin({
|
||||
Buffer: ['buffer', 'Buffer'],
|
||||
process: 'process/browser'
|
||||
}));
|
||||
}
|
||||
|
||||
plugins.push(new webpack.ProvidePlugin({
|
||||
Promise: ['@electron/internal/common/webpack-globals-provider', 'Promise']
|
||||
}));
|
||||
|
||||
plugins.push(new webpack.DefinePlugin(defines));
|
||||
|
||||
if (wrapInitWithProfilingTimeout) {
|
||||
plugins.push(new WrapperPlugin({
|
||||
header: 'function ___electron_webpack_init__() {',
|
||||
footer: `
|
||||
};
|
||||
if ((globalThis.process || binding.process).argv.includes("--profile-electron-init")) {
|
||||
setTimeout(___electron_webpack_init__, 0);
|
||||
} else {
|
||||
___electron_webpack_init__();
|
||||
}`
|
||||
}));
|
||||
}
|
||||
|
||||
if (wrapInitWithTryCatch) {
|
||||
plugins.push(new WrapperPlugin({
|
||||
header: 'try {',
|
||||
footer: `
|
||||
} catch (err) {
|
||||
console.error('Electron ${outputFilename} script failed to run');
|
||||
console.error(err);
|
||||
}`
|
||||
}));
|
||||
}
|
||||
|
||||
return {
|
||||
mode: 'development',
|
||||
devtool: false,
|
||||
entry,
|
||||
target: alwaysHasNode ? 'node' : 'web',
|
||||
output: {
|
||||
filename: outputFilename
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@electron/internal': path.resolve(electronRoot, 'lib'),
|
||||
electron$: electronAPIFile,
|
||||
'electron/main$': electronAPIFile,
|
||||
'electron/renderer$': electronAPIFile,
|
||||
'electron/common$': electronAPIFile,
|
||||
'electron/utility$': electronAPIFile,
|
||||
// Force timers to resolve to our own shim that doesn't use window.postMessage
|
||||
timers: path.resolve(electronRoot, 'lib', 'common', 'timers-shim.ts')
|
||||
},
|
||||
extensions: ['.ts', '.js'],
|
||||
fallback: {
|
||||
// We provide our own "timers" import above, any usage of setImmediate inside
|
||||
// one of our renderer bundles should import it from the 'timers' package
|
||||
setImmediate: false
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: (moduleName) => !onlyPrintingGraph && ignoredModules.includes(moduleName),
|
||||
loader: 'null-loader'
|
||||
}, {
|
||||
test: /\.ts$/,
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
configFile: path.resolve(electronRoot, 'tsconfig.electron.json'),
|
||||
transpileOnly: onlyPrintingGraph,
|
||||
ignoreDiagnostics: [
|
||||
// File '{0}' is not under 'rootDir' '{1}'.
|
||||
6059,
|
||||
// Private field '{0}' must be declared in an enclosing class.
|
||||
1111
|
||||
]
|
||||
}
|
||||
}]
|
||||
},
|
||||
node: {
|
||||
__dirname: false,
|
||||
__filename: false
|
||||
},
|
||||
optimization: {
|
||||
minimize: env.mode === 'production',
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
keep_classnames: true,
|
||||
keep_fnames: true
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
plugins
|
||||
};
|
||||
};
|
||||
};
|
||||
4
build/webpack/webpack.config.browser.js
Normal file
4
build/webpack/webpack.config.browser.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'browser',
|
||||
alwaysHasNode: true
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'isolated_renderer',
|
||||
alwaysHasNode: false,
|
||||
wrapInitWithTryCatch: true
|
||||
};
|
||||
});
|
||||
4
build/webpack/webpack.config.node.js
Normal file
4
build/webpack/webpack.config.node.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'node',
|
||||
alwaysHasNode: true
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
module.exports = {
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'preload_realm',
|
||||
alwaysHasNode: false,
|
||||
wrapInitWithProfilingTimeout: true,
|
||||
wrapInitWithTryCatch: true
|
||||
};
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'renderer',
|
||||
alwaysHasNode: true,
|
||||
targetDeletesNodeGlobals: true,
|
||||
wrapInitWithProfilingTimeout: true,
|
||||
wrapInitWithTryCatch: true
|
||||
};
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
module.exports = {
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'sandboxed_renderer',
|
||||
alwaysHasNode: false,
|
||||
wrapInitWithProfilingTimeout: true,
|
||||
wrapInitWithTryCatch: true
|
||||
};
|
||||
});
|
||||
4
build/webpack/webpack.config.utility.js
Normal file
4
build/webpack/webpack.config.utility.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'utility',
|
||||
alwaysHasNode: true
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
module.exports = require('./webpack.config.base')({
|
||||
target: 'worker',
|
||||
loadElectronFromAlternateTarget: 'renderer',
|
||||
alwaysHasNode: true,
|
||||
targetDeletesNodeGlobals: true,
|
||||
wrapInitWithTryCatch: true
|
||||
};
|
||||
});
|
||||
@@ -1,9 +1,9 @@
|
||||
import("../npm.gni")
|
||||
|
||||
template("esbuild_build") {
|
||||
assert(defined(invoker.config_file), "Need esbuild config file to run")
|
||||
template("webpack_build") {
|
||||
assert(defined(invoker.config_file), "Need webpack config file to run")
|
||||
assert(defined(invoker.out_file), "Need output file to run")
|
||||
assert(defined(invoker.inputs), "Need esbuild inputs to run")
|
||||
assert(defined(invoker.inputs), "Need webpack inputs to run")
|
||||
|
||||
npm_action(target_name) {
|
||||
forward_variables_from(invoker,
|
||||
@@ -11,14 +11,11 @@ template("esbuild_build") {
|
||||
"deps",
|
||||
"public_deps",
|
||||
])
|
||||
script = "bundle"
|
||||
script = "webpack"
|
||||
|
||||
inputs = [
|
||||
invoker.config_file,
|
||||
"//electron/build/esbuild/bundle.js",
|
||||
"//electron/build/esbuild/shims/node-globals-shim.js",
|
||||
"//electron/build/esbuild/shims/browser-globals-shim.js",
|
||||
"//electron/build/esbuild/shims/promise-shim.js",
|
||||
"//electron/build/webpack/webpack.config.base.js",
|
||||
"//electron/tsconfig.json",
|
||||
"//electron/yarn.lock",
|
||||
"//electron/typings/internal-ambient.d.ts",
|
||||
@@ -37,10 +34,10 @@ template("esbuild_build") {
|
||||
get_path_info(invoker.out_file, "file"),
|
||||
"--output-path",
|
||||
rebase_path(get_path_info(invoker.out_file, "dir")),
|
||||
"--buildflags",
|
||||
rebase_path("$target_gen_dir/buildflags/buildflags.h"),
|
||||
"--mode",
|
||||
mode,
|
||||
"--env",
|
||||
"buildflags=" + rebase_path("$target_gen_dir/buildflags/buildflags.h"),
|
||||
"--env",
|
||||
"mode=" + mode,
|
||||
]
|
||||
deps += [ "//electron/buildflags" ]
|
||||
|
||||
@@ -174,140 +174,14 @@ auto_filenames = {
|
||||
"docs/api/structures/window-session-end-event.md",
|
||||
]
|
||||
|
||||
typecheck_sources = [
|
||||
"build/esbuild/shims/browser-globals-shim.js",
|
||||
"build/esbuild/shims/node-globals-shim.js",
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/browser/api/app.ts",
|
||||
"lib/browser/api/auto-updater.ts",
|
||||
"lib/browser/api/auto-updater/auto-updater-msix.ts",
|
||||
"lib/browser/api/auto-updater/auto-updater-native.ts",
|
||||
"lib/browser/api/auto-updater/auto-updater-win.ts",
|
||||
"lib/browser/api/auto-updater/msix-update-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/clipboard.ts",
|
||||
"lib/browser/api/content-tracing.ts",
|
||||
"lib/browser/api/crash-reporter.ts",
|
||||
"lib/browser/api/desktop-capturer.ts",
|
||||
"lib/browser/api/dialog.ts",
|
||||
"lib/browser/api/exports/electron.ts",
|
||||
"lib/browser/api/global-shortcut.ts",
|
||||
"lib/browser/api/in-app-purchase.ts",
|
||||
"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/message-channel.ts",
|
||||
"lib/browser/api/module-list.ts",
|
||||
"lib/browser/api/native-theme.ts",
|
||||
"lib/browser/api/net-fetch.ts",
|
||||
"lib/browser/api/net-log.ts",
|
||||
"lib/browser/api/net.ts",
|
||||
"lib/browser/api/notification.ts",
|
||||
"lib/browser/api/power-monitor.ts",
|
||||
"lib/browser/api/power-save-blocker.ts",
|
||||
"lib/browser/api/protocol.ts",
|
||||
"lib/browser/api/push-notifications.ts",
|
||||
"lib/browser/api/safe-storage.ts",
|
||||
"lib/browser/api/screen.ts",
|
||||
"lib/browser/api/service-worker-main.ts",
|
||||
"lib/browser/api/session.ts",
|
||||
"lib/browser/api/share-menu.ts",
|
||||
"lib/browser/api/shared-texture.ts",
|
||||
"lib/browser/api/system-preferences.ts",
|
||||
"lib/browser/api/touch-bar.ts",
|
||||
"lib/browser/api/tray.ts",
|
||||
"lib/browser/api/utility-process.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/api/web-frame-main.ts",
|
||||
"lib/browser/default-menu.ts",
|
||||
"lib/browser/devtools.ts",
|
||||
"lib/browser/guest-view-manager.ts",
|
||||
"lib/browser/guest-window-manager.ts",
|
||||
"lib/browser/init.ts",
|
||||
"lib/browser/ipc-dispatch.ts",
|
||||
"lib/browser/ipc-main-impl.ts",
|
||||
"lib/browser/ipc-main-internal-utils.ts",
|
||||
"lib/browser/ipc-main-internal.ts",
|
||||
"lib/browser/message-port-main.ts",
|
||||
"lib/browser/parse-features-string.ts",
|
||||
"lib/browser/rpc-server.ts",
|
||||
"lib/browser/web-view-events.ts",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/net-client-request.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/deprecate.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/timers-shim.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/isolated_renderer/init.ts",
|
||||
"lib/node/asar-fs-wrapper.ts",
|
||||
"lib/node/init.ts",
|
||||
"lib/preload_realm/api/exports/electron.ts",
|
||||
"lib/preload_realm/api/module-list.ts",
|
||||
"lib/preload_realm/init.ts",
|
||||
"lib/renderer/api/clipboard.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/exports/electron.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.ts",
|
||||
"lib/renderer/api/shared-texture.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/api/web-utils.ts",
|
||||
"lib/renderer/common-init.ts",
|
||||
"lib/renderer/init.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
"lib/renderer/ipc-native-setup.ts",
|
||||
"lib/renderer/ipc-renderer-bindings.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
"lib/renderer/security-warnings.ts",
|
||||
"lib/renderer/web-frame-init.ts",
|
||||
"lib/renderer/web-view/guest-view-internal.ts",
|
||||
"lib/renderer/web-view/web-view-attributes.ts",
|
||||
"lib/renderer/web-view/web-view-constants.ts",
|
||||
"lib/renderer/web-view/web-view-element.ts",
|
||||
"lib/renderer/web-view/web-view-impl.ts",
|
||||
"lib/renderer/web-view/web-view-init.ts",
|
||||
"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/pre-init.ts",
|
||||
"lib/sandboxed_renderer/preload.ts",
|
||||
"lib/utility/api/exports/electron.ts",
|
||||
"lib/utility/api/module-list.ts",
|
||||
"lib/utility/api/net.ts",
|
||||
"lib/utility/init.ts",
|
||||
"lib/utility/parent-port.ts",
|
||||
"lib/worker/init.ts",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
"typings/internal-ambient.d.ts",
|
||||
"typings/internal-electron.d.ts",
|
||||
]
|
||||
|
||||
sandbox_bundle_deps = [
|
||||
"build/esbuild/shims/browser-globals-shim.js",
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/deprecate.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/timers-shim.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
@@ -342,8 +216,6 @@ auto_filenames = {
|
||||
]
|
||||
|
||||
isolated_bundle_deps = [
|
||||
"build/esbuild/shims/browser-globals-shim.js",
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/isolated_renderer/init.ts",
|
||||
"lib/renderer/web-view/web-view-attributes.ts",
|
||||
@@ -358,7 +230,6 @@ auto_filenames = {
|
||||
]
|
||||
|
||||
browser_bundle_deps = [
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/browser/api/app.ts",
|
||||
"lib/browser/api/auto-updater.ts",
|
||||
"lib/browser/api/auto-updater/auto-updater-msix.ts",
|
||||
@@ -429,8 +300,8 @@ auto_filenames = {
|
||||
"lib/common/deprecate.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/timers-shim.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"package.json",
|
||||
"tsconfig.electron.json",
|
||||
"tsconfig.json",
|
||||
@@ -439,8 +310,6 @@ auto_filenames = {
|
||||
]
|
||||
|
||||
renderer_bundle_deps = [
|
||||
"build/esbuild/shims/node-globals-shim.js",
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
@@ -448,8 +317,8 @@ auto_filenames = {
|
||||
"lib/common/deprecate.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/timers-shim.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-provider.ts",
|
||||
"lib/renderer/api/clipboard.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
@@ -483,8 +352,6 @@ auto_filenames = {
|
||||
]
|
||||
|
||||
worker_bundle_deps = [
|
||||
"build/esbuild/shims/node-globals-shim.js",
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/common/api/module-list.ts",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/api/shell.ts",
|
||||
@@ -492,7 +359,7 @@ auto_filenames = {
|
||||
"lib/common/deprecate.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/timers-shim.ts",
|
||||
"lib/common/webpack-provider.ts",
|
||||
"lib/renderer/api/clipboard.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
@@ -514,7 +381,6 @@ auto_filenames = {
|
||||
]
|
||||
|
||||
node_bundle_deps = [
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/node/asar-fs-wrapper.ts",
|
||||
"lib/node/init.ts",
|
||||
"package.json",
|
||||
@@ -525,7 +391,6 @@ auto_filenames = {
|
||||
]
|
||||
|
||||
utility_bundle_deps = [
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/browser/api/net-fetch.ts",
|
||||
"lib/browser/api/system-preferences.ts",
|
||||
"lib/browser/message-port-main.ts",
|
||||
@@ -533,7 +398,7 @@ auto_filenames = {
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/deprecate.ts",
|
||||
"lib/common/init.ts",
|
||||
"lib/common/timers-shim.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/utility/api/exports/electron.ts",
|
||||
"lib/utility/api/module-list.ts",
|
||||
"lib/utility/api/net.ts",
|
||||
@@ -547,11 +412,10 @@ auto_filenames = {
|
||||
]
|
||||
|
||||
preload_realm_bundle_deps = [
|
||||
"build/esbuild/shims/browser-globals-shim.js",
|
||||
"build/esbuild/shims/promise-shim.js",
|
||||
"lib/common/api/native-image.ts",
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/preload_realm/api/exports/electron.ts",
|
||||
"lib/preload_realm/api/module-list.ts",
|
||||
"lib/preload_realm/init.ts",
|
||||
|
||||
@@ -5,27 +5,23 @@ import type { ClientRequestConstructorOptions } from 'electron/main';
|
||||
|
||||
const { isOnline } = process._linkedBinding('electron_common_net');
|
||||
|
||||
function request (options: ClientRequestConstructorOptions | string, callback?: (message: IncomingMessage) => void) {
|
||||
export function request (options: ClientRequestConstructorOptions | string, callback?: (message: IncomingMessage) => void) {
|
||||
if (!app.isReady()) {
|
||||
throw new Error('net module can only be used after app is ready');
|
||||
}
|
||||
return new ClientRequest(options, callback);
|
||||
}
|
||||
|
||||
function fetch (input: RequestInfo, init?: RequestInit): Promise<Response> {
|
||||
export function fetch (input: RequestInfo, init?: RequestInit): Promise<Response> {
|
||||
return session.defaultSession.fetch(input, init);
|
||||
}
|
||||
|
||||
function resolveHost (host: string, options?: Electron.ResolveHostOptions): Promise<Electron.ResolvedHost> {
|
||||
export function resolveHost (host: string, options?: Electron.ResolveHostOptions): Promise<Electron.ResolvedHost> {
|
||||
return session.defaultSession.resolveHost(host, options);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
request,
|
||||
fetch,
|
||||
resolveHost,
|
||||
isOnline,
|
||||
get online () {
|
||||
return isOnline();
|
||||
}
|
||||
};
|
||||
exports.isOnline = isOnline;
|
||||
|
||||
Object.defineProperty(exports, 'online', {
|
||||
get: () => isOnline()
|
||||
});
|
||||
|
||||
@@ -181,7 +181,7 @@ delete process.appCodeLoaded;
|
||||
if (packagePath) {
|
||||
// Finally load app's main.js and transfer control to C++.
|
||||
if ((packageJson.type === 'module' && !mainStartupScript.endsWith('.cjs')) || mainStartupScript.endsWith('.mjs')) {
|
||||
const { runEntryPointWithESMLoader } = __non_webpack_require__('internal/modules/run_main');
|
||||
const { runEntryPointWithESMLoader } = __non_webpack_require__('internal/modules/run_main') as typeof import('@node/lib/internal/modules/run_main');
|
||||
const main = (require('url') as typeof url).pathToFileURL(path.join(packagePath, mainStartupScript));
|
||||
runEntryPointWithESMLoader(async (cascadedLoader: any) => {
|
||||
try {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as timers from 'timers';
|
||||
import timers = require('timers');
|
||||
import * as util from 'util';
|
||||
|
||||
import type * as stream from 'stream';
|
||||
@@ -41,15 +41,15 @@ function wrap <T extends AnyFn> (func: T, wrapper: (fn: AnyFn) => T) {
|
||||
// initiatively activate the uv loop once process.nextTick and setImmediate is
|
||||
// called.
|
||||
process.nextTick = wrapWithActivateUvLoop(process.nextTick);
|
||||
global.setImmediate = wrapWithActivateUvLoop(timers.setImmediate);
|
||||
global.setImmediate = timers.setImmediate = wrapWithActivateUvLoop(timers.setImmediate);
|
||||
global.clearImmediate = timers.clearImmediate;
|
||||
|
||||
// setTimeout needs to update the polling timeout of the event loop, when
|
||||
// called under Chromium's event loop the node's event loop won't get a chance
|
||||
// to update the timeout, so we have to force the node's event loop to
|
||||
// recalculate the timeout in the process.
|
||||
const wrappedSetTimeout = wrapWithActivateUvLoop(timers.setTimeout);
|
||||
const wrappedSetInterval = wrapWithActivateUvLoop(timers.setInterval);
|
||||
timers.setTimeout = wrapWithActivateUvLoop(timers.setTimeout);
|
||||
timers.setInterval = wrapWithActivateUvLoop(timers.setInterval);
|
||||
|
||||
// Update the global version of the timer apis to use the above wrapper
|
||||
// only in the process that runs node event loop alongside chromium
|
||||
@@ -57,8 +57,8 @@ const wrappedSetInterval = wrapWithActivateUvLoop(timers.setInterval);
|
||||
// are deleted in these processes, see renderer/init.js for reference.
|
||||
if (process.type === 'browser' ||
|
||||
process.type === 'utility') {
|
||||
global.setTimeout = wrappedSetTimeout;
|
||||
global.setInterval = wrappedSetInterval;
|
||||
global.setTimeout = timers.setTimeout;
|
||||
global.setInterval = timers.setInterval;
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Drop-in replacement for timers-browserify@1.4.2.
|
||||
// Provides the Node.js 'timers' API surface for renderer/web bundles
|
||||
// Provides the Node.js 'timers' API surface for renderer/web webpack bundles
|
||||
// without relying on window.postMessage (which the newer timers-browserify 2.x
|
||||
// polyfill uses and can interfere with Electron IPC).
|
||||
|
||||
|
||||
8
lib/common/webpack-globals-provider.ts
Normal file
8
lib/common/webpack-globals-provider.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// Captures original globals into a scope to ensure that userland modifications do
|
||||
// not impact Electron. Note that users doing:
|
||||
//
|
||||
// global.Promise.resolve = myFn
|
||||
//
|
||||
// Will mutate this captured one as well and that is OK.
|
||||
|
||||
export const Promise = global.Promise;
|
||||
18
lib/common/webpack-provider.ts
Normal file
18
lib/common/webpack-provider.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
// This file provides the global, process and Buffer variables to internal
|
||||
// Electron code once they have been deleted from the global scope.
|
||||
//
|
||||
// It does this through the ProvidePlugin in the webpack.config.base.js file
|
||||
// Check out the Module.wrapper override in renderer/init.ts for more
|
||||
// information on how this works and why we need it
|
||||
|
||||
// Rip global off of window (which is also global) so that webpack doesn't
|
||||
// auto replace it with a looped reference to this file
|
||||
const _global = typeof globalThis !== 'undefined' ? globalThis.global : (self || window).global;
|
||||
const process = _global.process;
|
||||
const Buffer = _global.Buffer;
|
||||
|
||||
export {
|
||||
_global,
|
||||
process,
|
||||
Buffer
|
||||
};
|
||||
@@ -52,20 +52,20 @@ const {
|
||||
getValidatedPath,
|
||||
getOptions,
|
||||
getDirent
|
||||
} = __non_webpack_require__('internal/fs/utils');
|
||||
} = __non_webpack_require__('internal/fs/utils') as typeof import('@node/lib/internal/fs/utils');
|
||||
|
||||
const {
|
||||
assignFunctionName
|
||||
} = __non_webpack_require__('internal/util');
|
||||
} = __non_webpack_require__('internal/util') as typeof import('@node/lib/internal/util');
|
||||
|
||||
const {
|
||||
validateBoolean,
|
||||
validateFunction
|
||||
} = __non_webpack_require__('internal/validators');
|
||||
} = __non_webpack_require__('internal/validators') as typeof import('@node/lib/internal/validators');
|
||||
|
||||
// In the renderer node internals use the node global URL but we do not set that to be
|
||||
// the global URL instance. We need to do instanceof checks against the internal URL impl.
|
||||
const { URL: NodeURL } = __non_webpack_require__('internal/url');
|
||||
// the global URL instance. We need to do instanceof checks against the internal URL impl
|
||||
const { URL: NodeURL } = __non_webpack_require__('internal/url') as typeof import('@node/lib/internal/url');
|
||||
|
||||
// Separate asar package's path from full path.
|
||||
const splitPath = (archivePathOrBuffer: string | Buffer | URL) => {
|
||||
|
||||
@@ -29,9 +29,8 @@ Module._load = function (request: string) {
|
||||
// code with JavaScript.
|
||||
//
|
||||
// Note 3: We provide the equivalent extra variables internally through the
|
||||
// esbuild inject shim in build/esbuild/shims/node-globals-shim.js. If you
|
||||
// add any extra variables to this wrapper please ensure to update that shim
|
||||
// as well.
|
||||
// webpack ProvidePlugin in webpack.config.base.js. If you add any extra
|
||||
// variables to this wrapper please ensure to update that plugin as well.
|
||||
Module.wrapper = [
|
||||
'(function (exports, require, module, __filename, __dirname, process, global, Buffer) { ' +
|
||||
// By running the code in a new closure, it would be possible for the module
|
||||
@@ -66,9 +65,9 @@ require('@electron/internal/renderer/common-init');
|
||||
|
||||
if (nodeIntegration) {
|
||||
// Export node bindings to global.
|
||||
const { makeRequireFunction } = __non_webpack_require__('internal/modules/helpers');
|
||||
const { makeRequireFunction } = __non_webpack_require__('internal/modules/helpers') as typeof import('@node/lib/internal/modules/helpers');
|
||||
global.module = new Module('electron/js2c/renderer_init');
|
||||
global.require = makeRequireFunction(global.module);
|
||||
global.require = makeRequireFunction(global.module) as NodeRequire;
|
||||
|
||||
// Set the __filename to the path of html file if it is file: protocol.
|
||||
if (window.location.protocol === 'file:') {
|
||||
@@ -153,7 +152,7 @@ if (cjsPreloads.length) {
|
||||
}
|
||||
}
|
||||
if (esmPreloads.length) {
|
||||
const { runEntryPointWithESMLoader } = __non_webpack_require__('internal/modules/run_main');
|
||||
const { runEntryPointWithESMLoader } = __non_webpack_require__('internal/modules/run_main') as typeof import('@node/lib/internal/modules/run_main');
|
||||
|
||||
runEntryPointWithESMLoader(async (cascadedLoader: any) => {
|
||||
// Load the preload scripts.
|
||||
|
||||
@@ -5,20 +5,18 @@ import type { ClientRequestConstructorOptions, IncomingMessage } from 'electron/
|
||||
|
||||
const { isOnline, resolveHost } = process._linkedBinding('electron_common_net');
|
||||
|
||||
function request (options: ClientRequestConstructorOptions | string, callback?: (message: IncomingMessage) => void) {
|
||||
export function request (options: ClientRequestConstructorOptions | string, callback?: (message: IncomingMessage) => void) {
|
||||
return new ClientRequest(options, callback);
|
||||
}
|
||||
|
||||
function fetch (input: RequestInfo, init?: RequestInit): Promise<Response> {
|
||||
export function fetch (input: RequestInfo, init?: RequestInit): Promise<Response> {
|
||||
return fetchWithSession(input, init, undefined, request);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
request,
|
||||
fetch,
|
||||
resolveHost,
|
||||
isOnline,
|
||||
get online () {
|
||||
return isOnline();
|
||||
}
|
||||
};
|
||||
exports.resolveHost = resolveHost;
|
||||
|
||||
exports.isOnline = isOnline;
|
||||
|
||||
Object.defineProperty(exports, 'online', {
|
||||
get: () => isOnline()
|
||||
});
|
||||
|
||||
@@ -36,7 +36,7 @@ parentPort.on('removeListener', (name: string) => {
|
||||
});
|
||||
|
||||
// Finally load entry script.
|
||||
const { runEntryPointWithESMLoader } = __non_webpack_require__('internal/modules/run_main');
|
||||
const { runEntryPointWithESMLoader } = __non_webpack_require__('internal/modules/run_main') as typeof import('@node/lib/internal/modules/run_main');
|
||||
const mainEntry = pathToFileURL(entryScript);
|
||||
|
||||
runEntryPointWithESMLoader(async (cascadedLoader: any) => {
|
||||
|
||||
@@ -13,9 +13,9 @@ require('@electron/internal/common/init');
|
||||
const { hasSwitch, getSwitchValue } = process._linkedBinding('electron_common_command_line');
|
||||
|
||||
// Export node bindings to global.
|
||||
const { makeRequireFunction } = __non_webpack_require__('internal/modules/helpers');
|
||||
const { makeRequireFunction } = __non_webpack_require__('internal/modules/helpers') as typeof import('@node/lib/internal/modules/helpers');
|
||||
global.module = new Module('electron/js2c/worker_init');
|
||||
global.require = makeRequireFunction(global.module);
|
||||
global.require = makeRequireFunction(global.module) as NodeRequire;
|
||||
|
||||
// See WebWorkerObserver::WorkerScriptReadyForEvaluation.
|
||||
if ((globalThis as any).blinkfetch) {
|
||||
|
||||
12
package.json
12
package.json
@@ -23,12 +23,10 @@
|
||||
"@types/temp": "^0.9.4",
|
||||
"@typescript-eslint/eslint-plugin": "^8.32.1",
|
||||
"@typescript-eslint/parser": "^8.7.0",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20260324.1",
|
||||
"@xmldom/xmldom": "^0.8.11",
|
||||
"buffer": "^6.0.3",
|
||||
"chalk": "^4.1.0",
|
||||
"check-for-leaks": "^1.2.1",
|
||||
"esbuild": "^0.25.0",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
@@ -45,15 +43,20 @@
|
||||
"markdownlint-cli2": "^0.18.0",
|
||||
"minimist": "^1.2.8",
|
||||
"node-gyp": "^11.4.2",
|
||||
"null-loader": "^4.0.1",
|
||||
"pre-flight": "^2.0.0",
|
||||
"process": "^0.11.10",
|
||||
"semver": "^7.6.3",
|
||||
"stream-json": "^1.9.1",
|
||||
"tap-xunit": "^2.4.1",
|
||||
"temp": "^0.9.4",
|
||||
"ts-loader": "^8.0.2",
|
||||
"ts-node": "6.2.0",
|
||||
"typescript": "^5.8.3",
|
||||
"url": "^0.11.4",
|
||||
"webpack": "^5.104.1",
|
||||
"webpack-cli": "^6.0.1",
|
||||
"wrapper-webpack-plugin": "^2.2.0",
|
||||
"yaml": "^2.8.1"
|
||||
},
|
||||
"private": true,
|
||||
@@ -90,9 +93,8 @@
|
||||
"repl": "node ./script/start.js --interactive",
|
||||
"start": "node ./script/start.js",
|
||||
"test": "node ./script/spec-runner.js",
|
||||
"tsc": "tsgo",
|
||||
"tsc-check": "node script/typecheck.js",
|
||||
"bundle": "node build/esbuild/bundle.js"
|
||||
"tsc": "tsc",
|
||||
"webpack": "webpack"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "Electron Community",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as cp from 'node:child_process';
|
||||
import * as fs from 'node:fs';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
|
||||
const rootPath = path.resolve(__dirname, '..');
|
||||
@@ -15,24 +16,52 @@ const allDocs = fs.readdirSync(path.resolve(__dirname, '../docs/api'))
|
||||
const typingFiles = fs.readdirSync(path.resolve(__dirname, '../typings')).map(child => `typings/${child}`);
|
||||
|
||||
const main = async () => {
|
||||
const bundleTargets = [
|
||||
{ name: 'sandbox_bundle_deps', config: 'sandboxed_renderer.js' },
|
||||
{ name: 'isolated_bundle_deps', config: 'isolated_renderer.js' },
|
||||
{ name: 'browser_bundle_deps', config: 'browser.js' },
|
||||
{ name: 'renderer_bundle_deps', config: 'renderer.js' },
|
||||
{ name: 'worker_bundle_deps', config: 'worker.js' },
|
||||
{ name: 'node_bundle_deps', config: 'node.js' },
|
||||
{ name: 'utility_bundle_deps', config: 'utility.js' },
|
||||
{ name: 'preload_realm_bundle_deps', config: 'preload_realm.js' }
|
||||
const webpackTargets = [
|
||||
{
|
||||
name: 'sandbox_bundle_deps',
|
||||
config: 'webpack.config.sandboxed_renderer.js'
|
||||
},
|
||||
{
|
||||
name: 'isolated_bundle_deps',
|
||||
config: 'webpack.config.isolated_renderer.js'
|
||||
},
|
||||
{
|
||||
name: 'browser_bundle_deps',
|
||||
config: 'webpack.config.browser.js'
|
||||
},
|
||||
{
|
||||
name: 'renderer_bundle_deps',
|
||||
config: 'webpack.config.renderer.js'
|
||||
},
|
||||
{
|
||||
name: 'worker_bundle_deps',
|
||||
config: 'webpack.config.worker.js'
|
||||
},
|
||||
{
|
||||
name: 'node_bundle_deps',
|
||||
config: 'webpack.config.node.js'
|
||||
},
|
||||
{
|
||||
name: 'utility_bundle_deps',
|
||||
config: 'webpack.config.utility.js'
|
||||
},
|
||||
{
|
||||
name: 'preload_realm_bundle_deps',
|
||||
config: 'webpack.config.preload_realm.js'
|
||||
}
|
||||
];
|
||||
|
||||
const targetsWithDeps = await Promise.all(bundleTargets.map(async bundleTarget => {
|
||||
const webpackTargetsWithDeps = await Promise.all(webpackTargets.map(async webpackTarget => {
|
||||
const tmpDir = await fs.promises.mkdtemp(path.resolve(os.tmpdir(), 'electron-filenames-'));
|
||||
const child = cp.spawn('node', [
|
||||
'./build/esbuild/bundle.js',
|
||||
'--config', `./build/esbuild/configs/${bundleTarget.config}`,
|
||||
'--print-graph'
|
||||
'./node_modules/webpack-cli/bin/cli.js',
|
||||
'--config', `./build/webpack/${webpackTarget.config}`,
|
||||
'--stats', 'errors-only',
|
||||
'--output-path', tmpDir,
|
||||
'--output-filename', `${webpackTarget.name}.measure.js`,
|
||||
'--env', 'PRINT_WEBPACK_GRAPH'
|
||||
], {
|
||||
cwd: rootPath
|
||||
cwd: path.resolve(__dirname, '..')
|
||||
});
|
||||
let output = '';
|
||||
child.stdout.on('data', chunk => {
|
||||
@@ -42,33 +71,32 @@ const main = async () => {
|
||||
await new Promise<void>((resolve, reject) => child.on('exit', (code) => {
|
||||
if (code !== 0) {
|
||||
console.error(output);
|
||||
return reject(new Error(`Failed to list bundle dependencies for entry: ${bundleTarget.name}`));
|
||||
return reject(new Error(`Failed to list webpack dependencies for entry: ${webpackTarget.name}`));
|
||||
}
|
||||
|
||||
resolve();
|
||||
}));
|
||||
|
||||
return {
|
||||
...bundleTarget,
|
||||
const webpackTargetWithDeps = {
|
||||
...webpackTarget,
|
||||
dependencies: (JSON.parse(output) as string[])
|
||||
// Remove whitespace
|
||||
.map(line => line.trim())
|
||||
.map(line => path.relative(rootPath, path.resolve(rootPath, line)).replace(/\\/g, '/'))
|
||||
// Get the relative path
|
||||
.map(line => path.relative(rootPath, line).replace(/\\/g, '/'))
|
||||
// Only care about files in //electron
|
||||
.filter(line => !line.startsWith('..'))
|
||||
// Only care about our own files
|
||||
.filter(line => !line.startsWith('node_modules'))
|
||||
// All webpack builds depend on the tsconfig and package json files
|
||||
.concat(['tsconfig.json', 'tsconfig.electron.json', 'package.json', ...typingFiles])
|
||||
// Make the generated list easier to read
|
||||
.sort()
|
||||
};
|
||||
await fs.promises.rm(tmpDir, { force: true, recursive: true });
|
||||
return webpackTargetWithDeps;
|
||||
}));
|
||||
|
||||
// The typecheck step runs tsgo over tsconfig.electron.json which includes
|
||||
// the whole lib/ + typings/ trees. For GN dependency tracking, list the
|
||||
// union of every bundle's dependency set (lib files) plus typings, and
|
||||
// dedupe.
|
||||
const typecheckSources = Array.from(new Set([
|
||||
...targetsWithDeps.flatMap(t => t.dependencies),
|
||||
...typingFiles
|
||||
])).sort();
|
||||
|
||||
fs.writeFileSync(
|
||||
gniPath,
|
||||
`# THIS FILE IS AUTO-GENERATED, PLEASE DO NOT EDIT BY HAND
|
||||
@@ -77,11 +105,7 @@ auto_filenames = {
|
||||
${allDocs.map(doc => ` "${doc}",`).join('\n')}
|
||||
]
|
||||
|
||||
typecheck_sources = [
|
||||
${typecheckSources.map(src => ` "${src}",`).join('\n')}
|
||||
]
|
||||
|
||||
${targetsWithDeps.map(target => ` ${target.name} = [
|
||||
${webpackTargetsWithDeps.map(target => ` ${target.name} = [
|
||||
${target.dependencies.map(dep => ` "${dep}",`).join('\n')}
|
||||
]`).join('\n\n')}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Octokit } from '@octokit/rest';
|
||||
|
||||
import { strict as assert } from 'node:assert';
|
||||
import * as assert from 'node:assert';
|
||||
|
||||
import { createGitHubTokenStrategy } from './github-token';
|
||||
import { ELECTRON_ORG, ELECTRON_REPO } from './types';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import minimist = require('minimist');
|
||||
import streamChain = require('stream-chain');
|
||||
import streamJson = require('stream-json');
|
||||
import * as minimist from 'minimist';
|
||||
import * as streamChain from 'stream-chain';
|
||||
import * as streamJson from 'stream-json';
|
||||
import { ignore as streamJsonIgnore } from 'stream-json/filters/Ignore';
|
||||
import { streamArray as streamJsonStreamArray } from 'stream-json/streamers/StreamArray';
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
// Runs `tsgo --noEmit -p <tsconfig>` and writes a stamp file on success,
|
||||
// so GN can track typecheck results as a build output.
|
||||
//
|
||||
// Usage: node script/typecheck.js --tsconfig <path> --stamp <path>
|
||||
|
||||
'use strict';
|
||||
|
||||
const cp = require('node:child_process');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
function parseArgs (argv) {
|
||||
const out = {};
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
const a = argv[i];
|
||||
if (a.startsWith('--')) {
|
||||
const next = argv[i + 1];
|
||||
if (next === undefined || next.startsWith('--')) {
|
||||
out[a.slice(2)] = true;
|
||||
} else {
|
||||
out[a.slice(2)] = next;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
if (!args.tsconfig || !args.stamp) {
|
||||
console.error('Usage: typecheck.js --tsconfig <path> --stamp <path>');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const electronRoot = path.resolve(__dirname, '..');
|
||||
// Resolve tsgo's bin entry directly from the package's `bin` map and run it
|
||||
// via the current Node executable. We can't `require.resolve` the bin path
|
||||
// (the package's `exports` field doesn't expose it) and we can't spawn
|
||||
// `node_modules/.bin/tsgo` directly on Windows (it's a `.cmd` shim).
|
||||
const tsgoPkgPath = require.resolve('@typescript/native-preview/package.json', {
|
||||
paths: [electronRoot]
|
||||
});
|
||||
const tsgoPkg = JSON.parse(fs.readFileSync(tsgoPkgPath, 'utf8'));
|
||||
const tsgoEntry = path.resolve(path.dirname(tsgoPkgPath), tsgoPkg.bin.tsgo);
|
||||
|
||||
const child = cp.spawnSync(
|
||||
process.execPath,
|
||||
[tsgoEntry, '--noEmit', '-p', path.resolve(args.tsconfig)],
|
||||
{ cwd: electronRoot, stdio: 'inherit' }
|
||||
);
|
||||
|
||||
if (child.status !== 0) {
|
||||
process.exit(child.status || 1);
|
||||
}
|
||||
|
||||
fs.mkdirSync(path.dirname(args.stamp), { recursive: true });
|
||||
fs.writeFileSync(args.stamp, '');
|
||||
@@ -80,9 +80,12 @@ gin::DeprecatedWrapperInfo ServiceWorkerContext::kWrapperInfo = {
|
||||
|
||||
ServiceWorkerContext::ServiceWorkerContext(
|
||||
v8::Isolate* isolate,
|
||||
ElectronBrowserContext* browser_context) {
|
||||
storage_partition_ = browser_context->GetDefaultStoragePartition();
|
||||
service_worker_context_ = storage_partition_->GetServiceWorkerContext();
|
||||
ElectronBrowserContext* browser_context)
|
||||
: service_worker_context_{browser_context->GetDefaultStoragePartition()
|
||||
->GetServiceWorkerContext()},
|
||||
browser_context_id_{browser_context->UniqueId()},
|
||||
storage_partition_config_{
|
||||
browser_context->GetDefaultStoragePartition()->GetConfig()} {
|
||||
service_worker_context_->AddObserver(this);
|
||||
}
|
||||
|
||||
@@ -93,9 +96,8 @@ ServiceWorkerContext::~ServiceWorkerContext() {
|
||||
void ServiceWorkerContext::OnRunningStatusChanged(
|
||||
int64_t version_id,
|
||||
blink::EmbeddedWorkerStatus running_status) {
|
||||
ServiceWorkerMain* worker =
|
||||
ServiceWorkerMain::FromVersionID(version_id, storage_partition_);
|
||||
if (worker)
|
||||
if (auto* worker = ServiceWorkerMain::FromVersionID(
|
||||
browser_context_id_, storage_partition_config_, version_id))
|
||||
worker->OnRunningStatusChanged(running_status);
|
||||
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
@@ -133,9 +135,8 @@ void ServiceWorkerContext::OnRegistrationCompleted(const GURL& scope) {
|
||||
|
||||
void ServiceWorkerContext::OnVersionRedundant(int64_t version_id,
|
||||
const GURL& scope) {
|
||||
ServiceWorkerMain* worker =
|
||||
ServiceWorkerMain::FromVersionID(version_id, storage_partition_);
|
||||
if (worker)
|
||||
if (auto* worker = ServiceWorkerMain::FromVersionID(
|
||||
browser_context_id_, storage_partition_config_, version_id))
|
||||
worker->OnVersionRedundant();
|
||||
}
|
||||
|
||||
@@ -206,18 +207,19 @@ v8::Local<v8::Value> ServiceWorkerContext::GetWorkerFromVersionID(
|
||||
v8::Isolate* isolate,
|
||||
int64_t version_id) {
|
||||
return ServiceWorkerMain::From(isolate, service_worker_context_,
|
||||
storage_partition_, version_id)
|
||||
browser_context_id_, storage_partition_config_,
|
||||
version_id)
|
||||
.ToV8();
|
||||
}
|
||||
|
||||
gin_helper::Handle<ServiceWorkerMain>
|
||||
ServiceWorkerContext::GetWorkerFromVersionIDIfExists(v8::Isolate* isolate,
|
||||
int64_t version_id) {
|
||||
ServiceWorkerMain* worker =
|
||||
ServiceWorkerMain::FromVersionID(version_id, storage_partition_);
|
||||
if (!worker)
|
||||
return gin_helper::Handle<ServiceWorkerMain>();
|
||||
return gin_helper::CreateHandle(isolate, worker);
|
||||
if (auto* worker = ServiceWorkerMain::FromVersionID(
|
||||
browser_context_id_, storage_partition_config_, version_id))
|
||||
return gin_helper::CreateHandle(isolate, worker);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
v8::Local<v8::Promise> ServiceWorkerContext::StartWorkerForScope(
|
||||
|
||||
@@ -5,18 +5,17 @@
|
||||
#ifndef ELECTRON_SHELL_BROWSER_API_ELECTRON_API_SERVICE_WORKER_CONTEXT_H_
|
||||
#define ELECTRON_SHELL_BROWSER_API_ELECTRON_API_SERVICE_WORKER_CONTEXT_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "content/public/browser/service_worker_context.h"
|
||||
#include "content/public/browser/service_worker_context_observer.h"
|
||||
#include "content/public/browser/storage_partition_config.h"
|
||||
#include "shell/browser/event_emitter_mixin.h"
|
||||
#include "shell/common/gin_helper/wrappable.h"
|
||||
#include "third_party/blink/public/common/service_worker/embedded_worker_status.h"
|
||||
#include "third_party/blink/public/common/tokens/tokens.h"
|
||||
|
||||
namespace content {
|
||||
class StoragePartition;
|
||||
}
|
||||
|
||||
namespace gin_helper {
|
||||
template <typename T>
|
||||
class Handle;
|
||||
@@ -99,9 +98,13 @@ class ServiceWorkerContext final
|
||||
|
||||
raw_ptr<content::ServiceWorkerContext> service_worker_context_;
|
||||
|
||||
// Service worker registration and versions are unique to a storage partition.
|
||||
// Keep a reference to the storage partition to be used for lookups.
|
||||
raw_ptr<content::StoragePartition> storage_partition_;
|
||||
// A key identifying the owning BrowserContext.
|
||||
// Used in ServiceWorkerMain lookups.
|
||||
const std::string browser_context_id_;
|
||||
|
||||
// A key identifying a StoragePartition within a BrowserContext.
|
||||
// Used in ServiceWorkerMain lookups.
|
||||
const content::StoragePartitionConfig storage_partition_config_;
|
||||
|
||||
base::WeakPtrFactory<ServiceWorkerContext> weak_ptr_factory_{this};
|
||||
};
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "base/containers/flat_map.h"
|
||||
#include "base/containers/map_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "content/browser/service_worker/service_worker_context_wrapper.h" // nogncheck
|
||||
@@ -27,7 +29,6 @@
|
||||
#include "shell/common/gin_helper/promise.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "shell/common/v8_util.h"
|
||||
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -58,27 +59,23 @@ std::optional<content::ServiceWorkerVersionBaseInfo> GetLiveVersionInfo(
|
||||
namespace electron::api {
|
||||
|
||||
// ServiceWorkerKey -> ServiceWorkerMain*
|
||||
using VersionIdMap = absl::flat_hash_map<ServiceWorkerKey,
|
||||
ServiceWorkerMain*,
|
||||
ServiceWorkerKey::Hasher>;
|
||||
|
||||
VersionIdMap& GetVersionIdMap() {
|
||||
static base::NoDestructor<VersionIdMap> instance;
|
||||
auto& GetVersionIdMap() {
|
||||
using Map = base::flat_map<ServiceWorkerKey, ServiceWorkerMain*>;
|
||||
static base::NoDestructor<Map> instance;
|
||||
return *instance;
|
||||
}
|
||||
|
||||
ServiceWorkerMain* FromServiceWorkerKey(const ServiceWorkerKey& key) {
|
||||
VersionIdMap& version_map = GetVersionIdMap();
|
||||
auto iter = version_map.find(key);
|
||||
auto* service_worker = iter == version_map.end() ? nullptr : iter->second;
|
||||
return service_worker;
|
||||
return base::FindPtrOrNull(GetVersionIdMap(), key);
|
||||
}
|
||||
|
||||
// static
|
||||
ServiceWorkerMain* ServiceWorkerMain::FromVersionID(
|
||||
int64_t version_id,
|
||||
const content::StoragePartition* storage_partition) {
|
||||
ServiceWorkerKey key(version_id, storage_partition);
|
||||
std::string browser_context_id,
|
||||
content::StoragePartitionConfig storage_partition_config,
|
||||
int64_t version_id) {
|
||||
const ServiceWorkerKey key{std::move(browser_context_id),
|
||||
std::move(storage_partition_config), version_id};
|
||||
return FromServiceWorkerKey(key);
|
||||
}
|
||||
|
||||
@@ -87,8 +84,10 @@ gin::DeprecatedWrapperInfo ServiceWorkerMain::kWrapperInfo = {
|
||||
|
||||
ServiceWorkerMain::ServiceWorkerMain(content::ServiceWorkerContext* sw_context,
|
||||
int64_t version_id,
|
||||
const ServiceWorkerKey& key)
|
||||
: version_id_(version_id), key_(key), service_worker_context_(sw_context) {
|
||||
ServiceWorkerKey key)
|
||||
: version_id_{version_id},
|
||||
key_{std::move(key)},
|
||||
service_worker_context_{sw_context} {
|
||||
GetVersionIdMap().emplace(key_, this);
|
||||
InvalidateVersionInfo();
|
||||
}
|
||||
@@ -298,12 +297,14 @@ gin_helper::Handle<ServiceWorkerMain> ServiceWorkerMain::New(
|
||||
gin_helper::Handle<ServiceWorkerMain> ServiceWorkerMain::From(
|
||||
v8::Isolate* isolate,
|
||||
content::ServiceWorkerContext* sw_context,
|
||||
const content::StoragePartition* storage_partition,
|
||||
std::string browser_context_id,
|
||||
content::StoragePartitionConfig storage_partition_config,
|
||||
int64_t version_id) {
|
||||
ServiceWorkerKey service_worker_key(version_id, storage_partition);
|
||||
ServiceWorkerKey service_worker_key{std::move(browser_context_id),
|
||||
std::move(storage_partition_config),
|
||||
version_id};
|
||||
|
||||
auto* service_worker = FromServiceWorkerKey(service_worker_key);
|
||||
if (service_worker)
|
||||
if (auto* service_worker = FromServiceWorkerKey(service_worker_key))
|
||||
return gin_helper::CreateHandle(isolate, service_worker);
|
||||
|
||||
// Ensure ServiceWorkerVersion exists and is not redundant (pending deletion)
|
||||
@@ -313,8 +314,8 @@ gin_helper::Handle<ServiceWorkerMain> ServiceWorkerMain::From(
|
||||
}
|
||||
|
||||
auto handle = gin_helper::CreateHandle(
|
||||
isolate,
|
||||
new ServiceWorkerMain(sw_context, version_id, service_worker_key));
|
||||
isolate, new ServiceWorkerMain{sw_context, version_id,
|
||||
std::move(service_worker_key)});
|
||||
|
||||
// Prevent garbage collection of worker until it has been deleted internally.
|
||||
handle->Pin(isolate);
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
#ifndef ELECTRON_SHELL_BROWSER_API_ELECTRON_API_SERVICE_WORKER_MAIN_H_
|
||||
#define ELECTRON_SHELL_BROWSER_API_ELECTRON_API_SERVICE_WORKER_MAIN_H_
|
||||
|
||||
#include <compare>
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/process/process.h"
|
||||
#include "content/public/browser/global_routing_id.h"
|
||||
#include "content/public/browser/service_worker_context.h"
|
||||
#include "content/public/browser/service_worker_version_base_info.h"
|
||||
#include "content/public/browser/storage_partition_config.h"
|
||||
#include "mojo/public/cpp/bindings/associated_receiver.h"
|
||||
#include "mojo/public/cpp/bindings/associated_remote.h"
|
||||
#include "mojo/public/cpp/bindings/pending_receiver.h"
|
||||
@@ -24,10 +24,6 @@
|
||||
|
||||
class GURL;
|
||||
|
||||
namespace content {
|
||||
class StoragePartition;
|
||||
}
|
||||
|
||||
namespace gin {
|
||||
class Arguments;
|
||||
} // namespace gin
|
||||
@@ -42,41 +38,21 @@ class Promise;
|
||||
|
||||
namespace electron::api {
|
||||
|
||||
// Key to uniquely identify a ServiceWorkerMain by its Version ID within the
|
||||
// associated StoragePartition.
|
||||
// Key to uniquely identify a ServiceWorkerMain by its
|
||||
// BrowserContext ID, the StoragePartition key, and version id.
|
||||
struct ServiceWorkerKey {
|
||||
std::string browser_context_id;
|
||||
content::StoragePartitionConfig storage_partition_config;
|
||||
int64_t version_id;
|
||||
raw_ptr<const content::StoragePartition> storage_partition;
|
||||
|
||||
ServiceWorkerKey(int64_t id, const content::StoragePartition* partition)
|
||||
: version_id(id), storage_partition(partition) {}
|
||||
|
||||
bool operator<(const ServiceWorkerKey& other) const {
|
||||
return std::tie(version_id, storage_partition) <
|
||||
std::tie(other.version_id, other.storage_partition);
|
||||
}
|
||||
|
||||
bool operator==(const ServiceWorkerKey& other) const {
|
||||
return version_id == other.version_id &&
|
||||
storage_partition == other.storage_partition;
|
||||
}
|
||||
|
||||
struct Hasher {
|
||||
std::size_t operator()(const ServiceWorkerKey& key) const {
|
||||
return std::hash<const content::StoragePartition*>()(
|
||||
key.storage_partition) ^
|
||||
std::hash<int64_t>()(key.version_id);
|
||||
}
|
||||
};
|
||||
auto operator<=>(const ServiceWorkerKey&) const = default;
|
||||
};
|
||||
|
||||
// Creates a wrapper to align with the lifecycle of the non-public
|
||||
// content::ServiceWorkerVersion. Object instances are pinned for the lifetime
|
||||
// of the underlying SW such that registered IPC handlers continue to dispatch.
|
||||
//
|
||||
// Instances are uniquely identified by pairing their version ID and the
|
||||
// StoragePartition in which they're registered. In Electron, this is always
|
||||
// the default StoragePartition for the associated BrowserContext.
|
||||
// Instances are uniquely identified by pairing their version ID with the
|
||||
// BrowserContext and StoragePartition in which they're registered.
|
||||
class ServiceWorkerMain final
|
||||
: public gin_helper::DeprecatedWrappable<ServiceWorkerMain>,
|
||||
public gin_helper::Pinnable<ServiceWorkerMain>,
|
||||
@@ -88,11 +64,13 @@ class ServiceWorkerMain final
|
||||
static gin_helper::Handle<ServiceWorkerMain> From(
|
||||
v8::Isolate* isolate,
|
||||
content::ServiceWorkerContext* sw_context,
|
||||
const content::StoragePartition* storage_partition,
|
||||
std::string browser_context_id,
|
||||
content::StoragePartitionConfig storage_partition_config,
|
||||
int64_t version_id);
|
||||
static ServiceWorkerMain* FromVersionID(
|
||||
int64_t version_id,
|
||||
const content::StoragePartition* storage_partition);
|
||||
std::string browser_context_id,
|
||||
content::StoragePartitionConfig storage_partition_config,
|
||||
int64_t version_id);
|
||||
|
||||
// gin_helper::Constructible
|
||||
static void FillObjectTemplate(v8::Isolate*, v8::Local<v8::ObjectTemplate>);
|
||||
@@ -112,7 +90,7 @@ class ServiceWorkerMain final
|
||||
protected:
|
||||
explicit ServiceWorkerMain(content::ServiceWorkerContext* sw_context,
|
||||
int64_t version_id,
|
||||
const ServiceWorkerKey& key);
|
||||
ServiceWorkerKey key);
|
||||
~ServiceWorkerMain() override;
|
||||
|
||||
private:
|
||||
@@ -146,11 +124,12 @@ class ServiceWorkerMain final
|
||||
GURL ScopeURL() const;
|
||||
GURL ScriptURL() const;
|
||||
|
||||
// Version ID unique only to the StoragePartition.
|
||||
int64_t version_id_;
|
||||
// Version ID assigned by the service worker storage.
|
||||
const int64_t version_id_;
|
||||
|
||||
// Unique identifier pairing the Version ID and StoragePartition.
|
||||
ServiceWorkerKey key_;
|
||||
// Unique identifier pairing the Version ID, BrowserContext, and
|
||||
// StoragePartition.
|
||||
const ServiceWorkerKey key_;
|
||||
|
||||
// Whether the Service Worker version has been destroyed.
|
||||
bool version_destroyed_ = false;
|
||||
|
||||
@@ -1767,7 +1767,8 @@ bool WebContents::CheckMediaAccessPermission(
|
||||
content::WebContents::FromRenderFrameHost(render_frame_host);
|
||||
auto* permission_helper =
|
||||
WebContentsPermissionHelper::FromWebContents(web_contents);
|
||||
return permission_helper->CheckMediaAccessPermission(security_origin, type);
|
||||
return permission_helper->CheckMediaAccessPermission(render_frame_host,
|
||||
security_origin, type);
|
||||
}
|
||||
|
||||
void WebContents::RequestMediaAccessPermission(
|
||||
|
||||
@@ -86,6 +86,7 @@ JavascriptEnvironment::~JavascriptEnvironment() {
|
||||
// Otherwise cppgc::internal::Sweeper::Start will try to request a task runner
|
||||
// from the NodePlatform with an already unregistered isolate.
|
||||
locker_.reset();
|
||||
DCHECK(!microtasks_runner_);
|
||||
isolate_holder_.reset();
|
||||
|
||||
platform_->UnregisterIsolate(isolate_);
|
||||
@@ -159,6 +160,7 @@ void JavascriptEnvironment::DestroyMicrotasksRunner() {
|
||||
gin_helper::CleanedUpAtExit::DoCleanup();
|
||||
}
|
||||
base::CurrentThread::Get()->RemoveTaskObserver(microtasks_runner_.get());
|
||||
microtasks_runner_.reset();
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
@@ -51,8 +51,7 @@ bool ElectronSerialDelegate::CanRequestPortPermission(
|
||||
auto* web_contents = content::WebContents::FromRenderFrameHost(frame);
|
||||
auto* permission_helper =
|
||||
WebContentsPermissionHelper::FromWebContents(web_contents);
|
||||
return permission_helper->CheckSerialAccessPermission(
|
||||
frame->GetLastCommittedOrigin());
|
||||
return permission_helper->CheckSerialAccessPermission(frame);
|
||||
}
|
||||
|
||||
bool ElectronSerialDelegate::HasPortPermission(
|
||||
|
||||
@@ -111,7 +111,7 @@ void AutofillPopupView::Show() {
|
||||
auto* host = popup_->frame_host_->GetRenderViewHost()->GetWidget();
|
||||
host->AddKeyPressEventCallback(keypress_callback_);
|
||||
|
||||
NotifyAccessibilityEventDeprecated(ax::mojom::Event::kMenuStart, true);
|
||||
GetViewAccessibility().NotifyEvent(ax::mojom::Event::kMenuStart, true);
|
||||
}
|
||||
|
||||
void AutofillPopupView::Hide() {
|
||||
@@ -122,7 +122,7 @@ void AutofillPopupView::Hide() {
|
||||
}
|
||||
|
||||
RemoveObserver();
|
||||
NotifyAccessibilityEventDeprecated(ax::mojom::Event::kMenuEnd, true);
|
||||
GetViewAccessibility().NotifyEvent(ax::mojom::Event::kMenuEnd, true);
|
||||
|
||||
if (GetWidget()) {
|
||||
GetWidget()->Close();
|
||||
@@ -165,7 +165,7 @@ void AutofillPopupView::OnSelectedRowChanged(
|
||||
int selected = current_row_selection.value_or(-1);
|
||||
if (selected == -1 || static_cast<size_t>(selected) >= children().size())
|
||||
return;
|
||||
children().at(selected)->NotifyAccessibilityEventDeprecated(
|
||||
children().at(selected)->GetViewAccessibility().NotifyEvent(
|
||||
ax::mojom::Event::kSelection, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,14 +228,14 @@ void WebContentsPermissionHelper::RequestPermission(
|
||||
}
|
||||
|
||||
bool WebContentsPermissionHelper::CheckPermission(
|
||||
content::RenderFrameHost* requesting_frame,
|
||||
blink::PermissionType permission,
|
||||
base::DictValue details) const {
|
||||
auto* rfh = web_contents_->GetPrimaryMainFrame();
|
||||
auto* permission_manager = static_cast<ElectronPermissionManager*>(
|
||||
web_contents_->GetBrowserContext()->GetPermissionControllerDelegate());
|
||||
auto origin = web_contents_->GetLastCommittedURL();
|
||||
return permission_manager->CheckPermissionWithDetails(permission, rfh, origin,
|
||||
std::move(details));
|
||||
auto origin = requesting_frame->GetLastCommittedOrigin().GetURL();
|
||||
return permission_manager->CheckPermissionWithDetails(
|
||||
permission, requesting_frame, origin, std::move(details));
|
||||
}
|
||||
|
||||
void WebContentsPermissionHelper::RequestFullscreenPermission(
|
||||
@@ -313,6 +313,7 @@ void WebContentsPermissionHelper::RequestOpenExternalPermission(
|
||||
}
|
||||
|
||||
bool WebContentsPermissionHelper::CheckMediaAccessPermission(
|
||||
content::RenderFrameHost* requesting_frame,
|
||||
const url::Origin& security_origin,
|
||||
blink::mojom::MediaStreamType type) const {
|
||||
base::DictValue details;
|
||||
@@ -321,14 +322,16 @@ bool WebContentsPermissionHelper::CheckMediaAccessPermission(
|
||||
auto blink_type = type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE
|
||||
? blink::PermissionType::AUDIO_CAPTURE
|
||||
: blink::PermissionType::VIDEO_CAPTURE;
|
||||
return CheckPermission(blink_type, std::move(details));
|
||||
return CheckPermission(requesting_frame, blink_type, std::move(details));
|
||||
}
|
||||
|
||||
bool WebContentsPermissionHelper::CheckSerialAccessPermission(
|
||||
const url::Origin& embedding_origin) const {
|
||||
content::RenderFrameHost* requesting_frame) const {
|
||||
base::DictValue details;
|
||||
details.Set("securityOrigin", embedding_origin.GetURL().spec());
|
||||
return CheckPermission(blink::PermissionType::SERIAL, std::move(details));
|
||||
details.Set("securityOrigin",
|
||||
requesting_frame->GetLastCommittedOrigin().GetURL().spec());
|
||||
return CheckPermission(requesting_frame, blink::PermissionType::SERIAL,
|
||||
std::move(details));
|
||||
}
|
||||
|
||||
WEB_CONTENTS_USER_DATA_KEY_IMPL(WebContentsPermissionHelper);
|
||||
|
||||
@@ -47,9 +47,11 @@ class WebContentsPermissionHelper
|
||||
const GURL& url);
|
||||
|
||||
// Synchronous Checks
|
||||
bool CheckMediaAccessPermission(const url::Origin& security_origin,
|
||||
bool CheckMediaAccessPermission(content::RenderFrameHost* requesting_frame,
|
||||
const url::Origin& security_origin,
|
||||
blink::mojom::MediaStreamType type) const;
|
||||
bool CheckSerialAccessPermission(const url::Origin& embedding_origin) const;
|
||||
bool CheckSerialAccessPermission(
|
||||
content::RenderFrameHost* requesting_frame) const;
|
||||
|
||||
private:
|
||||
explicit WebContentsPermissionHelper(content::WebContents* web_contents);
|
||||
@@ -61,7 +63,8 @@ class WebContentsPermissionHelper
|
||||
bool user_gesture = false,
|
||||
base::DictValue details = {});
|
||||
|
||||
bool CheckPermission(blink::PermissionType permission,
|
||||
bool CheckPermission(content::RenderFrameHost* requesting_frame,
|
||||
blink::PermissionType permission,
|
||||
base::DictValue details) const;
|
||||
|
||||
// TODO(clavin): refactor to use the WebContents provided by the
|
||||
|
||||
@@ -53,8 +53,7 @@ v8::MaybeLocal<v8::Value> CompileAndCall(
|
||||
context, v8::Null(isolate), arguments->size(), arguments->data());
|
||||
|
||||
// This will only be caught when something has gone terrible wrong as all
|
||||
// electron scripts are wrapped in a try {} catch {} by the esbuild bundler
|
||||
// (see build/esbuild/bundle.js applyWrappers).
|
||||
// electron scripts are wrapped in a try {} catch {} by webpack
|
||||
if (try_catch.HasCaught()) {
|
||||
std::string msg = "no error message";
|
||||
if (!try_catch.Message().IsEmpty()) {
|
||||
|
||||
@@ -1941,6 +1941,60 @@ describe('session module', () => {
|
||||
expect(handlerDetails!.isMainFrame).to.be.false();
|
||||
expect(handlerDetails!.embeddingOrigin).to.equal('file:///');
|
||||
});
|
||||
|
||||
it('provides iframe origin as requestingOrigin for media check from cross-origin subFrame', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
partition: 'very-temp-permission-handler-media'
|
||||
}
|
||||
});
|
||||
const ses = w.webContents.session;
|
||||
const iframeUrl = 'https://myfakesite/';
|
||||
let capturedOrigin: string | undefined;
|
||||
let capturedIsMainFrame: boolean | undefined;
|
||||
let capturedRequestingUrl: string | undefined;
|
||||
let capturedSecurityOrigin: string | undefined;
|
||||
|
||||
ses.protocol.interceptStringProtocol('https', (req, cb) => {
|
||||
cb('<html><body>iframe</body></html>');
|
||||
});
|
||||
|
||||
ses.setPermissionCheckHandler((wc, permission, requestingOrigin, details) => {
|
||||
if (permission === 'media') {
|
||||
capturedOrigin = requestingOrigin;
|
||||
capturedIsMainFrame = details.isMainFrame;
|
||||
capturedRequestingUrl = details.requestingUrl;
|
||||
capturedSecurityOrigin = (details as any).securityOrigin;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
try {
|
||||
await w.loadFile(path.join(fixtures, 'api', 'blank.html'));
|
||||
w.webContents.executeJavaScript(`
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.src = '${iframeUrl}';
|
||||
iframe.allow = 'camera; microphone';
|
||||
document.body.appendChild(iframe);
|
||||
null;
|
||||
`);
|
||||
const [,, frameProcessId, frameRoutingId] = await once(w.webContents, 'did-frame-finish-load');
|
||||
const frame = webFrameMain.fromId(frameProcessId, frameRoutingId)!;
|
||||
await frame.executeJavaScript(
|
||||
'navigator.mediaDevices.enumerateDevices().then(() => {}).catch(() => {});',
|
||||
true
|
||||
);
|
||||
|
||||
expect(capturedOrigin).to.equal(iframeUrl);
|
||||
expect(capturedIsMainFrame).to.be.false();
|
||||
expect(capturedRequestingUrl).to.equal(iframeUrl);
|
||||
expect(capturedSecurityOrigin).to.equal(iframeUrl);
|
||||
} finally {
|
||||
ses.protocol.uninterceptProtocol('https');
|
||||
ses.setPermissionCheckHandler(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('ses.isPersistent()', () => {
|
||||
|
||||
18
spec/fixtures/module/preload-require-identity.js
vendored
18
spec/fixtures/module/preload-require-identity.js
vendored
@@ -1,18 +0,0 @@
|
||||
/* eslint-disable no-self-compare, import/enforce-node-protocol-usage */
|
||||
// Regression test fixture for the esbuild __toCommonJS identity break.
|
||||
// Each pair must be the exact same object reference — see the
|
||||
// "module identity" describe block in spec/modules-spec.ts. The comparisons
|
||||
// are intentionally self-referential and intentionally use both the bare and
|
||||
// `node:`-prefixed module names, which is why no-self-compare and
|
||||
// import/enforce-node-protocol-usage are disabled for this file.
|
||||
const { ipcRenderer } = require('electron');
|
||||
|
||||
ipcRenderer.send('require-identity', {
|
||||
electron: require('electron') === require('electron'),
|
||||
electronCommon: require('electron/common') === require('electron'),
|
||||
events: require('events') === require('node:events'),
|
||||
timers: require('timers') === require('node:timers'),
|
||||
url: require('url') === require('node:url'),
|
||||
ipcRenderer: require('electron').ipcRenderer === require('electron').ipcRenderer,
|
||||
contextBridge: require('electron').contextBridge === require('electron').contextBridge
|
||||
});
|
||||
@@ -284,41 +284,6 @@ describe('modules support', () => {
|
||||
expect(result).to.equal('function');
|
||||
});
|
||||
});
|
||||
|
||||
// Regression test for the esbuild __toCommonJS identity break: esbuild's
|
||||
// runtime helper for `require()`-of-ESM allocates a fresh wrapper on
|
||||
// every call (see evanw/esbuild@f4ff26d3). The bundle driver patches it
|
||||
// with a WeakMap-memoized variant so module identity matches webpack's
|
||||
// __webpack_require__ cache semantics. Without the patch, every getter
|
||||
// on `require('electron')` that resolves to a non-default-exporting ESM
|
||||
// module would yield a fresh namespace on each access.
|
||||
describe('module identity', () => {
|
||||
it('returns the same object for repeated require("electron") accesses in the main process', () => {
|
||||
const electron = require('electron');
|
||||
for (const key of Object.keys(electron)) {
|
||||
// Touching `electron.net` before app ready throws — handled by the
|
||||
// sandboxed-preload identity test below; everything else must be
|
||||
// strictly equal across two reads.
|
||||
if (key === 'net') continue;
|
||||
expect((electron as any)[key]).to.equal((electron as any)[key],
|
||||
`require('electron').${key} must be identity-stable across reads`);
|
||||
}
|
||||
});
|
||||
|
||||
it('returns the same object for repeated require() in a sandboxed preload', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: { sandbox: true, contextIsolation: false, preload: path.join(fixtures, 'module', 'preload-require-identity.js') }
|
||||
});
|
||||
const result = once(w.webContents.ipc, 'require-identity');
|
||||
await w.loadURL('about:blank');
|
||||
const [, identities] = await result;
|
||||
for (const [name, same] of Object.entries(identities)) {
|
||||
expect(same).to.equal(true, `${name} must be identity-stable in sandboxed preload`);
|
||||
}
|
||||
w.destroy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('esm', () => {
|
||||
|
||||
@@ -2,8 +2,8 @@ const childProcess = require('node:child_process');
|
||||
const path = require('node:path');
|
||||
|
||||
const typeCheck = () => {
|
||||
const tsgoExec = path.resolve(require.resolve('@typescript/native-preview/package.json'), '..', 'bin', 'tsgo.js');
|
||||
const tscChild = childProcess.spawn(process.execPath, [tsgoExec, '--project', './ts-smoke/tsconfig.json'], {
|
||||
const tscExec = path.resolve(require.resolve('typescript'), '../../bin/tsc');
|
||||
const tscChild = childProcess.spawn(process.execPath, [tscExec, '--project', './ts-smoke/tsconfig.json'], {
|
||||
cwd: path.resolve(__dirname, '../')
|
||||
});
|
||||
tscChild.stdout.on('data', d => console.log(d.toString()));
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"compilerOptions": {
|
||||
"rootDir": "default_app",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler"
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": [
|
||||
"default_app",
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// rootDir intentionally omitted: some lib/ files pull type declarations
|
||||
// out of ../third_party/electron_node via the `@node/*` path alias (see
|
||||
// lib/node/asar-fs-wrapper.ts) and a rootDir of 'lib' rejects those
|
||||
// as outside-rootDir imports.
|
||||
"rootDir": "lib"
|
||||
},
|
||||
"include": [
|
||||
"lib",
|
||||
|
||||
@@ -10,12 +10,14 @@
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"strict": true,
|
||||
"baseUrl": ".",
|
||||
"allowJs": true,
|
||||
"noUnusedLocals": true,
|
||||
"outDir": "ts-gen",
|
||||
"typeRoots" : ["./node_modules/@types", "./spec/node_modules/@types"],
|
||||
"paths": {
|
||||
"@electron/internal/*": ["./lib/*"]
|
||||
"@electron/internal/*": ["lib/*"],
|
||||
"@node/*": ["../third_party/electron_node/*"]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
|
||||
36
typings/internal-ambient.d.ts
vendored
36
typings/internal-ambient.d.ts
vendored
@@ -1,36 +1,6 @@
|
||||
declare const BUILDFLAG: (flag: boolean) => boolean;
|
||||
/// <reference types="webpack/module" />
|
||||
|
||||
// esbuild build/esbuild/bundle.js rewrites calls to this identifier into
|
||||
// literal `require()` calls so that consumers can reach Node internal modules
|
||||
// (e.g. 'internal/modules/helpers') without the bundler trying to resolve
|
||||
// them. Overloads below pin the known internal-module IDs to narrow types;
|
||||
// the final catch-all signature keeps less-common paths usable at the cost
|
||||
// of no static type information.
|
||||
interface NodeInternalModules {
|
||||
'internal/modules/helpers': {
|
||||
makeRequireFunction: (mod: NodeModule) => NodeRequire;
|
||||
};
|
||||
'internal/modules/run_main': {
|
||||
runEntryPointWithESMLoader: (cb: (cascadedLoader: any) => any) => Promise<void>;
|
||||
};
|
||||
'internal/fs/utils': {
|
||||
getValidatedPath: (path: any, ...args: any[]) => string;
|
||||
getOptions: (options: any, defaultOptions?: any) => any;
|
||||
getDirent: (path: string, name: string | Buffer, type: number, callback?: Function) => any;
|
||||
};
|
||||
'internal/util': {
|
||||
assignFunctionName: (name: string | symbol, fn: Function) => Function;
|
||||
};
|
||||
'internal/validators': {
|
||||
validateBoolean: (value: unknown, name: string) => asserts value is boolean;
|
||||
validateFunction: (value: unknown, name: string) => void;
|
||||
};
|
||||
'internal/url': {
|
||||
URL: typeof URL;
|
||||
};
|
||||
}
|
||||
declare function __non_webpack_require__<K extends keyof NodeInternalModules>(id: K): NodeInternalModules[K];
|
||||
declare function __non_webpack_require__(id: string): unknown;
|
||||
declare const BUILDFLAG: (flag: boolean) => boolean;
|
||||
|
||||
declare namespace NodeJS {
|
||||
interface ModuleInternal extends NodeJS.Module {
|
||||
@@ -312,7 +282,7 @@ declare namespace NodeJS {
|
||||
}
|
||||
}
|
||||
|
||||
declare namespace NodeJS {
|
||||
declare module NodeJS {
|
||||
interface Global {
|
||||
require: NodeRequire;
|
||||
module: NodeModule;
|
||||
|
||||
Reference in New Issue
Block a user