Compare commits

...

7 Commits

Author SHA1 Message Date
trop[bot]
06521fad4c build: exit upload with error code if github upload fails (#49945)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>
2026-02-25 23:58:28 -08:00
trop[bot]
771cbce43b fix: potential std::stoi crash in Windows Toasts (#49952)
fix: potential std::stoi crash in Windows Toasts

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-02-25 23:58:04 -08:00
Samuel Attard
b5cfc5dd91 fix: implement keychain item deletion in MAS safeStorage migration (#49817)
The MAS safeStorage patch had three bugs:

1. `AddRandomPasswordToKeychain` was called with the unsuffixed account
   name, so first launches always created the wrong keychain entry,
   triggering an unnecessary migration on the next launch.

2. `FindGenericPassword` never populated the `SecKeychainItemRef` output
   parameter — the SecItem API doesn't return that deprecated type.

3. `ItemDelete` was a no-op stub that always returned `noErr` without
   actually deleting anything.

Fixes all three by using `suffixed_account_name` for new entries,
removing the unused `SecKeychainItemRef` parameter, and implementing
`ItemDelete` with `SecItemDelete` using the modern SecItem API.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 22:19:56 -08:00
trop[bot]
d6e6fcea86 feat: add support for --experimental-transform-types (#49883)
* feat: add support for `--experimental-transform-types`

Co-authored-by: Niklas Wenzel <dev@nikwen.de>

* chore: add tests

Co-authored-by: Niklas Wenzel <dev@nikwen.de>

* docs: add `--experimental-transform-types` to docs

Co-authored-by: Niklas Wenzel <dev@nikwen.de>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Niklas Wenzel <dev@nikwen.de>
2026-02-25 12:54:38 -05:00
trop[bot]
cc64a5e8d9 fix: crash after win.showAllTabs() new tab (#49934)
fix: crash after win.showAllTabs new tab

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-02-25 08:39:27 -05:00
electron-roller[bot]
2d12f059b4 chore: bump chromium to 144.0.7559.225 (40-x-y) (#49928)
chore: bump chromium in DEPS to 144.0.7559.225

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
2026-02-24 11:20:35 -05:00
trop[bot]
f715930d49 docs: fix some string enum typings (#49931)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2026-02-24 11:19:28 -05:00
12 changed files with 143 additions and 96 deletions

2
DEPS
View File

@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
vars = {
'chromium_version':
'144.0.7559.220',
'144.0.7559.225',
'node_version':
'v24.13.1',
'nan_version':

View File

@@ -1331,7 +1331,7 @@ Returns `boolean` - Whether the current desktop environment is Unity launcher.
### `app.getLoginItemSettings([options])` _macOS_ _Windows_
* `options` Object (optional)
* `type` string (optional) _macOS_ - Can be one of `mainAppService`, `agentService`, `daemonService`, or `loginItemService`. Defaults to `mainAppService`. Only available on macOS 13 and up. See [app.setLoginItemSettings](app.md#appsetloginitemsettingssettings-macos-windows) for more information about each type.
* `type` string (optional) _macOS_ - Can be `mainAppService`, `agentService`, `daemonService`, or `loginItemService`. Defaults to `mainAppService`. Only available on macOS 13 and up. See [app.setLoginItemSettings](app.md#appsetloginitemsettingssettings-macos-windows) for more information about each type.
* `serviceName` string (optional) _macOS_ - The name of the service. Required if `type` is non-default. Only available on macOS 13 and up.
* `path` string (optional) _Windows_ - The executable path to compare against. Defaults to `process.execPath`.
* `args` string[] (optional) _Windows_ - The command-line arguments to compare against. Defaults to an empty array.
@@ -1346,13 +1346,13 @@ Returns `Object`:
* `wasOpenedAtLogin` boolean _macOS_ - `true` if the app was opened at login automatically.
* `wasOpenedAsHidden` boolean _macOS_ _Deprecated_ - `true` if the app was opened as a hidden login item. This indicates that the app should not open any windows at startup. This setting is not available on [MAS builds][mas-builds] or on macOS 13 and up.
* `restoreState` boolean _macOS_ _Deprecated_ - `true` if the app was opened as a login item that should restore the state from the previous session. This indicates that the app should restore the windows that were open the last time the app was closed. This setting is not available on [MAS builds][mas-builds] or on macOS 13 and up.
* `status` string _macOS_ - can be one of `not-registered`, `enabled`, `requires-approval`, or `not-found`.
* `status` string _macOS_ - can be `not-registered`, `enabled`, `requires-approval`, or `not-found`.
* `executableWillLaunchAtLogin` boolean _Windows_ - `true` if app is set to open at login and its run key is not deactivated. This differs from `openAtLogin` as it ignores the `args` option, this property will be true if the given executable would be launched at login with **any** arguments.
* `launchItems` Object[] _Windows_
* `name` string _Windows_ - name value of a registry entry.
* `path` string _Windows_ - The executable to an app that corresponds to a registry entry.
* `args` string[] _Windows_ - the command-line arguments to pass to the executable.
* `scope` string _Windows_ - one of `user` or `machine`. Indicates whether the registry entry is under `HKEY_CURRENT USER` or `HKEY_LOCAL_MACHINE`.
* `scope` string _Windows_ - can be `user` or `machine`. Indicates whether the registry entry is under `HKEY_CURRENT USER` or `HKEY_LOCAL_MACHINE`.
* `enabled` boolean _Windows_ - `true` if the app registry key is startup approved and therefore shows as `enabled` in Task Manager and Windows settings.
### `app.setLoginItemSettings(settings)` _macOS_ _Windows_

View File

@@ -350,6 +350,11 @@ Affects the default output directory of [v8.setHeapSnapshotNearHeapLimit](https:
Disable exposition of [Navigator API][] on the global scope from Node.js.
### `--experimental-transform-types`
Enables the [transformation](https://nodejs.org/api/typescript.html#type-stripping)
of TypeScript-only syntax into JavaScript code.
## Chromium Flags
There isn't a documented list of all Chromium switches, but there are a few ways to find them.

View File

@@ -12,7 +12,7 @@ We attempt to migrate the safe storage key from the old account, if that migrati
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/common/keychain_password_mac.mm b/components/os_crypt/common/keychain_password_mac.mm
index f19628cc0cdba39b232f55935e8eee9786b02a77..036b50f53e78bc21ed1e1d6dd876b50ab1e8f05d 100644
index f19628cc0cdba39b232f55935e8eee9786b02a77..ab6ce9b1cf85029050a7497cd53813a03a46d408 100644
--- a/components/os_crypt/common/keychain_password_mac.mm
+++ b/components/os_crypt/common/keychain_password_mac.mm
@@ -27,6 +27,12 @@
@@ -28,14 +28,13 @@ index f19628cc0cdba39b232f55935e8eee9786b02a77..036b50f53e78bc21ed1e1d6dd876b50a
namespace {
// These two strings ARE indeed user facing. But they are used to access
@@ -96,11 +102,51 @@
@@ -96,18 +102,56 @@
uma_result);
};
+ const std::string account_name_suffix = kAccountNameSuffix;
+ const std::string suffixed_account_name = GetAccountName() + account_name_suffix;
auto password =
- keychain_->FindGenericPassword(GetServiceName(), GetAccountName());
+ keychain_->FindGenericPassword(GetServiceName(), suffixed_account_name);
+
+ if (password.has_value()) {
@@ -53,10 +52,8 @@ index f19628cc0cdba39b232f55935e8eee9786b02a77..036b50f53e78bc21ed1e1d6dd876b50a
+
+ // 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::apple::ScopedCFTypeRef<SecKeychainItemRef> item_ref;
+ password =
+ keychain_->FindGenericPassword(GetServiceName(), GetAccountName(),
+ item_ref.InitializeInto());
keychain_->FindGenericPassword(GetServiceName(), GetAccountName());
if (password.has_value()) {
uma_result = FindGenericPasswordResult::kPasswordFound;
@@ -70,7 +67,7 @@ index f19628cc0cdba39b232f55935e8eee9786b02a77..036b50f53e78bc21ed1e1d6dd876b50a
+ // 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());
+ error = keychain_->ItemDelete(GetServiceName(), GetAccountName());
+ if (error != noErr) {
+ OSSTATUS_DLOG(ERROR, error) << "Keychain delete for legacy key failed";
+ }
@@ -81,115 +78,79 @@ index f19628cc0cdba39b232f55935e8eee9786b02a77..036b50f53e78bc21ed1e1d6dd876b50a
return std::string(base::as_string_view(*password));
}
if (password.error() == errSecItemNotFound) {
uma_result = FindGenericPasswordResult::kPasswordNotFound;
return AddRandomPasswordToKeychain(*keychain_, GetServiceName(),
- GetAccountName());
+ suffixed_account_name);
}
OSSTATUS_LOG(ERROR, password.error()) << "Keychain lookup failed";
diff --git a/crypto/apple/keychain.h b/crypto/apple/keychain.h
index 1d2264a5229206f45d1a9bcb009d47180efa6a8b..1dcf2b1d09831012c7f5768a5c6193d529efc821 100644
index 1d2264a5229206f45d1a9bcb009d47180efa6a8b..4472e5b605e09659bd75cd4797f073775fe4b354 100644
--- a/crypto/apple/keychain.h
+++ b/crypto/apple/keychain.h
@@ -17,6 +17,14 @@
namespace crypto::apple {
+// TODO(smaddock): Migrate to SecItem* as part of
+// https://issues.chromium.org/issues/40233280
+#if BUILDFLAG(IS_IOS)
+using AppleSecKeychainItemRef = void*;
+#else
+using AppleSecKeychainItemRef = SecKeychainItemRef;
+#endif
+
// Wraps the KeychainServices API in a very thin layer, to allow it to be
// mocked out for testing.
@@ -44,13 +52,18 @@ class CRYPTO_EXPORT Keychain {
// std::vector<uint8_t> arm is populated instead.
virtual base::expected<std::vector<uint8_t>, OSStatus> FindGenericPassword(
std::string_view service_name,
- std::string_view account_name) const = 0;
+ std::string_view account_name,
+ AppleSecKeychainItemRef* item = nullptr) const = 0;
virtual OSStatus AddGenericPassword(
std::string_view service_name,
@@ -51,6 +51,11 @@ class CRYPTO_EXPORT Keychain {
std::string_view account_name,
base::span<const uint8_t> password) const = 0;
+#if BUILDFLAG(IS_MAC)
+ virtual OSStatus ItemDelete(AppleSecKeychainItemRef item) const = 0;
+#endif // !BUILDFLAG(IS_MAC)
+ virtual OSStatus ItemDelete(std::string_view service_name,
+ std::string_view account_name) const = 0;
+#endif // BUILDFLAG(IS_MAC)
+
protected:
Keychain();
};
diff --git a/crypto/apple/keychain_secitem.h b/crypto/apple/keychain_secitem.h
index eb74282adaba24ebd667f0ab3fc34dbe4cd8b527..7b91eb27489cece38eca719986255c5ec01c4bac 100644
index eb74282adaba24ebd667f0ab3fc34dbe4cd8b527..0d25e49e2fa1b374d6867b8c602f7685a7f9498d 100644
--- a/crypto/apple/keychain_secitem.h
+++ b/crypto/apple/keychain_secitem.h
@@ -17,12 +17,17 @@ class CRYPTO_EXPORT KeychainSecItem : public Keychain {
base::expected<std::vector<uint8_t>, OSStatus> FindGenericPassword(
std::string_view service_name,
- std::string_view account_name) const override;
+ std::string_view account_name,
+ AppleSecKeychainItemRef* item) const override;
OSStatus AddGenericPassword(
@@ -23,6 +23,11 @@ class CRYPTO_EXPORT KeychainSecItem : public Keychain {
std::string_view service_name,
std::string_view account_name,
base::span<const uint8_t> password) const override;
+
+#if BUILDFLAG(IS_MAC)
+ OSStatus ItemDelete(AppleSecKeychainItemRef item) const override;
+#endif // !BUILDFLAG(IS_MAC)
+ OSStatus ItemDelete(std::string_view service_name,
+ std::string_view account_name) const override;
+#endif // BUILDFLAG(IS_MAC)
};
} // namespace crypto::apple
diff --git a/crypto/apple/keychain_secitem.mm b/crypto/apple/keychain_secitem.mm
index a8d50dd27db52526b0635c2b97f076df1994a6aa..e45f0d1079c8acfae55cf873e66ab3d9a10ad8ee 100644
index a8d50dd27db52526b0635c2b97f076df1994a6aa..464c17909b9a554b269a70ea08771da6ec7ac011 100644
--- a/crypto/apple/keychain_secitem.mm
+++ b/crypto/apple/keychain_secitem.mm
@@ -138,7 +138,8 @@
base::expected<std::vector<uint8_t>, OSStatus>
KeychainSecItem::FindGenericPassword(std::string_view service_name,
- std::string_view account_name) const {
+ std::string_view account_name,
+ AppleSecKeychainItemRef* item) const {
base::apple::ScopedCFTypeRef<CFDictionaryRef> query =
MakeGenericPasswordQuery(service_name, account_name);
@@ -165,4 +166,13 @@
@@ -165,4 +165,18 @@
return base::ToVector(base::apple::CFDataToSpan(password_data));
}
+#if BUILDFLAG(IS_MAC)
+OSStatus KeychainSecItem::ItemDelete(AppleSecKeychainItemRef item) const {
+ // TODO(smaddock): AppleSecKeychainItemRef aliases the deprecated
+ // SecKeychainItemRef. Need to update this to accept a CFDictionary in the
+ // case of SecItemDelete.
+ return noErr;
+OSStatus KeychainSecItem::ItemDelete(std::string_view service_name,
+ std::string_view account_name) const {
+ NSDictionary* query = @{
+ CFToNSPtrCast(kSecClass) : CFToNSPtrCast(kSecClassGenericPassword),
+ CFToNSPtrCast(kSecAttrService) : base::SysUTF8ToNSString(service_name),
+ CFToNSPtrCast(kSecAttrAccount) : base::SysUTF8ToNSString(account_name),
+ };
+ base::apple::ScopedCFTypeRef<CFDictionaryRef> cf_query(
+ NSToCFOwnershipCast(query));
+ return SecItemDelete(cf_query.get());
+}
+#endif
+
} // namespace crypto::apple
diff --git a/crypto/apple/mock_keychain.cc b/crypto/apple/mock_keychain.cc
index 080806aaf3fc10548b160850ad36ef3519ea2b6f..21f04059d67ba41118face6ee9327aa05e854362 100644
index 080806aaf3fc10548b160850ad36ef3519ea2b6f..98625524b668b86c857d5a8910bfb53b3ab40575 100644
--- a/crypto/apple/mock_keychain.cc
+++ b/crypto/apple/mock_keychain.cc
@@ -32,7 +32,8 @@ MockKeychain::~MockKeychain() = default;
base::expected<std::vector<uint8_t>, OSStatus>
MockKeychain::FindGenericPassword(std::string_view service_name,
- std::string_view account_name) const {
+ std::string_view account_name,
+ AppleSecKeychainItemRef* item) const {
IncrementKeychainAccessHistogram();
// When simulating |noErr|, return canned |passwordData| and
@@ -56,6 +57,10 @@ OSStatus MockKeychain::AddGenericPassword(
@@ -56,6 +56,11 @@ OSStatus MockKeychain::AddGenericPassword(
return noErr;
}
+OSStatus MockKeychain::ItemDelete(SecKeychainItemRef itemRef) const {
+OSStatus MockKeychain::ItemDelete(std::string_view service_name,
+ std::string_view account_name) const {
+ return noErr;
+}
+
@@ -197,25 +158,17 @@ index 080806aaf3fc10548b160850ad36ef3519ea2b6f..21f04059d67ba41118face6ee9327aa0
IncrementKeychainAccessHistogram();
return kPassword;
diff --git a/crypto/apple/mock_keychain.h b/crypto/apple/mock_keychain.h
index 680efe0312c81449e069c19d9c6ef146da7834db..b49c2ba5f639344ab57e9f14c098effc38729d1f 100644
index 680efe0312c81449e069c19d9c6ef146da7834db..102db6013b505fed32db176a90f5176118f62773 100644
--- a/crypto/apple/mock_keychain.h
+++ b/crypto/apple/mock_keychain.h
@@ -36,13 +36,18 @@ class CRYPTO_EXPORT MockKeychain : public Keychain {
// Keychain implementation.
base::expected<std::vector<uint8_t>, OSStatus> FindGenericPassword(
std::string_view service_name,
- std::string_view account_name) const override;
+ std::string_view account_name,
+ AppleSecKeychainItemRef* item) const override;
OSStatus AddGenericPassword(
std::string_view service_name,
@@ -43,6 +43,11 @@ class CRYPTO_EXPORT MockKeychain : public Keychain {
std::string_view account_name,
base::span<const uint8_t> password) const override;
+#if !BUILDFLAG(IS_IOS)
+ OSStatus ItemDelete(SecKeychainItemRef itemRef) const override;
+#endif // !BUILDFLAG(IS_IOS)
+#if BUILDFLAG(IS_MAC)
+ OSStatus ItemDelete(std::string_view service_name,
+ std::string_view account_name) const override;
+#endif // BUILDFLAG(IS_MAC)
+
// Returns the password that OSCrypt uses to generate its encryption key.
std::string GetEncryptionPassword() const;

View File

@@ -368,6 +368,9 @@ def upload_io_to_github(release, filename, filepath, version):
for c in iter(lambda: upload_process.stdout.read(1), b""):
sys.stdout.buffer.write(c)
sys.stdout.flush()
upload_process.wait()
if upload_process.returncode != 0:
sys.exit(upload_process.returncode)
if "GITHUB_OUTPUT" in os.environ:
output_path = os.environ["GITHUB_OUTPUT"]

View File

@@ -371,7 +371,7 @@ void HandleToastActivation(const std::wstring& invoked_args,
int action_index = -1;
if (!action_index_str.empty()) {
action_index = std::stoi(action_index_str);
base::StringToInt(base::WideToUTF8(action_index_str), &action_index);
}
std::string reply_text;

View File

@@ -35,6 +35,41 @@ int ScopedDisableResize::disable_resize_ = 0;
typedef void (*MouseDownImpl)(id, SEL, NSEvent*);
// Work around an Apple bug where the visual tab picker's
// grid animation creates NSLayoutConstraints against nil layout anchors,
// crashing in NSVisualTabPickerShadowTileView. This happens when a new tabbed
// window is created while the tab picker is open — the "+" tile (and possibly
// others) have broken internal state. Rather than patching individual tile
// animation methods, short-circuit the entire grid animation by swizzling
// NSVisualTabPickerGridView's -startGridAnimation:completionHandler: to
// immediately invoke the completion handler without running the animation.
typedef void (*StartGridAnimationIMP)(id, SEL, id, id);
static StartGridAnimationIMP g_orig_startGridAnimation = nullptr;
static void Patched_startGridAnimation(id self,
SEL _cmd,
id animation,
void (^completionHandler)(void)) {
if (completionHandler)
completionHandler();
}
static void SwizzleTabPickerGridAnimation() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class cls = NSClassFromString(@"NSVisualTabPickerGridView");
if (!cls)
return;
SEL sel = @selector(startGridAnimation:completionHandler:);
Method method = class_getInstanceMethod(cls, sel);
if (!method)
return;
g_orig_startGridAnimation =
(StartGridAnimationIMP)method_getImplementation(method);
method_setImplementation(method, (IMP)Patched_startGridAnimation);
});
}
namespace {
MouseDownImpl g_nsthemeframe_mousedown;
MouseDownImpl g_nsnextstepframe_mousedown;
@@ -125,6 +160,7 @@ void SwizzleSwipeWithEvent(NSView* view, SEL swiz_selector) {
- (id)initWithShell:(electron::NativeWindowMac*)shell
styleMask:(NSUInteger)styleMask {
SwizzleTabPickerGridAnimation();
if ((self = [super initWithContentRect:ui::kWindowSizeDeterminedLater
styleMask:styleMask
backing:NSBackingStoreBuffered

View File

@@ -413,6 +413,7 @@ bool IsAllowedOption(const std::string_view option) {
"--inspect-port",
"--inspect-publish-uid",
"--experimental-network-inspection",
"--experimental-transform-types",
});
// This should be aligned with what's possible to set via the process object.

7
spec/fixtures/type-stripping/basic.ts vendored Normal file
View File

@@ -0,0 +1,7 @@
import { app } from 'electron/main';
const logMessage = (message: string): void => console.log(message);
logMessage('running');
app.exit(0);

View File

@@ -0,0 +1,9 @@
enum Test {
A,
B,
C,
}
console.log(Test.A);
process.exit(0);

View File

@@ -0,0 +1,11 @@
import { app } from 'electron/main';
enum Test {
A,
B,
C,
}
console.log(Test.A);
app.exit(0);

View File

@@ -1030,4 +1030,26 @@ describe('node feature', () => {
});
});
});
describe('type stripping', () => {
it('strips TypeScript types automatically in the main process', async () => {
const child = childProcess.spawn(process.execPath, [path.join(fixtures, 'type-stripping', 'basic.ts')]);
const [code] = await once(child, 'exit');
expect(code).to.equal(0);
});
it('will not transform TypeScript types without --experimental-transform-types', async () => {
const child = childProcess.spawn(process.execPath, [path.join(fixtures, 'type-stripping', 'transform-types-node.ts')], {
env: { ELECTRON_RUN_AS_NODE: 'true' }
});
const [code] = await once(child, 'exit');
expect(code).to.not.equal(0);
});
it('transforms TypeScript types with --experimental-transform-types', async () => {
const child = childProcess.spawn(process.execPath, ['--experimental-transform-types', path.join(fixtures, 'type-stripping', 'transform-types.ts')]);
const [code] = await once(child, 'exit');
expect(code).to.equal(0);
});
});
});