Compare commits

...

11 Commits

Author SHA1 Message Date
David Sanders
650ef9a51c build: update @electron/lint-roller to 1.9.0 (#39812) 2023-09-12 12:38:31 +02:00
Charles Kerr
a1c44a18e2 fix: NodeService order-of-destruction issue (#39783)
* refactor: make ElectronRendererClient::node_bindings_ a const ptr

refactor: make ElectronRendererClient::electron_bindings_ a const ptr

* fix: order-of-destruction bug in NodeService

js_env_ depends on the uv_loop from node_bindings_, but is destroyed after node_bindings_

* chore: revert unintentional commit
2023-09-12 12:27:14 +02:00
Shelley Vohr
ec9c8476fe fix: keyCodes being incorrectly converted in webContents.sendInputEvent() (#39776)
fix: sendInputEvent keyCodes being incorrectly converted
2023-09-12 11:28:45 +02:00
Samuel Attard
470a14d8d4 docs: add missing macOS tag 2023-09-11 14:07:25 -07:00
Samuel Attard
5bff0fe342 feat: add new ElectronSquirrelPreventDowngrades flag (#38625)
* sketch

* feat: add new ElectronSquirrelPreventDowngrades flag

* test: remove only

* chore: fix lint
2023-09-11 11:54:51 -07:00
Samuel Attard
16aec702b4 fix: ensure app load is limited to real asar files when appropriate (#39788) 2023-09-11 11:51:14 -07:00
Samuel Attard
ac040bf734 build: update @electron/typescript-definitions to fix titlebaroverlay (#39799)
build: update @electron/typescript-definitions to fix titlebarstyle
2023-09-11 11:36:36 -07:00
David Sanders
aceb432f45 chore: remove deprecated systemPreferences APIs (#39696) 2023-09-11 11:34:13 -04:00
Calvin
d182794179 fix: frameless mica/acrylic windows (#39708)
* fix: backgroundMaterial works with frameless

* TODO: fix frameless mica/acrylic windows

* update caption color appropriately

* set background color properly

* refactor translucency method

* actualization
2023-09-11 14:51:54 +02:00
Shelley Vohr
ab185c058f fix: fullscreen crashing with no roundedCorners and no frame (#39747) 2023-09-11 09:38:10 +02:00
David Sanders
2324c4d8fd ci: ignore blocked label removed on closed issues (#39793) 2023-09-11 09:33:39 +02:00
42 changed files with 675 additions and 210 deletions

View File

@@ -10,7 +10,7 @@ permissions:
jobs:
issue-unlabeled-blocked:
name: All blocked/* labels removed
if: startsWith(github.event.label.name, 'blocked/')
if: startsWith(github.event.label.name, 'blocked/') && github.event.issue.state == 'open'
runs-on: ubuntu-latest
steps:
- name: Check for any blocked labels

View File

@@ -1647,7 +1647,7 @@ removed in future Electron releases.
* `options` Object
* `color` String (optional) _Windows_ - The CSS color of the Window Controls Overlay when enabled.
* `symbolColor` String (optional) _Windows_ - The CSS color of the symbols on the Window Controls Overlay when enabled.
* `height` Integer (optional) _Windows_ - The height of the title bar and Window Controls Overlay in pixels.
* `height` Integer (optional) _macOS_ _Windows_ - The height of the title bar and Window Controls Overlay in pixels.
On a Window with Window Controls Overlay already enabled, this method updates
the style of the title bar overlay.

View File

@@ -273,7 +273,6 @@ This API is only available on macOS 10.14 Mojave or newer.
* `window-frame` - Window frame.
* `window-text` - Text in windows.
* On **macOS**
* `alternate-selected-control-text` - The text on a selected surface in a list or table. _Deprecated_
* `control-background` - The background of a large interface element, such as a browser or table.
* `control` - The surface of a control.
* `control-text` -The text of a control that isnt disabled.
@@ -339,21 +338,6 @@ Returns `string` - Can be `dark`, `light` or `unknown`.
Gets the macOS appearance setting that is currently applied to your application,
maps to [NSApplication.effectiveAppearance](https://developer.apple.com/documentation/appkit/nsapplication/2967171-effectiveappearance?language=objc)
### `systemPreferences.getAppLevelAppearance()` _macOS_ _Deprecated_
Returns `string` | `null` - Can be `dark`, `light` or `unknown`.
Gets the macOS appearance setting that you have declared you want for
your application, maps to [NSApplication.appearance](https://developer.apple.com/documentation/appkit/nsapplication/2967170-appearance?language=objc).
You can use the `setAppLevelAppearance` API to set this value.
### `systemPreferences.setAppLevelAppearance(appearance)` _macOS_ _Deprecated_
* `appearance` string | null - Can be `dark` or `light`
Sets the appearance setting for your application, this should override the
system default and override the value of `getEffectiveAppearance`.
### `systemPreferences.canPromptTouchID()` _macOS_
Returns `boolean` - whether or not this device has the ability to use Touch ID.
@@ -417,16 +401,6 @@ Returns an object with system animation settings.
## Properties
### `systemPreferences.appLevelAppearance` _macOS_ _Deprecated_
A `string` property that can be `dark`, `light` or `unknown`. It determines the macOS appearance setting for
your application. This maps to values in: [NSApplication.appearance](https://developer.apple.com/documentation/appkit/nsapplication/2967170-appearance?language=objc). Setting this will override the
system default as well as the value of `getEffectiveAppearance`.
Possible values that can be set are `dark` and `light`, and possible return values are `dark`, `light`, and `unknown`.
This property is only available on macOS 10.14 Mojave or newer.
### `systemPreferences.effectiveAppearance` _macOS_ _Readonly_
A `string` property that can be `dark`, `light` or `unknown`.

View File

@@ -107,6 +107,41 @@ w.webContents.getPrintersAsync().then((printers) => {
})
```
### Removed: `systemPreferences.{get,set}AppLevelAppearance` and `systemPreferences.appLevelAppearance`
The `systemPreferences.getAppLevelAppearance` and `systemPreferences.setAppLevelAppearance`
methods have been removed, as well as the `systemPreferences.appLevelAppearance` property.
Use the `nativeTheme` module instead.
```js
// Removed
systemPreferences.getAppLevelAppearance()
// Replace with
nativeTheme.shouldUseDarkColors
// Removed
systemPreferences.appLevelAppearance
// Replace with
nativeTheme.shouldUseDarkColors
// Removed
systemPreferences.setAppLevelAppearance('dark')
// Replace with
nativeTheme.themeSource = 'dark'
```
### Removed: `alternate-selected-control-text` value for `systemPreferences.getColor`
The `alternate-selected-control-text` value for `systemPreferences.getColor`
has been removed. Use `selected-content-background` instead.
```js
// Removed
systemPreferences.getColor('alternate-selected-control-text')
// Replace with
systemPreferences.getColor('selected-content-background')
```
## Planned Breaking API Changes (26.0)
### Deprecated: `webContents.getPrinters`

View File

@@ -27,7 +27,7 @@ const cachedArchives = new Map<string, NodeJS.AsarArchive>();
const getOrCreateArchive = (archivePath: string) => {
const isCached = cachedArchives.has(archivePath);
if (isCached) {
return cachedArchives.get(archivePath);
return cachedArchives.get(archivePath)!;
}
try {
@@ -39,6 +39,8 @@ const getOrCreateArchive = (archivePath: string) => {
}
};
process._getOrCreateArchive = getOrCreateArchive;
const asarRe = /\.asar/i;
const { getValidatedPath } = __non_webpack_require__('internal/fs/utils');

View File

@@ -1,33 +1,5 @@
import * as deprecate from '@electron/internal/common/deprecate';
const { systemPreferences } = process._linkedBinding('electron_browser_system_preferences');
if ('getAppLevelAppearance' in systemPreferences) {
const nativeALAGetter = systemPreferences.getAppLevelAppearance;
const nativeALASetter = systemPreferences.setAppLevelAppearance;
const warnALA = deprecate.warnOnce('appLevelAppearance');
const warnALAGetter = deprecate.warnOnce('getAppLevelAppearance function');
const warnALASetter = deprecate.warnOnce('setAppLevelAppearance function');
Object.defineProperty(systemPreferences, 'appLevelAppearance', {
get: () => {
warnALA();
return nativeALAGetter.call(systemPreferences);
},
set: (appearance) => {
warnALA();
nativeALASetter.call(systemPreferences, appearance);
}
});
systemPreferences.getAppLevelAppearance = () => {
warnALAGetter();
return nativeALAGetter.call(systemPreferences);
};
systemPreferences.setAppLevelAppearance = (appearance) => {
warnALASetter();
nativeALASetter.call(systemPreferences, appearance);
};
}
if ('getEffectiveAppearance' in systemPreferences) {
const nativeEAGetter = systemPreferences.getEffectiveAppearance;
Object.defineProperty(systemPreferences, 'effectiveAppearance', {

View File

@@ -84,11 +84,20 @@ const v8Util = process._linkedBinding('electron_common_v8_util');
let packagePath = null;
let packageJson = null;
const searchPaths: string[] = v8Util.getHiddenValue(global, 'appSearchPaths');
const searchPathsOnlyLoadASAR: boolean = v8Util.getHiddenValue(global, 'appSearchPathsOnlyLoadASAR');
// Borrow the _getOrCreateArchive asar helper
const getOrCreateArchive = process._getOrCreateArchive;
delete process._getOrCreateArchive;
if (process.resourcesPath) {
for (packagePath of searchPaths) {
try {
packagePath = path.join(process.resourcesPath, packagePath);
if (searchPathsOnlyLoadASAR) {
if (!getOrCreateArchive?.(packagePath)) {
continue;
}
}
packageJson = Module._load(path.join(packagePath, 'package.json'));
break;
} catch {

View File

@@ -9,7 +9,7 @@
"@electron/docs-parser": "^1.1.1",
"@electron/fiddle-core": "^1.0.4",
"@electron/github-app-auth": "^2.0.0",
"@electron/lint-roller": "^1.8.0",
"@electron/lint-roller": "^1.9.0",
"@electron/typescript-definitions": "^8.14.5",
"@octokit/rest": "^19.0.7",
"@primer/octicons": "^10.0.0",

View File

@@ -130,5 +130,6 @@ fix_harden_blink_scriptstate_maybefrom.patch
chore_add_buildflag_guard_around_new_include.patch
fix_use_delegated_generic_capturer_when_available.patch
build_remove_ent_content_analysis_assert.patch
fix_activate_background_material_on_windows.patch
fix_move_autopipsettingshelper_behind_branding_buildflag.patch
revert_remove_the_allowaggressivethrottlingwithwebsocket_feature.patch

View File

@@ -0,0 +1,24 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: clavin <clavin@electronjs.org>
Date: Wed, 30 Aug 2023 18:15:36 -0700
Subject: fix: activate background material on windows
This patch adds a condition to the HWND message handler to allow windows
with translucent background materials to become activated.
This patch likely can't be upstreamed as-is, as Chromium doesn't have
this use case in mind currently.
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 13268bd89c710690eed5296f4b2157e9476f195e..37de479e95d49f4d2b1d8164c9e3f6a7bcd82612 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -1094,7 +1094,7 @@ void HWNDMessageHandler::FrameTypeChanged() {
void HWNDMessageHandler::PaintAsActiveChanged() {
if (!delegate_->HasNonClientView() || !delegate_->CanActivate() ||
- !delegate_->HasFrame() ||
+ (!delegate_->HasFrame() && !is_translucent_) ||
(delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN)) {
return;
}

View File

@@ -5,3 +5,4 @@ feat_add_new_squirrel_mac_bundle_installation_method_behind_flag.patch
refactor_use_posix_spawn_instead_of_nstask_so_we_can_disclaim_the.patch
fix_abort_installation_attempt_at_the_final_mile_if_the_app_is.patch
chore_disable_api_deprecation_warnings_in_nskeyedarchiver.patch
feat_add_ability_to_prevent_version_downgrades.patch

View File

@@ -0,0 +1,116 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samuel Attard <marshallofsound@electronjs.org>
Date: Tue, 6 Jun 2023 15:20:38 -0700
Subject: feat: add ability to prevent version downgrades
The ElectronSquirrelPreventDowngrades flag in your Info.plist can enable this feature, it must be set to true. This feature prevents a class of downgrade / freeze issues but has significant drawbacks in that it may break existing apps that delibrately downgrade users via the updater and requires that version strings are exactly A.B.C in order for the version comparison logic to work. Because of this this feature can not (and is not) enabled by default.
diff --git a/Squirrel/SQRLUpdater.h b/Squirrel/SQRLUpdater.h
index b3526b246b3729a7556ca0ec348fdc08825c89ed..87119399e8548e04134d1ee0cd18f85c2ad672c2 100644
--- a/Squirrel/SQRLUpdater.h
+++ b/Squirrel/SQRLUpdater.h
@@ -117,6 +117,10 @@ typedef enum {
// documentation for more information.
@property (atomic, strong) Class updateClass;
+// Publicly exposed for testing purposes, compares two version strings to see if it's
+// allowed. This assumes that the ElectronSquirrelPreventDowngrades flag is enabled.
++ (bool) isVersionAllowedForUpdate:(NSString*)targetVersion from:(NSString*)currentVersion;
+
// Initializes an updater that will send the given request to check for updates.
//
// This is the designated initializer for this class.
diff --git a/Squirrel/SQRLUpdater.m b/Squirrel/SQRLUpdater.m
index 4c703159a2bb0239b7d4e1793a985b5ec2edcfa9..592c7ea51515aab96934e0117df3c8065494fa09 100644
--- a/Squirrel/SQRLUpdater.m
+++ b/Squirrel/SQRLUpdater.m
@@ -42,6 +42,18 @@
// followed by a random string of characters.
static NSString * const SQRLUpdaterUniqueTemporaryDirectoryPrefix = @"update.";
+BOOL isVersionStandard(NSString* version) {
+ NSCharacterSet *alphaNums = [NSCharacterSet decimalDigitCharacterSet];
+
+ NSArray* versionParts = [version componentsSeparatedByString:@"."];
+ BOOL versionBad = [versionParts count] != 3;
+ for (NSString* part in versionParts) {
+ versionBad = versionBad || [alphaNums isSupersetOfSet:[NSCharacterSet characterSetWithCharactersInString:part]];
+ }
+
+ return !versionBad;
+}
+
@interface SQRLUpdater ()
@property (atomic, readwrite) SQRLUpdaterState state;
@@ -59,6 +71,8 @@ @interface SQRLUpdater ()
// Sends completed or error.
@property (nonatomic, strong, readonly) RACSignal *shipItLauncher;
++ (bool) isVersionAllowedForUpdate:(NSString*)targetVersion from:(NSString*)currentVersion;
+
// Parses an update model from downloaded data.
//
// data - JSON data representing an update manifest. This must not be nil.
@@ -372,6 +386,10 @@ - (RACDisposable *)startAutomaticChecksWithInterval:(NSTimeInterval)interval {
connect];
}
++ (bool) isVersionAllowedForUpdate:(NSString*)targetVersion from:(NSString*)currentVersion {
+ return [currentVersion compare:targetVersion options:NSNumericSearch] != NSOrderedDescending;
+}
+
- (RACSignal *)updateFromJSONData:(NSData *)data {
NSParameterAssert(data != nil);
@@ -711,6 +729,50 @@ - (RACSignal *)verifyAndPrepareUpdate:(SQRLUpdate *)update fromBundle:(NSBundle
return [[[[self.signature
verifyBundleAtURL:updateBundle.bundleURL]
then:^{
+ NSRunningApplication *currentApplication = NSRunningApplication.currentApplication;
+ NSBundle *appBundle = [NSBundle bundleWithURL:currentApplication.bundleURL];
+ BOOL preventDowngrades = [[appBundle objectForInfoDictionaryKey:@"ElectronSquirrelPreventDowngrades"] boolValue];
+
+ if (preventDowngrades == YES) {
+ NSString* currentVersion = [appBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
+ NSString* updateVersion = [updateBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
+ if (!currentVersion || !updateVersion) {
+ NSDictionary *errorInfo = @{
+ NSLocalizedDescriptionKey: NSLocalizedString(@"Cannot update to a bundle with a lower version number", nil),
+ NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"The application has ElectronSquirrelPreventDowngrades enabled and is missing a valid version string in either the current bundle or the target bundle", nil),
+ };
+ NSError *error = [NSError errorWithDomain:SQRLUpdaterErrorDomain code:SQRLUpdaterErrorMissingUpdateBundle userInfo:errorInfo];
+ return [RACSignal error:error];
+ }
+
+ if (!isVersionStandard(currentVersion)) {
+ NSDictionary *errorInfo = @{
+ NSLocalizedDescriptionKey: NSLocalizedString(@"Cannot update to a bundle with a lower version number", nil),
+ NSLocalizedRecoverySuggestionErrorKey: [NSString stringWithFormat:NSLocalizedString(@"The application has ElectronSquirrelPreventDowngrades enabled and is trying to update from '%@' which is not a valid version string", nil), currentVersion],
+ };
+ NSError *error = [NSError errorWithDomain:SQRLUpdaterErrorDomain code:SQRLUpdaterErrorMissingUpdateBundle userInfo:errorInfo];
+ return [RACSignal error:error];
+ }
+
+ if (!isVersionStandard(updateVersion)) {
+ NSDictionary *errorInfo = @{
+ NSLocalizedDescriptionKey: NSLocalizedString(@"Cannot update to a bundle with a lower version number", nil),
+ NSLocalizedRecoverySuggestionErrorKey: [NSString stringWithFormat:NSLocalizedString(@"The application has ElectronSquirrelPreventDowngrades enabled and is trying to update to '%@' which is not a valid version string", nil), updateVersion],
+ };
+ NSError *error = [NSError errorWithDomain:SQRLUpdaterErrorDomain code:SQRLUpdaterErrorMissingUpdateBundle userInfo:errorInfo];
+ return [RACSignal error:error];
+ }
+
+ if (![SQRLUpdater isVersionAllowedForUpdate:updateVersion from:currentVersion]) {
+ NSDictionary *errorInfo = @{
+ NSLocalizedDescriptionKey: NSLocalizedString(@"Cannot update to a bundle with a lower version number", nil),
+ NSLocalizedRecoverySuggestionErrorKey: [NSString stringWithFormat:NSLocalizedString(@"The application has ElectronSquirrelPreventDowngrades enabled and is trying to update from '%@' to '%@' which appears to be a downgrade", nil), currentVersion, updateVersion],
+ };
+ NSError *error = [NSError errorWithDomain:SQRLUpdaterErrorDomain code:SQRLUpdaterErrorMissingUpdateBundle userInfo:errorInfo];
+ return [RACSignal error:error];
+ }
+ }
+
SQRLDownloadedUpdate *downloadedUpdate = [[SQRLDownloadedUpdate alloc] initWithUpdate:update bundle:updateBundle];
return [RACSignal return:downloadedUpdate];
}]

View File

@@ -95,10 +95,6 @@ void AutoUpdater::OnWindowAllClosed() {
QuitAndInstall();
}
void AutoUpdater::SetFeedURL(gin::Arguments* args) {
auto_updater::AutoUpdater::SetFeedURL(args);
}
void AutoUpdater::QuitAndInstall() {
Emit("before-quit-for-update");
@@ -124,7 +120,11 @@ gin::ObjectTemplateBuilder AutoUpdater::GetObjectTemplateBuilder(
isolate)
.SetMethod("checkForUpdates", &auto_updater::AutoUpdater::CheckForUpdates)
.SetMethod("getFeedURL", &auto_updater::AutoUpdater::GetFeedURL)
.SetMethod("setFeedURL", &AutoUpdater::SetFeedURL)
.SetMethod("setFeedURL", &auto_updater::AutoUpdater::SetFeedURL)
#if DCHECK_IS_ON()
.SetMethod("isVersionAllowedForUpdate",
&auto_updater::AutoUpdater::IsVersionAllowedForUpdate)
#endif
.SetMethod("quitAndInstall", &AutoUpdater::QuitAndInstall);
}

View File

@@ -54,7 +54,6 @@ class AutoUpdater : public gin::Wrappable<AutoUpdater>,
private:
std::string GetFeedURL();
void SetFeedURL(gin::Arguments* args);
void QuitAndInstall();
};

View File

@@ -42,19 +42,11 @@ BrowserWindow::BrowserWindow(gin::Arguments* args,
auto web_preferences = gin_helper::Dictionary::CreateEmpty(isolate);
options.Get(options::kWebPreferences, &web_preferences);
bool transparent = false;
options.Get(options::kTransparent, &transparent);
std::string vibrancy_type;
#if BUILDFLAG(IS_MAC)
options.Get(options::kVibrancyType, &vibrancy_type);
#endif
// Copy the backgroundColor to webContents.
std::string color;
if (options.Get(options::kBackgroundColor, &color)) {
web_preferences.SetHidden(options::kBackgroundColor, color);
} else if (!vibrancy_type.empty() || transparent) {
} else if (window_->IsTranslucent()) {
// If the BrowserWindow is transparent or a vibrancy type has been set,
// also propagate transparency to the WebContents unless a separate
// backgroundColor has been set.

View File

@@ -89,10 +89,6 @@ gin::ObjectTemplateBuilder SystemPreferences::GetObjectTemplateBuilder(
&SystemPreferences::IsSwipeTrackingFromScrollEventsEnabled)
.SetMethod("getEffectiveAppearance",
&SystemPreferences::GetEffectiveAppearance)
.SetMethod("getAppLevelAppearance",
&SystemPreferences::GetAppLevelAppearance)
.SetMethod("setAppLevelAppearance",
&SystemPreferences::SetAppLevelAppearance)
.SetMethod("getSystemColor", &SystemPreferences::GetSystemColor)
.SetMethod("canPromptTouchID", &SystemPreferences::CanPromptTouchID)
.SetMethod("promptTouchID", &SystemPreferences::PromptTouchID)

View File

@@ -112,8 +112,6 @@ class SystemPreferences
// TODO(MarshallOfSound): Write tests for these methods once we
// are running tests on a Mojave machine
v8::Local<v8::Value> GetEffectiveAppearance(v8::Isolate* isolate);
v8::Local<v8::Value> GetAppLevelAppearance(v8::Isolate* isolate);
void SetAppLevelAppearance(gin::Arguments* args);
#endif
v8::Local<v8::Value> GetAnimationSettings(v8::Isolate* isolate);

View File

@@ -471,14 +471,7 @@ bool SystemPreferences::IsTrustedAccessibilityClient(bool prompt) {
std::string SystemPreferences::GetColor(gin_helper::ErrorThrower thrower,
const std::string& color) {
NSColor* sysColor = nil;
if (color == "alternate-selected-control-text") {
sysColor = [NSColor alternateSelectedControlTextColor];
EmitWarning(
node::Environment::GetCurrent(thrower.isolate()),
"'alternate-selected-control-text' is deprecated as an input to "
"getColor. Use 'selected-content-background' instead.",
"electron");
} else if (color == "control-background") {
if (color == "control-background") {
sysColor = [NSColor controlBackgroundColor];
} else if (color == "control") {
sysColor = [NSColor controlColor];
@@ -605,19 +598,4 @@ v8::Local<v8::Value> SystemPreferences::GetEffectiveAppearance(
isolate, [NSApplication sharedApplication].effectiveAppearance);
}
v8::Local<v8::Value> SystemPreferences::GetAppLevelAppearance(
v8::Isolate* isolate) {
return gin::ConvertToV8(isolate,
[NSApplication sharedApplication].appearance);
}
void SystemPreferences::SetAppLevelAppearance(gin::Arguments* args) {
NSAppearance* appearance;
if (args->GetNext(&appearance)) {
[[NSApplication sharedApplication] setAppearance:appearance];
} else {
args->ThrowError();
}
}
} // namespace electron::api

View File

@@ -26,6 +26,11 @@ void AutoUpdater::SetFeedURL(gin::Arguments* args) {}
void AutoUpdater::CheckForUpdates() {}
void AutoUpdater::QuitAndInstall() {}
bool AutoUpdater::IsVersionAllowedForUpdate(const std::string& current_version,
const std::string& target_version) {
return false;
}
#endif
} // namespace auto_updater

View File

@@ -70,6 +70,9 @@ class AutoUpdater {
static void CheckForUpdates();
static void QuitAndInstall();
static bool IsVersionAllowedForUpdate(const std::string& current_version,
const std::string& target_version);
private:
static Delegate* delegate_;
};

View File

@@ -179,4 +179,11 @@ void AutoUpdater::QuitAndInstall() {
}
}
bool AutoUpdater::IsVersionAllowedForUpdate(const std::string& current_version,
const std::string& target_version) {
return [SQRLUpdater
isVersionAllowedForUpdate:base::SysUTF8ToNSString(target_version)
from:base::SysUTF8ToNSString(current_version)];
}
} // namespace auto_updater

View File

@@ -13,6 +13,7 @@
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "content/public/browser/web_contents_user_data.h"
#include "include/core/SkColor.h"
#include "shell/browser/browser.h"
#include "shell/browser/native_window_features.h"
#include "shell/browser/ui/drag_util.h"
@@ -259,13 +260,15 @@ void NativeWindow::InitFromOptions(const gin_helper::Dictionary& options) {
SetBackgroundMaterial(material);
}
#endif
std::string color;
if (options.Get(options::kBackgroundColor, &color)) {
SetBackgroundColor(ParseCSSColor(color));
} else if (!transparent()) {
// For normal window, use white as default background.
SetBackgroundColor(SK_ColorWHITE);
SkColor background_color = SK_ColorWHITE;
if (std::string color; options.Get(options::kBackgroundColor, &color)) {
background_color = ParseCSSColor(color);
} else if (IsTranslucent()) {
background_color = SK_ColorTRANSPARENT;
}
SetBackgroundColor(background_color);
std::string title(Browser::Get()->GetName());
options.Get(options::kTitle, &title);
SetTitle(title);
@@ -472,9 +475,13 @@ bool NativeWindow::AddTabbedWindow(NativeWindow* window) {
return true; // for non-Mac platforms
}
void NativeWindow::SetVibrancy(const std::string& type) {}
void NativeWindow::SetVibrancy(const std::string& type) {
vibrancy_ = type;
}
void NativeWindow::SetBackgroundMaterial(const std::string& type) {}
void NativeWindow::SetBackgroundMaterial(const std::string& type) {
background_material_ = type;
}
void NativeWindow::SetTouchBar(
std::vector<gin_helper::PersistentDictionary> items) {}
@@ -798,6 +805,30 @@ void NativeWindow::HandlePendingFullscreenTransitions() {
// static
int32_t NativeWindow::next_id_ = 0;
bool NativeWindow::IsTranslucent() const {
// Transparent windows are translucent
if (transparent()) {
return true;
}
#if BUILDFLAG(IS_MAC)
// Windows with vibrancy set are translucent
if (!vibrancy().empty()) {
return true;
}
#endif
#if BUILDFLAG(IS_WIN)
// Windows with certain background materials may be translucent
const std::string& bg_material = background_material();
if (!bg_material.empty() && bg_material != "none") {
return true;
}
#endif
return false;
}
// static
void NativeWindowRelay::CreateForWebContents(
content::WebContents* web_contents,

View File

@@ -218,8 +218,12 @@ class NativeWindow : public base::SupportsUserData,
virtual void SetAutoHideCursor(bool auto_hide);
// Vibrancy API
const std::string& vibrancy() const { return vibrancy_; }
virtual void SetVibrancy(const std::string& type);
const std::string& background_material() const {
return background_material_;
}
virtual void SetBackgroundMaterial(const std::string& type);
// Traffic Light API
@@ -395,6 +399,8 @@ class NativeWindow : public base::SupportsUserData,
void AddDraggableRegionProvider(DraggableRegionProvider* provider);
void RemoveDraggableRegionProvider(DraggableRegionProvider* provider);
bool IsTranslucent() const;
protected:
friend class api::BrowserView;
@@ -492,6 +498,9 @@ class NativeWindow : public base::SupportsUserData,
// Accessible title.
std::u16string accessible_title_;
std::string vibrancy_;
std::string background_material_;
gfx::Rect overlay_rect_;
base::WeakPtrFactory<NativeWindow> weak_factory_{this};

View File

@@ -683,9 +683,6 @@ void NativeWindowMac::DetachChildren() {
}
void NativeWindowMac::SetFullScreen(bool fullscreen) {
if (!has_frame() && !HasStyleMask(NSWindowStyleMaskTitled))
return;
// [NSWindow -toggleFullScreen] is an asynchronous operation, which means
// that it's possible to call it while a fullscreen transition is currently
// in process. This can create weird behavior (incl. phantom windows),
@@ -718,7 +715,8 @@ void NativeWindowMac::SetFullScreen(bool fullscreen) {
? FullScreenTransitionState::kEntering
: FullScreenTransitionState::kExiting;
[window_ toggleFullScreenMode:nil];
if (![window_ toggleFullScreenMode:nil])
fullscreen_transition_state_ = FullScreenTransitionState::kNone;
}
bool NativeWindowMac::IsFullscreen() const {
@@ -1458,6 +1456,8 @@ void NativeWindowMac::UpdateWindowOriginalFrame() {
}
void NativeWindowMac::SetVibrancy(const std::string& type) {
NativeWindow::SetVibrancy(type);
NSVisualEffectView* vibrantView = [window_ vibrantView];
if (type.empty()) {

View File

@@ -284,12 +284,12 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
params.remove_standard_frame = !has_frame() || has_client_frame();
// If a client frame, we need to draw our own shadows.
if (transparent() || has_client_frame())
if (IsTranslucent() || has_client_frame())
params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
// The given window is most likely not rectangular since it uses
// transparency and has no standard frame, don't show a shadow for it.
if (transparent() && !has_frame())
// The given window is most likely not rectangular since it is translucent and
// has no standard frame, don't show a shadow for it.
if (IsTranslucent() && !has_frame())
params.shadow_type = views::Widget::InitParams::ShadowType::kNone;
bool focusable;
@@ -1457,6 +1457,8 @@ bool NativeWindowViews::IsMenuBarVisible() {
}
void NativeWindowViews::SetBackgroundMaterial(const std::string& material) {
NativeWindow::SetBackgroundMaterial(material);
#if BUILDFLAG(IS_WIN)
// DWMWA_USE_HOSTBACKDROPBRUSH is only supported on Windows 11 22H2 and up.
if (base::win::GetVersion() < base::win::Version::WIN11_22H2)
@@ -1468,6 +1470,20 @@ void NativeWindowViews::SetBackgroundMaterial(const std::string& material) {
&backdrop_type, sizeof(backdrop_type));
if (FAILED(result))
LOG(WARNING) << "Failed to set background material to " << material;
// For frameless windows with a background material set, we also need to
// remove the caption color so it doesn't render a caption bar (since the
// window is frameless)
COLORREF caption_color = DWMWA_COLOR_DEFAULT;
if (backdrop_type != DWMSBT_NONE && backdrop_type != DWMSBT_AUTO &&
!has_frame()) {
caption_color = DWMWA_COLOR_NONE;
}
result = DwmSetWindowAttribute(GetAcceleratedWidget(), DWMWA_CAPTION_COLOR,
&caption_color, sizeof(caption_color));
if (FAILED(result))
LOG(WARNING) << "Failed to set caption color to transparent";
#endif
}

View File

@@ -44,7 +44,7 @@ class ScopedDisableResize {
- (electron::NativeWindowMac*)shell;
- (id)accessibilityFocusedUIElement;
- (NSRect)originalContentRectForFrameRect:(NSRect)frameRect;
- (void)toggleFullScreenMode:(id)sender;
- (BOOL)toggleFullScreenMode:(id)sender;
- (NSImage*)_cornerMask;
@end

View File

@@ -334,7 +334,10 @@ void SwizzleSwipeWithEvent(NSView* view, SEL swiz_selector) {
}
}
- (void)toggleFullScreenMode:(id)sender {
- (BOOL)toggleFullScreenMode:(id)sender {
if (!shell_->has_frame() && !shell_->HasStyleMask(NSWindowStyleMaskTitled))
return NO;
bool is_simple_fs = shell_->IsSimpleFullScreen();
bool always_simple_fs = shell_->always_simple_fullscreen();
@@ -363,6 +366,8 @@ void SwizzleSwipeWithEvent(NSView* view, SEL swiz_selector) {
bool maximizable = shell_->IsMaximizable();
shell_->SetMaximizable(maximizable);
}
return YES;
}
- (void)performMiniaturize:(id)sender {

View File

@@ -10,6 +10,7 @@
#include "base/containers/fixed_flat_map.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversion_utils.h"
#include "base/strings/utf_string_conversions.h"
#include "gin/converter.h"
#include "gin/data_object_builder.h"
@@ -275,11 +276,18 @@ bool Converter<blink::WebKeyboardEvent>::FromV8(v8::Isolate* isolate,
if ((out->GetType() == blink::WebInputEvent::Type::kChar ||
out->GetType() == blink::WebInputEvent::Type::kRawKeyDown)) {
// If the keyCode is e.g. Space or Plus we want to use the character
// instead of the keyCode: ' ' instead of 'Space', '+' instead of 'Plus'.
std::string character_str;
if (str.size() > 1 && domKey.IsCharacter())
base::WriteUnicodeCharacter(domKey.ToCharacter(), &character_str);
// Make sure to not read beyond the buffer in case some bad code doesn't
// NULL-terminate it (this is called from plugins).
size_t text_length_cap = blink::WebKeyboardEvent::kTextLengthCap;
std::u16string text16 = base::UTF8ToUTF16(str);
std::u16string text16 = character_str.empty()
? base::UTF8ToUTF16(str)
: base::UTF8ToUTF16(character_str);
std::fill_n(out->text, text_length_cap, 0);
std::fill_n(out->unmodified_text, text_length_cap, 0);
for (size_t i = 0; i < std::min(text_length_cap - 1, text16.size()); ++i) {

View File

@@ -586,6 +586,13 @@ std::shared_ptr<node::Environment> NodeBindings::CreateEnvironment(
electron::fuses::IsOnlyLoadAppFromAsarEnabled()
? app_asar_search_paths
: search_paths));
context->Global()->SetPrivate(
context,
v8::Private::ForApi(
isolate, gin::ConvertToV8(isolate, "appSearchPathsOnlyLoadASAR")
.As<v8::String>()),
gin::ConvertToV8(isolate,
electron::fuses::IsOnlyLoadAppFromAsarEnabled()));
}
base::FilePath resources_path = GetResourcesPath();

View File

@@ -20,10 +20,10 @@ namespace electron {
NodeService::NodeService(
mojo::PendingReceiver<node::mojom::NodeService> receiver)
: node_bindings_(
NodeBindings::Create(NodeBindings::BrowserEnvironment::kUtility)),
electron_bindings_(
std::make_unique<ElectronBindings>(node_bindings_->uv_loop())) {
: node_bindings_{NodeBindings::Create(
NodeBindings::BrowserEnvironment::kUtility)},
electron_bindings_{
std::make_unique<ElectronBindings>(node_bindings_->uv_loop())} {
if (receiver.is_valid())
receiver_.Bind(std::move(receiver));
}

View File

@@ -37,10 +37,18 @@ class NodeService : public node::mojom::NodeService {
private:
bool node_env_stopped_ = false;
const std::unique_ptr<NodeBindings> node_bindings_;
// depends-on: node_bindings_'s uv_loop
const std::unique_ptr<ElectronBindings> electron_bindings_;
// depends-on: node_bindings_'s uv_loop
std::unique_ptr<JavascriptEnvironment> js_env_;
std::unique_ptr<NodeBindings> node_bindings_;
std::unique_ptr<ElectronBindings> electron_bindings_;
// depends-on: js_env_'s isolate
std::shared_ptr<node::Environment> node_env_;
mojo::Receiver<node::mojom::NodeService> receiver_{this};
};

View File

@@ -9,7 +9,7 @@ import * as psList from 'ps-list';
import { AddressInfo } from 'node:net';
import { ifdescribe, ifit } from './lib/spec-helpers';
import * as uuid from 'uuid';
import { systemPreferences } from 'electron';
import { autoUpdater, systemPreferences } from 'electron';
const features = process._linkedBinding('electron_common_features');
@@ -129,11 +129,13 @@ ifdescribe(process.platform === 'darwin' && !(process.env.CI && process.arch ===
const cachedZips: Record<string, string> = {};
const getOrCreateUpdateZipPath = async (version: string, fixture: string, mutateAppPostSign?: {
type Mutation = {
mutate: (appPath: string) => Promise<void>,
mutationKey: string,
}) => {
const key = `${version}-${fixture}-${mutateAppPostSign?.mutationKey || 'no-mutation'}`;
};
const getOrCreateUpdateZipPath = async (version: string, fixture: string, mutateAppPreSign?: Mutation, mutateAppPostSign?: Mutation) => {
const key = `${version}-${fixture}-${mutateAppPreSign?.mutationKey || 'no-pre-mutation'}-${mutateAppPostSign?.mutationKey || 'no-post-mutation'}`;
if (!cachedZips[key]) {
let updateZipPath: string;
await withTempDirectory(async (dir) => {
@@ -143,6 +145,12 @@ ifdescribe(process.platform === 'darwin' && !(process.env.CI && process.arch ===
appPJPath,
(await fs.readFile(appPJPath, 'utf8')).replace('1.0.0', version)
);
const infoPath = path.resolve(secondAppPath, 'Contents', 'Info.plist');
await fs.writeFile(
infoPath,
(await fs.readFile(infoPath, 'utf8')).replace(/(<key>CFBundleShortVersionString<\/key>\s+<string>)[^<]+/g, `$1${version}`)
);
await mutateAppPreSign?.mutate(secondAppPath);
await signApp(secondAppPath);
await mutateAppPostSign?.mutate(secondAppPath);
updateZipPath = path.resolve(dir, 'update.zip');
@@ -279,16 +287,20 @@ ifdescribe(process.platform === 'darwin' && !(process.env.CI && process.arch ===
nextVersion: string;
startFixture: string;
endFixture: string;
mutateAppPostSign?: {
mutate: (appPath: string) => Promise<void>,
mutationKey: string,
}
mutateAppPreSign?: Mutation;
mutateAppPostSign?: Mutation;
}, fn: (appPath: string, zipPath: string) => Promise<void>) => {
await withTempDirectory(async (dir) => {
const appPath = await copyApp(dir, opts.startFixture);
await opts.mutateAppPreSign?.mutate(appPath);
const infoPath = path.resolve(appPath, 'Contents', 'Info.plist');
await fs.writeFile(
infoPath,
(await fs.readFile(infoPath, 'utf8')).replace(/(<key>CFBundleShortVersionString<\/key>\s+<string>)[^<]+/g, '$11.0.0')
);
await signApp(appPath);
const updateZipPath = await getOrCreateUpdateZipPath(opts.nextVersion, opts.endFixture, opts.mutateAppPostSign);
const updateZipPath = await getOrCreateUpdateZipPath(opts.nextVersion, opts.endFixture, opts.mutateAppPreSign, opts.mutateAppPostSign);
await fn(appPath, updateZipPath);
});
@@ -335,6 +347,231 @@ ifdescribe(process.platform === 'darwin' && !(process.env.CI && process.arch ===
});
});
it('should hit the download endpoint when an update is available and update successfully when the zip is provided even after a different update was staged', async () => {
await withUpdatableApp({
nextVersion: '2.0.0',
startFixture: 'update-stack',
endFixture: 'update-stack'
}, async (appPath, updateZipPath2) => {
await withUpdatableApp({
nextVersion: '3.0.0',
startFixture: 'update-stack',
endFixture: 'update-stack'
}, async (_, updateZipPath3) => {
let updateCount = 0;
server.get('/update-file', (req, res) => {
res.download(updateCount > 1 ? updateZipPath3 : updateZipPath2);
});
server.get('/update-check', (req, res) => {
updateCount++;
res.json({
url: `http://localhost:${port}/update-file`,
name: 'My Release Name',
notes: 'Theses are some release notes innit',
pub_date: (new Date()).toString()
});
});
const relaunchPromise = new Promise<void>((resolve) => {
server.get('/update-check/updated/:version', (req, res) => {
res.status(204).send();
resolve();
});
});
const launchResult = await launchApp(appPath, [`http://localhost:${port}/update-check`]);
logOnError(launchResult, () => {
expect(launchResult).to.have.property('code', 0);
expect(launchResult.out).to.include('Update Downloaded');
expect(requests).to.have.lengthOf(4);
expect(requests[0]).to.have.property('url', '/update-check');
expect(requests[1]).to.have.property('url', '/update-file');
expect(requests[0].header('user-agent')).to.include('Electron/');
expect(requests[1].header('user-agent')).to.include('Electron/');
expect(requests[2]).to.have.property('url', '/update-check');
expect(requests[3]).to.have.property('url', '/update-file');
expect(requests[2].header('user-agent')).to.include('Electron/');
expect(requests[3].header('user-agent')).to.include('Electron/');
});
await relaunchPromise;
expect(requests).to.have.lengthOf(5);
expect(requests[4].url).to.equal('/update-check/updated/3.0.0');
expect(requests[4].header('user-agent')).to.include('Electron/');
});
});
});
it('should update to lower version numbers', async () => {
await withUpdatableApp({
nextVersion: '0.0.1',
startFixture: 'update',
endFixture: 'update'
}, async (appPath, updateZipPath) => {
server.get('/update-file', (req, res) => {
res.download(updateZipPath);
});
server.get('/update-check', (req, res) => {
res.json({
url: `http://localhost:${port}/update-file`,
name: 'My Release Name',
notes: 'Theses are some release notes innit',
pub_date: (new Date()).toString()
});
});
const relaunchPromise = new Promise<void>((resolve) => {
server.get('/update-check/updated/:version', (req, res) => {
res.status(204).send();
resolve();
});
});
const launchResult = await launchApp(appPath, [`http://localhost:${port}/update-check`]);
logOnError(launchResult, () => {
expect(launchResult).to.have.property('code', 0);
expect(launchResult.out).to.include('Update Downloaded');
expect(requests).to.have.lengthOf(2);
expect(requests[0]).to.have.property('url', '/update-check');
expect(requests[1]).to.have.property('url', '/update-file');
expect(requests[0].header('user-agent')).to.include('Electron/');
expect(requests[1].header('user-agent')).to.include('Electron/');
});
await relaunchPromise;
expect(requests).to.have.lengthOf(3);
expect(requests[2].url).to.equal('/update-check/updated/0.0.1');
expect(requests[2].header('user-agent')).to.include('Electron/');
});
});
describe('with ElectronSquirrelPreventDowngrades enabled', () => {
it('should not update to lower version numbers', async () => {
await withUpdatableApp({
nextVersion: '0.0.1',
startFixture: 'update',
endFixture: 'update',
mutateAppPreSign: {
mutationKey: 'prevent-downgrades',
mutate: async (appPath) => {
const infoPath = path.resolve(appPath, 'Contents', 'Info.plist');
await fs.writeFile(
infoPath,
(await fs.readFile(infoPath, 'utf8')).replace('<key>NSSupportsAutomaticGraphicsSwitching</key>', '<key>ElectronSquirrelPreventDowngrades</key><true/><key>NSSupportsAutomaticGraphicsSwitching</key>')
);
}
}
}, async (appPath, updateZipPath) => {
server.get('/update-file', (req, res) => {
res.download(updateZipPath);
});
server.get('/update-check', (req, res) => {
res.json({
url: `http://localhost:${port}/update-file`,
name: 'My Release Name',
notes: 'Theses are some release notes innit',
pub_date: (new Date()).toString()
});
});
const launchResult = await launchApp(appPath, [`http://localhost:${port}/update-check`]);
logOnError(launchResult, () => {
expect(launchResult).to.have.property('code', 1);
expect(launchResult.out).to.include('Cannot update to a bundle with a lower version number');
expect(requests).to.have.lengthOf(2);
expect(requests[0]).to.have.property('url', '/update-check');
expect(requests[1]).to.have.property('url', '/update-file');
expect(requests[0].header('user-agent')).to.include('Electron/');
expect(requests[1].header('user-agent')).to.include('Electron/');
});
});
});
it('should not update to version strings that are not simple Major.Minor.Patch', async () => {
await withUpdatableApp({
nextVersion: '2.0.0-bad',
startFixture: 'update',
endFixture: 'update',
mutateAppPreSign: {
mutationKey: 'prevent-downgrades',
mutate: async (appPath) => {
const infoPath = path.resolve(appPath, 'Contents', 'Info.plist');
await fs.writeFile(
infoPath,
(await fs.readFile(infoPath, 'utf8')).replace('<key>NSSupportsAutomaticGraphicsSwitching</key>', '<key>ElectronSquirrelPreventDowngrades</key><true/><key>NSSupportsAutomaticGraphicsSwitching</key>')
);
}
}
}, async (appPath, updateZipPath) => {
server.get('/update-file', (req, res) => {
res.download(updateZipPath);
});
server.get('/update-check', (req, res) => {
res.json({
url: `http://localhost:${port}/update-file`,
name: 'My Release Name',
notes: 'Theses are some release notes innit',
pub_date: (new Date()).toString()
});
});
const launchResult = await launchApp(appPath, [`http://localhost:${port}/update-check`]);
logOnError(launchResult, () => {
expect(launchResult).to.have.property('code', 1);
expect(launchResult.out).to.include('Cannot update to a bundle with a lower version number');
expect(requests).to.have.lengthOf(2);
expect(requests[0]).to.have.property('url', '/update-check');
expect(requests[1]).to.have.property('url', '/update-file');
expect(requests[0].header('user-agent')).to.include('Electron/');
expect(requests[1].header('user-agent')).to.include('Electron/');
});
});
});
it('should still update to higher version numbers', async () => {
await withUpdatableApp({
nextVersion: '1.0.1',
startFixture: 'update',
endFixture: 'update'
}, async (appPath, updateZipPath) => {
server.get('/update-file', (req, res) => {
res.download(updateZipPath);
});
server.get('/update-check', (req, res) => {
res.json({
url: `http://localhost:${port}/update-file`,
name: 'My Release Name',
notes: 'Theses are some release notes innit',
pub_date: (new Date()).toString()
});
});
const relaunchPromise = new Promise<void>((resolve) => {
server.get('/update-check/updated/:version', (req, res) => {
res.status(204).send();
resolve();
});
});
const launchResult = await launchApp(appPath, [`http://localhost:${port}/update-check`]);
logOnError(launchResult, () => {
expect(launchResult).to.have.property('code', 0);
expect(launchResult.out).to.include('Update Downloaded');
expect(requests).to.have.lengthOf(2);
expect(requests[0]).to.have.property('url', '/update-check');
expect(requests[1]).to.have.property('url', '/update-file');
expect(requests[0].header('user-agent')).to.include('Electron/');
expect(requests[1].header('user-agent')).to.include('Electron/');
});
await relaunchPromise;
expect(requests).to.have.lengthOf(3);
expect(requests[2].url).to.equal('/update-check/updated/1.0.1');
expect(requests[2].header('user-agent')).to.include('Electron/');
});
});
it('should compare version numbers correctly', () => {
expect(autoUpdater.isVersionAllowedForUpdate('1.0.0', '2.0.0')).to.equal(true);
expect(autoUpdater.isVersionAllowedForUpdate('1.0.1', '1.0.10')).to.equal(true);
expect(autoUpdater.isVersionAllowedForUpdate('1.0.10', '1.0.1')).to.equal(false);
expect(autoUpdater.isVersionAllowedForUpdate('1.31.1', '1.32.0')).to.equal(true);
expect(autoUpdater.isVersionAllowedForUpdate('1.31.1', '0.32.0')).to.equal(false);
});
});
it('should abort the update if the application is still running when ShipIt kicks off', async () => {
await withUpdatableApp({
nextVersion: '2.0.0',

View File

@@ -1,11 +1,9 @@
import { expect } from 'chai';
import { nativeTheme, systemPreferences, BrowserWindow, ipcMain } from 'electron/main';
import { nativeTheme, BrowserWindow, ipcMain } from 'electron/main';
import { once } from 'node:events';
import * as path from 'node:path';
import { setTimeout } from 'node:timers/promises';
import { expectDeprecationMessages } from './lib/deprecate-helpers';
import { ifdescribe } from './lib/spec-helpers';
import { closeAllWindows } from './lib/window-helpers';
describe('nativeTheme module', () => {
@@ -59,20 +57,6 @@ describe('nativeTheme module', () => {
expect(called).to.equal(false);
});
ifdescribe(process.platform === 'darwin')('on macOS', () => {
it('should update appLevelAppearance when set', async () => {
await expectDeprecationMessages(
() => {
nativeTheme.themeSource = 'dark';
expect(systemPreferences.appLevelAppearance).to.equal('dark');
nativeTheme.themeSource = 'light';
expect(systemPreferences.appLevelAppearance).to.equal('light');
},
"(electron) 'appLevelAppearance' is deprecated and will be removed."
);
});
});
const getPrefersColorSchemeIsDark = async (w: Electron.BrowserWindow) => {
const isDark: boolean = await w.webContents.executeJavaScript(
'matchMedia("(prefers-color-scheme: dark)").matches'

View File

@@ -1,6 +1,5 @@
import { expect } from 'chai';
import { systemPreferences } from 'electron/main';
import { expectDeprecationMessages } from './lib/deprecate-helpers';
import { ifdescribe } from './lib/spec-helpers';
describe('systemPreferences module', () => {
@@ -215,52 +214,6 @@ describe('systemPreferences module', () => {
const sysColor = systemPreferences.getColor(color);
expect(sysColor).to.be.a('string');
}
await expectDeprecationMessages(
() => {
const sysColor = systemPreferences.getColor('alternate-selected-control-text');
expect(sysColor).to.be.a('string');
},
"'alternate-selected-control-text' is deprecated as an input to getColor. Use 'selected-content-background' instead."
);
});
});
ifdescribe(process.platform === 'darwin')('systemPreferences.appLevelAppearance', () => {
const options = ['dark', 'light', 'unknown', null];
describe('with properties', () => {
it('returns a valid appearance', () => {
const appearance = systemPreferences.appLevelAppearance;
expect(options).to.include(appearance);
});
it('can be changed', () => {
systemPreferences.appLevelAppearance = 'dark';
expect(systemPreferences.appLevelAppearance).to.eql('dark');
});
});
describe('with functions', () => {
it('returns a valid appearance', async () => {
await expectDeprecationMessages(
() => {
const appearance = systemPreferences.getAppLevelAppearance();
expect(options).to.include(appearance);
},
"(electron) 'getAppLevelAppearance function' is deprecated and will be removed."
);
});
it('can be changed', async () => {
await expectDeprecationMessages(
() => {
systemPreferences.setAppLevelAppearance('dark');
const appearance = systemPreferences.getAppLevelAppearance();
expect(appearance).to.eql('dark');
},
"(electron) 'setAppLevelAppearance function' is deprecated and will be removed."
);
});
});
});

View File

@@ -839,6 +839,20 @@ describe('webContents module', () => {
expect(altKey).to.be.false();
});
it('can correctly convert accelerators to key codes', async () => {
const keyup = once(ipcMain, 'keyup');
w.webContents.sendInputEvent({ keyCode: 'Plus', type: 'char' });
w.webContents.sendInputEvent({ keyCode: 'Space', type: 'char' });
w.webContents.sendInputEvent({ keyCode: 'Plus', type: 'char' });
w.webContents.sendInputEvent({ keyCode: 'Space', type: 'char' });
w.webContents.sendInputEvent({ keyCode: 'Plus', type: 'char' });
w.webContents.sendInputEvent({ keyCode: 'Plus', type: 'keyUp' });
await keyup;
const inputText = await w.webContents.executeJavaScript('document.getElementById("input").value');
expect(inputText).to.equal('+ + +');
});
it('can send char events with modifiers', async () => {
const keypress = once(ipcMain, 'keypress');
w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Z' });

View File

@@ -0,0 +1,57 @@
const fs = require('node:fs');
const path = require('node:path');
process.on('uncaughtException', (err) => {
console.error(err);
process.exit(1);
});
const { app, autoUpdater } = require('electron');
autoUpdater.on('error', (err) => {
console.error(err);
process.exit(1);
});
const urlPath = path.resolve(__dirname, '../../../../url.txt');
let feedUrl = process.argv[1];
if (feedUrl === 'remain-open') {
// Hold the event loop
setInterval(() => {});
} else {
if (!feedUrl || !feedUrl.startsWith('http')) {
feedUrl = `${fs.readFileSync(urlPath, 'utf8')}/${app.getVersion()}`;
} else {
fs.writeFileSync(urlPath, `${feedUrl}/updated`);
}
autoUpdater.setFeedURL({
url: feedUrl
});
autoUpdater.checkForUpdates();
autoUpdater.on('update-available', () => {
console.log('Update Available');
});
let updateStackCount = 0;
autoUpdater.on('update-downloaded', () => {
updateStackCount++;
console.log('Update Downloaded');
if (updateStackCount > 1) {
autoUpdater.quitAndInstall();
} else {
setTimeout(() => {
autoUpdater.checkForUpdates();
}, 1000);
}
});
autoUpdater.on('update-not-available', () => {
console.error('No update available');
process.exit(1);
});
}

View File

@@ -0,0 +1,5 @@
{
"name": "electron-test-update-stack",
"version": "1.0.0",
"main": "./index.js"
}

View File

@@ -1,11 +1,17 @@
<html>
<body>
<input type="text" id="input" autofocus/>
<script type="text/javascript" charset="utf-8">
const { ipcRenderer } = require('electron')
document.onkeydown = function (e) {
require('electron').ipcRenderer.send('keydown', e.key, e.code, e.keyCode, e.shiftKey, e.ctrlKey, e.altKey)
ipcRenderer.send('keydown', e.key, e.code, e.keyCode, e.shiftKey, e.ctrlKey, e.altKey)
}
document.onkeypress = function (e) {
require('electron').ipcRenderer.send('keypress', e.key, e.code, e.keyCode, e.shiftKey, e.ctrlKey, e.altKey)
ipcRenderer.send('keypress', e.key, e.code, e.keyCode, e.shiftKey, e.ctrlKey, e.altKey)
}
document.onkeyup = function (e) {
ipcRenderer.send('keyup', e.key, e.code, e.keyCode, e.shiftKey, e.ctrlKey, e.altKey)
}
</script>
</body>

View File

@@ -377,6 +377,14 @@ if (process.platform === 'darwin') {
console.log(value);
const value2 = systemPreferences.getUserDefault('Foo', 'boolean');
console.log(value2);
// @ts-expect-error Removed API
console.log(systemPreferences.getAppLevelAppearance());
// @ts-expect-error Removed API
systemPreferences.setAppLevelAppearance('dark');
// @ts-expect-error Removed API
console.log(systemPreferences.appLevelAppearance);
// @ts-expect-error Removed API
console.log(systemPreferences.getColor('alternate-selected-control-text'));
}
// Create the window.

View File

@@ -254,6 +254,7 @@ declare namespace NodeJS {
// Additional properties
_firstFileName?: string;
_serviceStartupScript: string;
_getOrCreateArchive?: (path: string) => NodeJS.AsarArchive | null;
helperExecPath: string;
mainModule?: NodeJS.Module | undefined;

View File

@@ -19,6 +19,10 @@ declare namespace Electron {
setAppPath(path: string | null): void;
}
interface AutoUpdater {
isVersionAllowedForUpdate(currentVersion: string, targetVersion: string): boolean;
}
type TouchBarItemType = NonNullable<Electron.TouchBarConstructorOptions['items']>[0];
interface BaseWindow {

View File

@@ -199,10 +199,10 @@
"@octokit/auth-app" "^4.0.13"
"@octokit/rest" "^19.0.11"
"@electron/lint-roller@^1.8.0":
version "1.8.0"
resolved "https://registry.yarnpkg.com/@electron/lint-roller/-/lint-roller-1.8.0.tgz#26c29f2b2b7eaa9429fde04b178d7fdfe9d56da9"
integrity sha512-4LKE2SeSM3kdorDoFMzvZBTKvNUPAJl8apH9e1E9Gb5SKhFBZuG9CIJwtPKwtRYiGmBfu/HxoHuGEGkycxM+3Q==
"@electron/lint-roller@^1.9.0":
version "1.9.0"
resolved "https://registry.yarnpkg.com/@electron/lint-roller/-/lint-roller-1.9.0.tgz#784d2b9b7664dc7b696cc57cf6c9e181ea701e76"
integrity sha512-DgZJdzSAG19PotTPhWM4Xgt0RDv/nJS1JNKpJdvOUv2AsdU0rIq3kDAzm2AdzIW/AlrOnkpUA1UJNezdIAIA4g==
dependencies:
"@dsanders11/vscode-markdown-languageservice" "^0.3.0"
balanced-match "^2.0.0"
@@ -220,9 +220,9 @@
vscode-uri "^3.0.7"
"@electron/typescript-definitions@^8.14.5":
version "8.14.5"
resolved "https://registry.yarnpkg.com/@electron/typescript-definitions/-/typescript-definitions-8.14.5.tgz#07ffc7dac6008e0f659215e3b88bc0d7c6bc6ece"
integrity sha512-68JfMTcj6X7B0dhjhj8lGGnnOlfuiR4f+2UBEtQDYRHfeSuFriKErno3Lh+jAolGSqhw39qr4lLO+FGToVdCew==
version "8.14.6"
resolved "https://registry.yarnpkg.com/@electron/typescript-definitions/-/typescript-definitions-8.14.6.tgz#78ba1fa8314f06255bb9309791b33c9695ac42ef"
integrity sha512-HK70Q3nrp6h4cCxb/P65vFixdJ99vABLIG8TpqU21/fmuHdYboL4zcleWaYhXhU2EwduuOPfORFMrUTdBRc+lw==
dependencies:
"@types/node" "^11.13.7"
chalk "^2.4.2"