mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
27 Commits
v22.0.0-al
...
v23.0.0-ni
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e02de74ff2 | ||
|
|
ebb866e63d | ||
|
|
ef00a2a1da | ||
|
|
6072c4c71b | ||
|
|
1fe21ff712 | ||
|
|
a072f06168 | ||
|
|
8bfbb251cc | ||
|
|
3f4c4a4470 | ||
|
|
5c784c2b1b | ||
|
|
e1494ddc47 | ||
|
|
e31c96a564 | ||
|
|
faafcc7f87 | ||
|
|
a6b6816bec | ||
|
|
f916ce2c49 | ||
|
|
6196393c94 | ||
|
|
ff0517be3e | ||
|
|
d8e037e426 | ||
|
|
3bd85c8dc2 | ||
|
|
e63d4a6321 | ||
|
|
256d4678bb | ||
|
|
2cda1443fc | ||
|
|
12eade752d | ||
|
|
c76a931e20 | ||
|
|
16f459228b | ||
|
|
94955a7999 | ||
|
|
29ca3d1467 | ||
|
|
3f598ef1ed |
14
.github/stale.yml
vendored
14
.github/stale.yml
vendored
@@ -1,14 +0,0 @@
|
||||
daysUntilStale: 90
|
||||
daysUntilClose: 30
|
||||
exemptLabels:
|
||||
- discussion
|
||||
- security 🔒
|
||||
- "enhancement :sparkles:"
|
||||
staleLabel: stale
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale. **If this issue is still affecting you, please leave any comment** (for example, "bump"), and we'll keep it open. If you have any new additional information—in particular, if this is still reproducible in the [latest version of Electron](https://www.electronjs.org/releases/stable) or in the [beta](https://www.electronjs.org/releases/beta)—please include it with your comment!
|
||||
closeComment: >
|
||||
This issue has been closed as it was considered stale, this issue will not be
|
||||
monitored. If this is a bug and you can reproduce this issue on a [supported
|
||||
version of Electron](https://www.electronjs.org/docs/latest/tutorial/electron-timelines#timeline) please open a new issue and ensure a repro is provided.
|
||||
|
||||
25
.github/workflows/stale.yml
vendored
Normal file
25
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: 'Close stale issues'
|
||||
on:
|
||||
schedule:
|
||||
# 1:30am every day
|
||||
- cron: '30 1 * * *'
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@3de2653986ebd134983c79fe2be5d45cc3d9f4e1
|
||||
with:
|
||||
days-before-stale: 90
|
||||
days-before-close: 30
|
||||
stale-issue-label: stale
|
||||
operations-per-run: 1750
|
||||
stale-issue-message: >
|
||||
This issue has been automatically marked as stale. **If this issue is still affecting you, please leave any comment** (for example, "bump"), and we'll keep it open. If you have any new additional information—in particular, if this is still reproducible in the [latest version of Electron](https://www.electronjs.org/releases/stable) or in the [beta](https://www.electronjs.org/releases/beta)—please include it with your comment!
|
||||
close-issue-message: >
|
||||
This issue has been closed due to inactivity, and will not be monitored. If this is a bug and you can reproduce this issue on a [supported version of Electron](https://www.electronjs.org/docs/latest/tutorial/electron-timelines#timeline) please open a new issue and include instructions for reproducing the issue.
|
||||
exempt-issue-labels: "discussion,security \U0001F512,enhancement :sparkles:"
|
||||
only-pr-labels: not-a-real-label
|
||||
@@ -1 +1 @@
|
||||
22.0.0-alpha.3
|
||||
23.0.0-nightly.20221010
|
||||
@@ -1437,13 +1437,16 @@ Returns `boolean` - Whether the window's document has been edited.
|
||||
|
||||
#### `win.blurWebView()`
|
||||
|
||||
#### `win.capturePage([rect])`
|
||||
#### `win.capturePage([rect, opts])`
|
||||
|
||||
* `rect` [Rectangle](structures/rectangle.md) (optional) - The bounds to capture
|
||||
* `opts` Object (optional)
|
||||
* `stayHidden` boolean (optional) - Keep the page hidden instead of visible. Default is `false`.
|
||||
* `stayAwake` boolean (optional) - Keep the system awake instead of allowing it to sleep. Default is `false`.
|
||||
|
||||
Returns `Promise<NativeImage>` - Resolves with a [NativeImage](native-image.md)
|
||||
|
||||
Captures a snapshot of the page within `rect`. Omitting `rect` will capture the whole visible page. If the page is not visible, `rect` may be empty.
|
||||
Captures a snapshot of the page within `rect`. Omitting `rect` will capture the whole visible page. If the page is not visible, `rect` may be empty. The page is considered visible when its browser window is hidden and the capturer count is non-zero. If you would like the page to stay hidden, you should ensure that `stayHidden` is set to true.
|
||||
|
||||
#### `win.loadURL(url[, options])`
|
||||
|
||||
|
||||
@@ -1338,20 +1338,25 @@ const requestId = webContents.findInPage('api')
|
||||
console.log(requestId)
|
||||
```
|
||||
|
||||
#### `contents.capturePage([rect])`
|
||||
#### `contents.capturePage([rect, opts])`
|
||||
|
||||
* `rect` [Rectangle](structures/rectangle.md) (optional) - The area of the page to be captured.
|
||||
* `opts` Object (optional)
|
||||
* `stayHidden` boolean (optional) - Keep the page hidden instead of visible. Default is `false`.
|
||||
* `stayAwake` boolean (optional) - Keep the system awake instead of allowing it to sleep. Default is `false`.
|
||||
|
||||
Returns `Promise<NativeImage>` - Resolves with a [NativeImage](native-image.md)
|
||||
|
||||
Captures a snapshot of the page within `rect`. Omitting `rect` will capture the whole visible page.
|
||||
The page is considered visible when its browser window is hidden and the capturer count is non-zero.
|
||||
If you would like the page to stay hidden, you should ensure that `stayHidden` is set to true.
|
||||
|
||||
#### `contents.isBeingCaptured()`
|
||||
|
||||
Returns `boolean` - Whether this page is being captured. It returns true when the capturer count
|
||||
is large then 0.
|
||||
|
||||
#### `contents.incrementCapturerCount([size, stayHidden, stayAwake])`
|
||||
#### `contents.incrementCapturerCount([size, stayHidden, stayAwake])` _Deprecated_
|
||||
|
||||
* `size` [Size](structures/size.md) (optional) - The preferred size for the capturer.
|
||||
* `stayHidden` boolean (optional) - Keep the page hidden instead of visible.
|
||||
@@ -1362,7 +1367,9 @@ hidden and the capturer count is non-zero. If you would like the page to stay hi
|
||||
|
||||
This also affects the Page Visibility API.
|
||||
|
||||
#### `contents.decrementCapturerCount([stayHidden, stayAwake])`
|
||||
**Deprecated:** This API's functionality is now handled automatically within `contents.capturePage()`. See [breaking changes](../breaking-changes.md).
|
||||
|
||||
#### `contents.decrementCapturerCount([stayHidden, stayAwake])` _Deprecated_
|
||||
|
||||
* `stayHidden` boolean (optional) - Keep the page in hidden state instead of visible.
|
||||
* `stayAwake` boolean (optional) - Keep the system awake instead of allowing it to sleep.
|
||||
@@ -1371,6 +1378,9 @@ Decrease the capturer count by one. The page will be set to hidden or occluded s
|
||||
browser window is hidden or occluded and the capturer count reaches zero. If you want to
|
||||
decrease the hidden capturer count instead you should set `stayHidden` to true.
|
||||
|
||||
**Deprecated:** This API's functionality is now handled automatically within `contents.capturePage()`.
|
||||
See [breaking changes](../breaking-changes.md).
|
||||
|
||||
#### `contents.getPrinters()` _Deprecated_
|
||||
|
||||
Get the system printer list.
|
||||
|
||||
@@ -38,14 +38,98 @@ win.webContents.on('input-event', (_, event) => {
|
||||
})
|
||||
```
|
||||
|
||||
### Removed: `webContents.incrementCapturerCount(stayHidden, stayAwake)`
|
||||
|
||||
The `webContents.incrementCapturerCount(stayHidden, stayAwake)` function has been removed.
|
||||
It is now automatically handled by `webContents.capturePage` when a page capture completes.
|
||||
|
||||
```js
|
||||
const w = new BrowserWindow({ show: false })
|
||||
|
||||
// Removed in Electron 23
|
||||
w.webContents.incrementCapturerCount()
|
||||
w.capturePage().then(image => {
|
||||
console.log(image.toDataURL())
|
||||
w.webContents.decrementCapturerCount()
|
||||
})
|
||||
|
||||
// Replace with
|
||||
w.capturePage().then(image => {
|
||||
console.log(image.toDataURL())
|
||||
})
|
||||
```
|
||||
|
||||
### Removed: `webContents.decrementCapturerCount(stayHidden, stayAwake)`
|
||||
|
||||
The `webContents.decrementCapturerCount(stayHidden, stayAwake)` function has been removed.
|
||||
It is now automatically handled by `webContents.capturePage` when a page capture completes.
|
||||
|
||||
```js
|
||||
const w = new BrowserWindow({ show: false })
|
||||
|
||||
// Removed in Electron 23
|
||||
w.webContents.incrementCapturerCount()
|
||||
w.capturePage().then(image => {
|
||||
console.log(image.toDataURL())
|
||||
w.webContents.decrementCapturerCount()
|
||||
})
|
||||
|
||||
// Replace with
|
||||
w.capturePage().then(image => {
|
||||
console.log(image.toDataURL())
|
||||
})
|
||||
```
|
||||
|
||||
## Planned Breaking API Changes (22.0)
|
||||
|
||||
### Deprecated: `webContents.incrementCapturerCount(stayHidden, stayAwake)`
|
||||
|
||||
`webContents.incrementCapturerCount(stayHidden, stayAwake)` has been deprecated.
|
||||
It is now automatically handled by `webContents.capturePage` when a page capture completes.
|
||||
|
||||
```js
|
||||
const w = new BrowserWindow({ show: false })
|
||||
|
||||
// Removed in Electron 23
|
||||
w.webContents.incrementCapturerCount()
|
||||
w.capturePage().then(image => {
|
||||
console.log(image.toDataURL())
|
||||
w.webContents.decrementCapturerCount()
|
||||
})
|
||||
|
||||
// Replace with
|
||||
w.capturePage().then(image => {
|
||||
console.log(image.toDataURL())
|
||||
})
|
||||
```
|
||||
|
||||
### Removed: `webContents.decrementCapturerCount(stayHidden, stayAwake)`
|
||||
|
||||
`webContents.decrementCapturerCount(stayHidden, stayAwake)` has been deprecated.
|
||||
It is now automatically handled by `webContents.capturePage` when a page capture completes.
|
||||
|
||||
```js
|
||||
const w = new BrowserWindow({ show: false })
|
||||
|
||||
// Removed in Electron 23
|
||||
w.webContents.incrementCapturerCount()
|
||||
w.capturePage().then(image => {
|
||||
console.log(image.toDataURL())
|
||||
w.webContents.decrementCapturerCount()
|
||||
})
|
||||
|
||||
// Replace with
|
||||
w.capturePage().then(image => {
|
||||
console.log(image.toDataURL())
|
||||
})
|
||||
```
|
||||
|
||||
### Removed: WebContents `new-window` event
|
||||
|
||||
The `new-window` event of WebContents has been removed. It is replaced by [`webContents.setWindowOpenHandler()`](api/web-contents.md#contentssetwindowopenhandlerhandler).
|
||||
|
||||
```js
|
||||
// Removed in Electron 21
|
||||
// Removed in Electron 22
|
||||
webContents.on('new-window', (event) => {
|
||||
event.preventDefault()
|
||||
})
|
||||
|
||||
@@ -116,10 +116,6 @@ $ git config --system core.longpaths true
|
||||
|
||||
This can happen during build, when Debugging Tools for Windows has been installed with Windows Driver Kit. Uninstall Windows Driver Kit and install Debugging Tools with steps described above.
|
||||
|
||||
### ImportError: No module named win32file
|
||||
|
||||
Make sure you have installed `pywin32` with `pip install pywin32`.
|
||||
|
||||
### Build Scripts Hang Until Keypress
|
||||
|
||||
This bug is a "feature" of Windows' command prompt. It happens when clicking inside the prompt window with
|
||||
|
||||
@@ -52,8 +52,9 @@ if (process.type === 'renderer') {
|
||||
}
|
||||
|
||||
const originalResolveFilename = Module._resolveFilename;
|
||||
const electronModuleNames = new Set(['electron', 'electron/main', 'electron/renderer', 'electron/common']);
|
||||
Module._resolveFilename = function (request: string, parent: NodeModule, isMain: boolean, options?: { paths: Array<string>}) {
|
||||
if (request === 'electron' || request.startsWith('electron/')) {
|
||||
if (electronModuleNames.has(request)) {
|
||||
return 'electron';
|
||||
} else {
|
||||
return originalResolveFilename(request, parent, isMain, options);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "22.0.0-alpha.3",
|
||||
"version": "23.0.0-nightly.20221010",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -120,4 +120,5 @@ fix_crash_loading_non-standard_schemes_in_iframes.patch
|
||||
disable_optimization_guide_for_preconnect_feature.patch
|
||||
fix_return_v8_value_from_localframe_requestexecutescript.patch
|
||||
create_browser_v8_snapshot_file_name_fuse.patch
|
||||
feat_ensure_mas_builds_of_the_same_application_can_use_safestorage.patch
|
||||
cherry-pick-c83640db21b5.patch
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samuel Attard <sattard@salesforce.com>
|
||||
Date: Thu, 29 Sep 2022 16:58:47 -0700
|
||||
Subject: feat: ensure mas builds of the same application can use safestorage
|
||||
|
||||
This change ensures that MAS builds of applications with an equivilant darwin build that share the same name do not fight over access to the same Safe Storage account.
|
||||
|
||||
Specifically this changes the account name for app "My App" from "My App" to "My App AppStore" if the app is using a MAS build of Electron.
|
||||
|
||||
We attempt to migrate the safe storage key from the old account, if that migration succeeds we delete the old key and move on.
|
||||
|
||||
Existing apps that aren't built for the app store should be unimpacted, there is one edge case where a user uses BOTH an AppStore and a darwin build of the same app only one will keep it's access to the safestorage key as during the migration we delete the old account. This is an acceptable edge case as no one should be actively using two versions of the same app.
|
||||
|
||||
diff --git a/components/os_crypt/keychain_password_mac.mm b/components/os_crypt/keychain_password_mac.mm
|
||||
index 214ae79b9a6de27b99ccfa9cf03327449fd79198..1b740e8dd19eeb34e68db30ba66ebadd1a132a39 100644
|
||||
--- a/components/os_crypt/keychain_password_mac.mm
|
||||
+++ b/components/os_crypt/keychain_password_mac.mm
|
||||
@@ -22,6 +22,12 @@
|
||||
using KeychainNameContainerType = const base::NoDestructor<std::string>;
|
||||
#endif
|
||||
|
||||
+#if defined(MAS_BUILD)
|
||||
+const char kAccountNameSuffix[] = " App Store Key";
|
||||
+#else
|
||||
+const char kAccountNameSuffix[] = " Key";
|
||||
+#endif
|
||||
+
|
||||
namespace {
|
||||
|
||||
// These two strings ARE indeed user facing. But they are used to access
|
||||
@@ -82,11 +88,18 @@
|
||||
std::string KeychainPassword::GetPassword() const {
|
||||
UInt32 password_length = 0;
|
||||
void* password_data = nullptr;
|
||||
+ KeychainPassword::KeychainNameType service_name = GetServiceName();
|
||||
+ KeychainPassword::KeychainNameType account_name = GetAccountName();
|
||||
+ const std::string account_name_suffix = kAccountNameSuffix;
|
||||
+ const std::string suffixed_account_name = account_name + account_name_suffix;
|
||||
+
|
||||
+ // We should check if the suffixed account exists first
|
||||
OSStatus error = keychain_.FindGenericPassword(
|
||||
- GetServiceName().size(), GetServiceName().c_str(),
|
||||
- GetAccountName().size(), GetAccountName().c_str(), &password_length,
|
||||
+ service_name.size(), service_name.c_str(),
|
||||
+ suffixed_account_name.size(), suffixed_account_name.c_str(), &password_length,
|
||||
&password_data, nullptr);
|
||||
|
||||
+ // If it exists we can return it immediately
|
||||
if (error == noErr) {
|
||||
std::string password =
|
||||
std::string(static_cast<char*>(password_data), password_length);
|
||||
@@ -94,9 +107,52 @@
|
||||
return password;
|
||||
}
|
||||
|
||||
+ // If the error was anything other than "it does not exist" we should error out here
|
||||
+ // This normally means the account exists but we were deniged access to it
|
||||
+ if (error != errSecItemNotFound) {
|
||||
+ OSSTATUS_LOG(ERROR, error) << "Keychain lookup for suffixed key failed";
|
||||
+ return std::string();
|
||||
+ }
|
||||
+
|
||||
+ // If the suffixed account didn't exist, we should check if the legacy non-suffixed account
|
||||
+ // exists. If it does we can use that key and migrate it to the new account
|
||||
+ base::ScopedCFTypeRef<SecKeychainItemRef> item_ref;
|
||||
+ error = keychain_.FindGenericPassword(
|
||||
+ service_name.size(), service_name.c_str(),
|
||||
+ account_name.size(), account_name.c_str(), &password_length,
|
||||
+ &password_data, item_ref.InitializeInto());
|
||||
+
|
||||
+ if (error == noErr) {
|
||||
+ std::string password =
|
||||
+ std::string(static_cast<char*>(password_data), password_length);
|
||||
+
|
||||
+ // If we found the legacy account name we should copy it over to
|
||||
+ // the new suffixed account
|
||||
+ error = keychain_.AddGenericPassword(
|
||||
+ service_name.size(), service_name.data(), suffixed_account_name.size(),
|
||||
+ suffixed_account_name.data(), password.size(), password_data, NULL);
|
||||
+
|
||||
+ if (error == noErr) {
|
||||
+ // If we successfully made the suffixed account we can delete the old
|
||||
+ // account to ensure new apps don't try to use it and run into IAM
|
||||
+ // issues
|
||||
+ error = keychain_.ItemDelete(item_ref.get());
|
||||
+ if (error != noErr) {
|
||||
+ OSSTATUS_DLOG(ERROR, error) << "Keychain delete for legacy key failed";
|
||||
+ }
|
||||
+ } else {
|
||||
+ OSSTATUS_DLOG(ERROR, error) << "Keychain add for suffixed key failed";
|
||||
+ }
|
||||
+
|
||||
+ keychain_.ItemFreeContent(password_data);
|
||||
+ return password;
|
||||
+ }
|
||||
+
|
||||
+ // If the legacy account name was not found, make a new account in the
|
||||
+ // with the suffixed name
|
||||
if (error == errSecItemNotFound) {
|
||||
std::string password = AddRandomPasswordToKeychain(
|
||||
- keychain_, GetServiceName(), GetAccountName());
|
||||
+ keychain_, service_name, suffixed_account_name);
|
||||
return password;
|
||||
}
|
||||
|
||||
@@ -51,3 +51,4 @@ fixup_for_error_declaration_shadows_a_local_variable.patch
|
||||
fixup_for_wc_98-compat-extra-semi.patch
|
||||
drop_deserializerequest_move_constructor_for_c_20_compat.patch
|
||||
fix_parallel_test-v8-stats.patch
|
||||
fix_expose_the_built-in_electron_module_via_the_esm_loader.patch
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samuel Attard <sattard@salesforce.com>
|
||||
Date: Thu, 6 Oct 2022 04:09:16 -0700
|
||||
Subject: fix: expose the built-in electron module via the ESM loader
|
||||
|
||||
This allows usage of `import { app } from 'electron'` and `import('electron')` natively in the browser + non-sandboxed renderer
|
||||
|
||||
diff --git a/lib/internal/modules/esm/get_format.js b/lib/internal/modules/esm/get_format.js
|
||||
index 5ae0e17dcfb5e24a1a117c33c4d42891686e693f..619fe6cef3b02eb575410225f41d3e7d51f37b93 100644
|
||||
--- a/lib/internal/modules/esm/get_format.js
|
||||
+++ b/lib/internal/modules/esm/get_format.js
|
||||
@@ -31,6 +31,7 @@ const protocolHandlers = ObjectAssign(ObjectCreate(null), {
|
||||
'http:': getHttpProtocolModuleFormat,
|
||||
'https:': getHttpProtocolModuleFormat,
|
||||
'node:'() { return 'builtin'; },
|
||||
+ 'electron:'() { return 'commonjs'; },
|
||||
});
|
||||
|
||||
function getDataProtocolModuleFormat(parsed) {
|
||||
diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js
|
||||
index fc5dcd6863dc358102a74bd2cc723d54436fae64..97eea17d815967671a2a0fc2a9c95a9bb85947ac 100644
|
||||
--- a/lib/internal/modules/esm/resolve.js
|
||||
+++ b/lib/internal/modules/esm/resolve.js
|
||||
@@ -883,6 +883,8 @@ function parsePackageName(specifier, base) {
|
||||
return { packageName, packageSubpath, isScoped };
|
||||
}
|
||||
|
||||
+const electronSpecifiers = new SafeSet(['electron', 'electron/main', 'electron/common', 'electron/renderer']);
|
||||
+
|
||||
/**
|
||||
* @param {string} specifier
|
||||
* @param {string | URL | undefined} base
|
||||
@@ -895,6 +897,10 @@ function packageResolve(specifier, base, conditions) {
|
||||
return new URL('node:' + specifier);
|
||||
}
|
||||
|
||||
+ if (electronSpecifiers.has(specifier)) {
|
||||
+ return new URL('electron:electron');
|
||||
+ }
|
||||
+
|
||||
const { packageName, packageSubpath, isScoped } =
|
||||
parsePackageName(specifier, base);
|
||||
|
||||
@@ -1095,7 +1101,7 @@ function checkIfDisallowedImport(specifier, parsed, parsedParentURL) {
|
||||
|
||||
function throwIfUnsupportedURLProtocol(url) {
|
||||
if (url.protocol !== 'file:' && url.protocol !== 'data:' &&
|
||||
- url.protocol !== 'node:') {
|
||||
+ url.protocol !== 'node:' && url.protocol !== 'electron:') {
|
||||
throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(url);
|
||||
}
|
||||
}
|
||||
diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js
|
||||
index 8fb3c96f8dc4c535c3898755f7846ae24d9867c4..bda3ca0797e8f1b1d69295c2276c0f841cae99f2 100644
|
||||
--- a/lib/internal/modules/esm/translators.js
|
||||
+++ b/lib/internal/modules/esm/translators.js
|
||||
@@ -155,7 +155,7 @@ translators.set('commonjs', async function commonjsStrategy(url, source,
|
||||
|
||||
if (!cjsParse) await initCJSParse();
|
||||
const { module, exportNames } = cjsPreparseModuleExports(filename);
|
||||
- const namesWithDefault = exportNames.has('default') ?
|
||||
+ const namesWithDefault = filename === 'electron' ? ['default', ...Object.keys(module.exports)] : exportNames.has('default') ?
|
||||
[...exportNames] : ['default', ...exportNames];
|
||||
|
||||
return new ModuleWrap(url, undefined, namesWithDefault, function() {
|
||||
@@ -174,7 +174,7 @@ translators.set('commonjs', async function commonjsStrategy(url, source,
|
||||
}
|
||||
}
|
||||
|
||||
- for (const exportName of exportNames) {
|
||||
+ for (const exportName of namesWithDefault) {
|
||||
if (!ObjectPrototypeHasOwnProperty(exports, exportName) ||
|
||||
exportName === 'default')
|
||||
continue;
|
||||
diff --git a/lib/internal/url.js b/lib/internal/url.js
|
||||
index 22bff28595f6f5b109ae47c79aa1f5ac463ec6c3..d4eb2e044cc1152f48c92d0503f9216e3aad5f4b 100644
|
||||
--- a/lib/internal/url.js
|
||||
+++ b/lib/internal/url.js
|
||||
@@ -1432,6 +1432,8 @@ function fileURLToPath(path) {
|
||||
path = new URL(path);
|
||||
else if (!isURLInstance(path))
|
||||
throw new ERR_INVALID_ARG_TYPE('path', ['string', 'URL'], path);
|
||||
+ if (path.protocol === 'electron:')
|
||||
+ return 'electron';
|
||||
if (path.protocol !== 'file:')
|
||||
throw new ERR_INVALID_URL_SCHEME('file');
|
||||
return isWindows ? getPathFromURLWin32(path) : getPathFromURLPosix(path);
|
||||
@@ -181,6 +181,7 @@ const LINTERS = [{
|
||||
const patchesConfig = path.resolve(patchesDir, 'config.json');
|
||||
// If the config does not exist, that's a problem
|
||||
if (!fs.existsSync(patchesConfig)) {
|
||||
console.error(`Patches config file: "${patchesConfig}" does not exist`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -188,34 +189,48 @@ const LINTERS = [{
|
||||
for (const key of Object.keys(config)) {
|
||||
// The directory the config points to should exist
|
||||
const targetPatchesDir = path.resolve(__dirname, '../../..', key);
|
||||
if (!fs.existsSync(targetPatchesDir)) throw new Error(`target patch directory: "${targetPatchesDir}" does not exist`);
|
||||
if (!fs.existsSync(targetPatchesDir)) {
|
||||
console.error(`target patch directory: "${targetPatchesDir}" does not exist`);
|
||||
process.exit(1);
|
||||
}
|
||||
// We need a .patches file
|
||||
const dotPatchesPath = path.resolve(targetPatchesDir, '.patches');
|
||||
if (!fs.existsSync(dotPatchesPath)) throw new Error(`.patches file: "${dotPatchesPath}" does not exist`);
|
||||
if (!fs.existsSync(dotPatchesPath)) {
|
||||
console.error(`.patches file: "${dotPatchesPath}" does not exist`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Read the patch list
|
||||
const patchFileList = fs.readFileSync(dotPatchesPath, 'utf8').trim().split('\n');
|
||||
const patchFileSet = new Set(patchFileList);
|
||||
patchFileList.reduce((seen, file) => {
|
||||
if (seen.has(file)) {
|
||||
throw new Error(`'${file}' is listed in ${dotPatchesPath} more than once`);
|
||||
console.error(`'${file}' is listed in ${dotPatchesPath} more than once`);
|
||||
process.exit(1);
|
||||
}
|
||||
return seen.add(file);
|
||||
}, new Set());
|
||||
if (patchFileList.length !== patchFileSet.size) throw new Error('each patch file should only be in the .patches file once');
|
||||
|
||||
if (patchFileList.length !== patchFileSet.size) {
|
||||
console.error('Each patch file should only be in the .patches file once');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
for (const file of fs.readdirSync(targetPatchesDir)) {
|
||||
// Ignore the .patches file and READMEs
|
||||
if (file === '.patches' || file === 'README.md') continue;
|
||||
|
||||
if (!patchFileSet.has(file)) {
|
||||
throw new Error(`Expected the .patches file at "${dotPatchesPath}" to contain a patch file ("${file}") present in the directory but it did not`);
|
||||
console.error(`Expected the .patches file at "${dotPatchesPath}" to contain a patch file ("${file}") present in the directory but it did not`);
|
||||
process.exit(1);
|
||||
}
|
||||
patchFileSet.delete(file);
|
||||
}
|
||||
|
||||
// If anything is left in this set, it means it did not exist on disk
|
||||
if (patchFileSet.size > 0) {
|
||||
throw new Error(`Expected all the patch files listed in the .patches file at "${dotPatchesPath}" to exist but some did not:\n${JSON.stringify([...patchFileSet.values()], null, 2)}`);
|
||||
console.error(`Expected all the patch files listed in the .patches file at "${dotPatchesPath}" to exist but some did not:\n${JSON.stringify([...patchFileSet.values()], null, 2)}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -317,9 +317,6 @@ absl::optional<int> ElectronMainDelegate::BasicStartupComplete() {
|
||||
::switches::kDisableGpuMemoryBufferCompositorResources);
|
||||
#endif
|
||||
|
||||
content_client_ = std::make_unique<ElectronContentClient>();
|
||||
SetContentClient(content_client_.get());
|
||||
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
@@ -440,6 +437,11 @@ base::StringPiece ElectronMainDelegate::GetBrowserV8SnapshotFilename() {
|
||||
return ContentMainDelegate::GetBrowserV8SnapshotFilename();
|
||||
}
|
||||
|
||||
content::ContentClient* ElectronMainDelegate::CreateContentClient() {
|
||||
content_client_ = std::make_unique<ElectronContentClient>();
|
||||
return content_client_.get();
|
||||
}
|
||||
|
||||
content::ContentBrowserClient*
|
||||
ElectronMainDelegate::CreateContentBrowserClient() {
|
||||
browser_client_ = std::make_unique<ElectronBrowserClient>();
|
||||
|
||||
@@ -38,6 +38,7 @@ class ElectronMainDelegate : public content::ContentMainDelegate {
|
||||
void PreSandboxStartup() override;
|
||||
void SandboxInitialized(const std::string& process_type) override;
|
||||
absl::optional<int> PreBrowserMain() override;
|
||||
content::ContentClient* CreateContentClient() override;
|
||||
content::ContentBrowserClient* CreateContentBrowserClient() override;
|
||||
content::ContentGpuClient* CreateContentGpuClient() override;
|
||||
content::ContentRendererClient* CreateContentRendererClient() override;
|
||||
|
||||
@@ -386,11 +386,13 @@ base::IDMap<WebContents*>& GetAllWebContents() {
|
||||
return *s_all_web_contents;
|
||||
}
|
||||
|
||||
// Called when CapturePage is done.
|
||||
void OnCapturePageDone(gin_helper::Promise<gfx::Image> promise,
|
||||
base::ScopedClosureRunner capture_handle,
|
||||
const SkBitmap& bitmap) {
|
||||
// Hack to enable transparency in captured image
|
||||
promise.Resolve(gfx::Image::CreateFrom1xBitmap(bitmap));
|
||||
|
||||
capture_handle.RunAndReset();
|
||||
}
|
||||
|
||||
absl::optional<base::TimeDelta> GetCursorBlinkInterval() {
|
||||
@@ -3174,13 +3176,22 @@ void WebContents::StartDrag(const gin_helper::Dictionary& item,
|
||||
}
|
||||
|
||||
v8::Local<v8::Promise> WebContents::CapturePage(gin::Arguments* args) {
|
||||
gfx::Rect rect;
|
||||
gin_helper::Promise<gfx::Image> promise(args->isolate());
|
||||
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||
|
||||
// get rect arguments if they exist
|
||||
gfx::Rect rect;
|
||||
args->GetNext(&rect);
|
||||
|
||||
bool stay_hidden = false;
|
||||
bool stay_awake = false;
|
||||
if (args && args->Length() == 2) {
|
||||
gin_helper::Dictionary options;
|
||||
if (args->GetNext(&options)) {
|
||||
options.Get("stayHidden", &stay_hidden);
|
||||
options.Get("stayAwake", &stay_awake);
|
||||
}
|
||||
}
|
||||
|
||||
auto* const view = web_contents()->GetRenderWidgetHostView();
|
||||
if (!view) {
|
||||
promise.Resolve(gfx::Image());
|
||||
@@ -3199,6 +3210,9 @@ v8::Local<v8::Promise> WebContents::CapturePage(gin::Arguments* args) {
|
||||
}
|
||||
#endif // BUILDFLAG(IS_MAC)
|
||||
|
||||
auto capture_handle = web_contents()->IncrementCapturerCount(
|
||||
rect.size(), stay_hidden, stay_awake);
|
||||
|
||||
// Capture full page if user doesn't specify a |rect|.
|
||||
const gfx::Size view_size =
|
||||
rect.IsEmpty() ? view->GetViewBounds().size() : rect.size();
|
||||
@@ -3215,11 +3229,18 @@ v8::Local<v8::Promise> WebContents::CapturePage(gin::Arguments* args) {
|
||||
bitmap_size = gfx::ScaleToCeiledSize(view_size, scale);
|
||||
|
||||
view->CopyFromSurface(gfx::Rect(rect.origin(), view_size), bitmap_size,
|
||||
base::BindOnce(&OnCapturePageDone, std::move(promise)));
|
||||
base::BindOnce(&OnCapturePageDone, std::move(promise),
|
||||
std::move(capture_handle)));
|
||||
return handle;
|
||||
}
|
||||
|
||||
// TODO(codebytere): remove in Electron v23.
|
||||
void WebContents::IncrementCapturerCount(gin::Arguments* args) {
|
||||
EmitWarning(node::Environment::GetCurrent(args->isolate()),
|
||||
"webContents.incrementCapturerCount() is deprecated and will be "
|
||||
"removed in v23",
|
||||
"electron");
|
||||
|
||||
gfx::Size size;
|
||||
bool stay_hidden = false;
|
||||
bool stay_awake = false;
|
||||
@@ -3236,7 +3257,13 @@ void WebContents::IncrementCapturerCount(gin::Arguments* args) {
|
||||
.Release();
|
||||
}
|
||||
|
||||
// TODO(codebytere): remove in Electron v23.
|
||||
void WebContents::DecrementCapturerCount(gin::Arguments* args) {
|
||||
EmitWarning(node::Environment::GetCurrent(args->isolate()),
|
||||
"webContents.decrementCapturerCount() is deprecated and will be "
|
||||
"removed in v23",
|
||||
"electron");
|
||||
|
||||
bool stay_hidden = false;
|
||||
bool stay_awake = false;
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 22,0,0,3
|
||||
PRODUCTVERSION 22,0,0,3
|
||||
FILEVERSION 23,0,0,20221010
|
||||
PRODUCTVERSION 23,0,0,20221010
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -68,12 +68,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Electron"
|
||||
VALUE "FileVersion", "22.0.0"
|
||||
VALUE "FileVersion", "23.0.0"
|
||||
VALUE "InternalName", "electron.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
||||
VALUE "OriginalFilename", "electron.exe"
|
||||
VALUE "ProductName", "Electron"
|
||||
VALUE "ProductVersion", "22.0.0"
|
||||
VALUE "ProductVersion", "23.0.0"
|
||||
VALUE "SquirrelAwareVersion", "1"
|
||||
END
|
||||
END
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/cxx17_backports.h"
|
||||
#include "base/feature_list.h"
|
||||
#include "base/i18n/rtl.h"
|
||||
#include "components/autofill/core/common/autofill_features.h"
|
||||
|
||||
@@ -1737,6 +1737,36 @@ describe('BrowserWindow module', () => {
|
||||
expect(image.isEmpty()).to.equal(true);
|
||||
});
|
||||
|
||||
ifit(process.platform === 'darwin')('honors the stayHidden argument', async () => {
|
||||
const w = new BrowserWindow({
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
});
|
||||
|
||||
w.loadFile(path.join(fixtures, 'pages', 'visibilitychange.html'));
|
||||
|
||||
{
|
||||
const [, visibilityState, hidden] = await emittedOnce(ipcMain, 'pong');
|
||||
expect(visibilityState).to.equal('visible');
|
||||
expect(hidden).to.be.false('hidden');
|
||||
}
|
||||
|
||||
w.hide();
|
||||
|
||||
{
|
||||
const [, visibilityState, hidden] = await emittedOnce(ipcMain, 'pong');
|
||||
expect(visibilityState).to.equal('hidden');
|
||||
expect(hidden).to.be.true('hidden');
|
||||
}
|
||||
|
||||
await w.capturePage({ x: 0, y: 0, width: 0, height: 0 }, { stayHidden: true });
|
||||
|
||||
const visible = await w.webContents.executeJavaScript('document.visibilityState');
|
||||
expect(visible).to.equal('hidden');
|
||||
});
|
||||
|
||||
it('resolves after the window is hidden', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
w.loadFile(path.join(fixtures, 'pages', 'a.html'));
|
||||
|
||||
@@ -174,4 +174,16 @@ describe('modules support', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('esm', () => {
|
||||
it('can load the built-in "electron" module via ESM import', async () => {
|
||||
await expect(import('electron')).to.eventually.be.ok();
|
||||
});
|
||||
|
||||
it('the built-in "electron" module loaded via ESM import has the same exports as the CJS module', async () => {
|
||||
const esmElectron = await import('electron');
|
||||
const cjsElectron = require('electron');
|
||||
expect(Object.keys(esmElectron)).to.deep.equal(Object.keys(cjsElectron));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user