Compare commits

...

6 Commits

Author SHA1 Message Date
trop[bot]
de61f6c5e8 feat: show toast dismissal reason on Windows (#50030)
* feat: show toast dismissal reason on Windows

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

* Update docs/api/notification.md

Co-authored-by: David Sanders <dsanders11@ucsbalum.com>

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-05 11:29:21 +01:00
electron-roller[bot]
90f85f2bf4 chore: bump chromium to 144.0.7559.236 (40-x-y) (#50060)
* chore: bump chromium in DEPS to 144.0.7559.236

* chore: update patches

---------

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <kleinschmidtorama@gmail.com>
2026-03-05 10:30:45 +01:00
trop[bot]
60951cdca9 fix: use proper quoting for exe paths and args on Windows (#50075)
Previously, GetProtocolLaunchPath and FormatCommandLineString in
browser_win.cc used naive quoting which could break when paths or
arguments contained backslashes, spaces, or embedded quotes.

Fix by extracting the CommandLineToArgvW-compatible quoting logic from
relauncher_win.cc into a shared utility and use it in both browser_win.cc
and relauncher_win.cc to properly quote the exe path and each argument
individually.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-05 10:27:40 +01:00
trop[bot]
a3022df30f build: fix code-signing for MacOS x64 tests (#50072)
* fix: code-sign binaries for notification tests

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

* test: remove redundent feedURL test

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

* test: move squirrel feed tests to api-autoupdater

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

* fix: fix SQRLShipItRequest.JSONKeyPathsByPropertyKey mappings

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

* Revert "fix: fix SQRLShipItRequest.JSONKeyPathsByPropertyKey mappings"

This reverts commit 5ad9892a67.

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

* test: unsign tests requiring no signed app

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

* fixup! fix: fix SQRLShipItRequest.JSONKeyPathsByPropertyKey mappings

chore: fix patch shear

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2026-03-05 10:27:29 +01:00
trop[bot]
996fbfd6bc chore: remove applescript from trash (#50065)
Previously, when trashItemAtURL: failed (e.g. on network shares or
under app translocation), the code fell back to constructing an
AppleScript that interpolated the bundle path directly into a string
literal via %@ with no escaping. This was fragile and unnecessary —
trashItemAtURL: has been the standard API since 10.8 and covers the
relevant cases. The fix simply removes the AppleScript fallback
entirely, so Trash() now returns the result of trashItemAtURL: directly.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2026-03-04 16:14:15 +01:00
trop[bot]
79d1e32281 fix: uaf in non-client hittest during view teardown (#50053)
* fix: uaf in non-client hittest during view teardown

Co-authored-by: deepak1556 <hop2deep@gmail.com>

* chore: update crash spec

Co-authored-by: deepak1556 <hop2deep@gmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: deepak1556 <hop2deep@gmail.com>
2026-03-03 14:16:05 -05:00
28 changed files with 282 additions and 155 deletions

View File

@@ -191,12 +191,21 @@ jobs:
run: |
cd src/out/Default
unzip -:o dist.zip
#- name: Import & Trust Self-Signed Codesigning Cert on MacOS
# if: ${{ inputs.target-platform == 'macos' && inputs.target-arch == 'x64' }}
# run: |
# sudo security authorizationdb write com.apple.trust-settings.admin allow
# cd src/electron
# ./script/codesign/generate-identity.sh
- name: Import & Trust Self-Signed Codesigning Cert on MacOS
if: ${{ inputs.target-platform == 'macos' }}
run: |
cd src/electron
./script/codesign/generate-identity.sh
# Only sign on x64 — arm64 builds are already ad-hoc signed, and re-signing
# with an untrusted cert breaks macOS system integrations (e.g. dock bounce).
# Autoupdater tests sign their own fixture copies via signApp().
- name: Sign Electron.app for macOS tests
if: ${{ inputs.target-platform == 'macos' && inputs.target-arch == 'x64' }}
run: |
identity=$(src/electron/script/codesign/get-trusted-identity.sh)
if [ -n "$identity" ]; then
codesign -s "$identity" --deep --force src/out/Default/Electron.app
fi
- name: Run Electron Tests
shell: bash

2
DEPS
View File

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

View File

@@ -111,7 +111,8 @@ app.whenReady().then(() => {
Returns:
* `event` Event
* `details` Event\<\>
* `reason` _Windows_ string (optional) - The reason the notification was closed. This can be 'userCanceled', 'applicationHidden', or 'timedOut'.
Emitted when the notification is closed by manual intervention from the user.

View File

@@ -115,6 +115,8 @@ filenames = {
"shell/browser/win/scoped_hstring.h",
"shell/common/api/electron_api_native_image_win.cc",
"shell/common/application_info_win.cc",
"shell/common/command_line_util_win.cc",
"shell/common/command_line_util_win.h",
"shell/common/language_util_win.cc",
"shell/common/node_bindings_win.cc",
"shell/common/node_bindings_win.h",

View File

@@ -9,10 +9,10 @@ potentially prevent a window from being created.
TODO(loc): this patch is currently broken.
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index edaf9a7b2efc5ed7f4e946d720de54d5001f44d4..7d27b076c1947d2cd08364f87286ed6d9f460cdc 100644
index ce64151028cc30c81292326ee73126cb8415aec5..3feca12a6185afef139a0cb4a8148b5a3ca9e32f 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -9867,6 +9867,7 @@ void RenderFrameHostImpl::CreateNewWindow(
@@ -9868,6 +9868,7 @@ void RenderFrameHostImpl::CreateNewWindow(
last_committed_origin_, params->window_container_type,
params->target_url, params->referrer.To<Referrer>(),
params->frame_name, params->disposition, *params->features,

View File

@@ -15,10 +15,10 @@ Note that we also need to manually update embedder's
`api::WebContents::IsFullscreenForTabOrPending` value.
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 7d27b076c1947d2cd08364f87286ed6d9f460cdc..55d1609ba46abe9433d22a894fc2b498735ff778 100644
index 3feca12a6185afef139a0cb4a8148b5a3ca9e32f..393808a7959d684fe4e46f86eff687ec15258fa9 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -8973,6 +8973,17 @@ void RenderFrameHostImpl::EnterFullscreen(
@@ -8974,6 +8974,17 @@ void RenderFrameHostImpl::EnterFullscreen(
}
}

View File

@@ -0,0 +1,21 @@
#!/bin/sh
# Removes the codesigning keychain created by generate-identity.sh.
# Safe to run even if generate-identity.sh was never run (each step
# is guarded).
set -eo pipefail
KEYCHAIN="electron-codesign.keychain-db"
# delete-keychain also removes it from the search list
if security list-keychains -d user | grep -q "$KEYCHAIN"; then
security delete-keychain "$KEYCHAIN"
echo "Deleted keychain: $KEYCHAIN"
else
echo "Keychain not found, nothing to delete"
fi
# Clean up working directory
rm -rf "$(dirname $0)"/.working
echo "Cleanup complete"

View File

@@ -3,6 +3,8 @@
set -eo pipefail
dir="$(dirname $0)"/.working
KEYCHAIN="electron-codesign.keychain-db"
KEYCHAIN_TEMP="$(openssl rand -hex 12)"
cleanup() {
rm -rf "$dir"
@@ -18,30 +20,16 @@ mkdir -p "$dir"
# Generate Certs
openssl req -new -newkey rsa:2048 -x509 -days 7300 -nodes -config "$(dirname $0)"/codesign.cnf -extensions extended -batch -out "$dir"/certificate.cer -keyout "$dir"/certificate.key
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "$dir"/certificate.cer
sudo security import "$dir"/certificate.key -A -k /Library/Keychains/System.keychain
# restart(reload) taskgated daemon
sudo pkill -f /usr/libexec/taskgated
# macOS 15+ blocks modifications to the system keychain via SIP/TCC,
# so we use a custom user-scoped keychain instead.
# Refs https://github.com/electron/electron/issues/48182
security create-keychain -p "$KEYCHAIN_TEMP" "$KEYCHAIN"
security set-keychain-settings -t 3600 -u "$KEYCHAIN"
security unlock-keychain -p "$KEYCHAIN_TEMP" "$KEYCHAIN"
# need once
sudo security authorizationdb write system.privilege.taskport allow
# need once
DevToolsSecurity -enable
security list-keychains -d user -s "$KEYCHAIN" $(security list-keychains -d user | tr -d '"')
security import "$dir"/certificate.cer -k "$KEYCHAIN" -T /usr/bin/codesign
security import "$dir"/certificate.key -k "$KEYCHAIN" -T /usr/bin/codesign -A
# openssl req -newkey rsa:2048 -nodes -keyout "$dir"/private.pem -x509 -days 1 -out "$dir"/certificate.pem -extensions extended -config "$(dirname $0)"/codesign.cnf
# openssl x509 -inform PEM -in "$dir"/certificate.pem -outform DER -out "$dir"/certificate.cer
# openssl x509 -pubkey -noout -in "$dir"/certificate.pem > "$dir"/public.key
# rm -f "$dir"/certificate.pem
# Import Certs
# security import "$dir"/certificate.cer -k $KEY_CHAIN
# security import "$dir"/private.pem -k $KEY_CHAIN
# security import "$dir"/public.key -k $KEY_CHAIN
# Generate Trust Settings
# TODO: Remove NPX
npm_config_yes=true npx ts-node "$(dirname $0)"/gen-trust.ts "$dir"/certificate.cer "$dir"/trust.xml
# Import Trust Settings
sudo security trust-settings-import -d "$dir/trust.xml"
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_TEMP" "$KEYCHAIN"

View File

@@ -2,7 +2,7 @@
set -e
valid_certs=$(security find-identity -p codesigning -v)
valid_certs=$(security find-identity -p codesigning)
if [[ $valid_certs == *"1)"* ]]; then
first_valid_cert=$(echo $valid_certs | sed 's/ \".*//' | sed 's/.* //')
echo $first_valid_cert

View File

@@ -189,8 +189,23 @@ void Notification::NotificationFailed(const std::string& error) {
void Notification::NotificationDestroyed() {}
void Notification::NotificationClosed() {
Emit("close");
void Notification::NotificationClosed(const std::string& reason) {
if (reason.empty()) {
Emit("close");
} else {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate);
gin_helper::internal::Event* event =
gin_helper::internal::Event::New(isolate);
v8::Local<v8::Object> event_object =
event->GetWrapper(isolate).ToLocalChecked();
gin_helper::Dictionary dict(isolate, event_object);
dict.Set("reason", reason);
EmitWithoutEvent("close", event_object);
}
}
void Notification::Close() {

View File

@@ -50,7 +50,7 @@ class Notification final : public gin_helper::DeprecatedWrappable<Notification>,
void NotificationReplied(const std::string& reply) override;
void NotificationDisplayed() override;
void NotificationDestroyed() override;
void NotificationClosed() override;
void NotificationClosed(const std::string& reason) override;
void NotificationFailed(const std::string& error) override;
// gin_helper::Wrappable

View File

@@ -83,13 +83,17 @@ void WebContentsView::ApplyBorderRadius() {
int WebContentsView::NonClientHitTest(const gfx::Point& point) {
if (api_web_contents_) {
auto* iwc = api_web_contents_->inspectable_web_contents();
if (!iwc)
return HTNOWHERE;
// Convert the point to the contents view's coordinate space rather than
// the InspectableWebContentsView's coordinate space, because the draggable
// region is relative to the web content area. When DevTools is docked
// (e.g. to the left), the contents view is offset within the parent,
// so we need to account for that offset.
auto* inspectable_view =
api_web_contents_->inspectable_web_contents()->GetView();
auto* inspectable_view = iwc->GetView();
if (!inspectable_view)
return HTNOWHERE;
auto* contents_view = inspectable_view->GetContentsView();
gfx::Point local_point(point);
views::View::ConvertPointFromWidget(contents_view, &local_point);

View File

@@ -37,6 +37,7 @@
#include "shell/browser/ui/win/jump_list.h"
#include "shell/browser/window_list.h"
#include "shell/common/application_info.h"
#include "shell/common/command_line_util_win.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/image_converter.h"
#include "shell/common/gin_converters/login_item_settings_converter.h"
@@ -79,13 +80,22 @@ bool GetProtocolLaunchPath(gin::Arguments* args, std::wstring* exe) {
return false;
}
// Strip surrounding double quotes before re-quoting with AddQuoteForArg.
if (exe->size() >= 2 && exe->front() == L'"' && exe->back() == L'"') {
*exe = exe->substr(1, exe->size() - 2);
}
// Read in optional args arg
std::vector<std::wstring> launch_args;
if (args->GetNext(&launch_args) && !launch_args.empty()) {
std::wstring joined_args = base::JoinString(launch_args, L"\" \"");
*exe = base::StrCat({L"\"", *exe, L"\" \"", joined_args, L"\" \"%1\""});
std::wstring result = electron::AddQuoteForArg(*exe);
for (const auto& arg : launch_args) {
result += L' ';
result += electron::AddQuoteForArg(arg);
}
*exe = base::StrCat({result, L" \"%1\""});
} else {
*exe = base::StrCat({L"\"", *exe, L"\" \"%1\""});
*exe = base::StrCat({electron::AddQuoteForArg(*exe), L" \"%1\""});
}
return true;
@@ -153,9 +163,18 @@ bool FormatCommandLineString(std::wstring* exe,
return false;
}
// Strip surrounding double quotes before re-quoting with AddQuoteForArg.
if (exe->size() >= 2 && exe->front() == L'"' && exe->back() == L'"') {
*exe = exe->substr(1, exe->size() - 2);
}
*exe = electron::AddQuoteForArg(*exe);
if (!launch_args.empty()) {
std::u16string joined_launch_args = base::JoinString(launch_args, u" ");
*exe = base::StrCat({*exe, L" ", base::AsWStringView(joined_launch_args)});
for (const auto& arg : launch_args) {
*exe += L' ';
*exe += electron::AddQuoteForArg(std::wstring(base::AsWStringView(arg)));
}
}
return true;

View File

@@ -46,9 +46,10 @@ void Notification::NotificationClicked() {
Destroy();
}
void Notification::NotificationDismissed(bool should_destroy) {
void Notification::NotificationDismissed(bool should_destroy,
const std::string& close_reason) {
if (delegate())
delegate()->NotificationClosed();
delegate()->NotificationClosed(close_reason);
set_is_dismissed(true);

View File

@@ -76,7 +76,8 @@ class Notification {
// Should be called by derived classes.
void NotificationClicked();
void NotificationDismissed(bool should_destroy = true);
void NotificationDismissed(bool should_destroy = true,
const std::string& close_reason = "");
void NotificationFailed(const std::string& error = "");
// delete this.

View File

@@ -24,7 +24,7 @@ class NotificationDelegate {
virtual void NotificationAction(int action_index, int selection_index = -1) {}
virtual void NotificationClick() {}
virtual void NotificationClosed() {}
virtual void NotificationClosed(const std::string& reason = "") {}
virtual void NotificationDisplayed() {}
protected:

View File

@@ -60,7 +60,7 @@ class NotificationDelegateImpl final : public electron::NotificationDelegate {
->DispatchNonPersistentClickEvent(notification_id_, base::DoNothing());
}
void NotificationClosed() override {
void NotificationClosed(const std::string& reason) override {
content::NotificationEventDispatcher::GetInstance()
->DispatchNonPersistentCloseEvent(notification_id_, base::DoNothing());
}

View File

@@ -800,9 +800,24 @@ IFACEMETHODIMP ToastEventHandler::Invoke(
IFACEMETHODIMP ToastEventHandler::Invoke(
winui::Notifications::IToastNotification* sender,
winui::Notifications::IToastDismissedEventArgs* e) {
winui::Notifications::ToastDismissalReason reason;
std::string reason_string;
if (SUCCEEDED(e->get_Reason(&reason))) {
switch (reason) {
case winui::Notifications::ToastDismissalReason_UserCanceled:
reason_string = "userCanceled";
break;
case winui::Notifications::ToastDismissalReason_ApplicationHidden:
reason_string = "applicationHidden";
break;
case winui::Notifications::ToastDismissalReason_TimedOut:
reason_string = "timedOut";
break;
}
}
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&Notification::NotificationDismissed,
notification_, false));
notification_, false, reason_string));
DebugLog("Notification dismissed");
return S_OK;
}

View File

@@ -14,6 +14,7 @@
#include "base/win/scoped_handle.h"
#include "sandbox/win/src/nt_internals.h"
#include "sandbox/win/src/win_utils.h"
#include "shell/common/command_line_util_win.h"
namespace relauncher::internal {
@@ -50,49 +51,6 @@ HANDLE GetParentProcessHandle(base::ProcessHandle handle) {
return ::OpenProcess(PROCESS_ALL_ACCESS, TRUE, ppid);
}
StringType AddQuoteForArg(const StringType& arg) {
// We follow the quoting rules of CommandLineToArgvW.
// http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
std::wstring quotable_chars(L" \\\"");
if (arg.find_first_of(quotable_chars) == std::wstring::npos) {
// No quoting necessary.
return arg;
}
std::wstring out;
out.push_back(L'"');
for (size_t i = 0; i < arg.size(); ++i) {
if (arg[i] == '\\') {
// Find the extent of this run of backslashes.
size_t start = i, end = start + 1;
for (; end < arg.size() && arg[end] == '\\'; ++end) {
}
size_t backslash_count = end - start;
// Backslashes are escapes only if the run is followed by a double quote.
// Since we also will end the string with a double quote, we escape for
// either a double quote or the end of the string.
if (end == arg.size() || arg[end] == '"') {
// To quote, we need to output 2x as many backslashes.
backslash_count *= 2;
}
for (size_t j = 0; j < backslash_count; ++j)
out.push_back('\\');
// Advance i to one before the end to balance i++ in loop.
i = end - 1;
} else if (arg[i] == '"') {
out.push_back('\\');
out.push_back('"');
} else {
out.push_back(arg[i]);
}
}
out.push_back('"');
return out;
}
} // namespace
StringType GetWaitEventName(base::ProcessId pid) {
@@ -105,7 +63,7 @@ StringType ArgvToCommandLineString(const StringVector& argv) {
for (const StringType& arg : argv) {
if (!command_line.empty())
command_line += L' ';
command_line += AddQuoteForArg(arg);
command_line += electron::AddQuoteForArg(arg);
}
return command_line;
}

View File

@@ -270,34 +270,10 @@ void Relaunch(NSString* destinationPath) {
}
bool Trash(NSString* path) {
bool result = false;
if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_8) {
result = [[NSFileManager defaultManager]
trashItemAtURL:[NSURL fileURLWithPath:path]
resultingItemURL:nil
error:nil];
}
// As a last resort try trashing with AppleScript.
// This allows us to trash the app in macOS Sierra even when the app is
// running inside an app translocation image.
if (!result) {
auto* code = R"str(
set theFile to POSIX file "%@"
tell application "Finder"
move theFile to trash
end tell
)str";
NSAppleScript* appleScript = [[NSAppleScript alloc]
initWithSource:[NSString stringWithFormat:@(code), path]];
NSDictionary* errorDict = nil;
NSAppleEventDescriptor* scriptResult =
[appleScript executeAndReturnError:&errorDict];
result = (scriptResult != nil);
}
return result;
return [[NSFileManager defaultManager]
trashItemAtURL:[NSURL fileURLWithPath:path]
resultingItemURL:nil
error:nil];
}
bool DeleteOrTrash(NSString* path) {

View File

@@ -343,6 +343,10 @@ InspectableWebContents::InspectableWebContents(
}
InspectableWebContents::~InspectableWebContents() {
// Explicitly destroy the view first to ensure that any callbacks triggered
// during view teardown (like NonClientHitTest) does not access a
// partially-destroyed view.
view_.reset();
// Unsubscribe from devtools and Clean up resources.
if (GetDevToolsWebContents())
WebContentsDestroyed();

View File

@@ -0,0 +1,54 @@
// Copyright (c) 2026 Microsoft GmbH.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/common/command_line_util_win.h"
#include <string>
namespace electron {
std::wstring AddQuoteForArg(const std::wstring& arg) {
// We follow the quoting rules of CommandLineToArgvW.
// http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
constexpr wchar_t kQuotableChars[] = L" \\\"";
if (arg.find_first_of(kQuotableChars) == std::wstring::npos) {
// No quoting necessary.
return arg;
}
std::wstring out;
out.push_back(L'"');
for (size_t i = 0; i < arg.size(); ++i) {
if (arg[i] == '\\') {
// Find the extent of this run of backslashes.
size_t start = i, end = start + 1;
for (; end < arg.size() && arg[end] == '\\'; ++end) {
}
size_t backslash_count = end - start;
// Backslashes are escapes only if the run is followed by a double quote.
// Since we also will end the string with a double quote, we escape for
// either a double quote or the end of the string.
if (end == arg.size() || arg[end] == '"') {
// To quote, we need to output 2x as many backslashes.
backslash_count *= 2;
}
for (size_t j = 0; j < backslash_count; ++j)
out.push_back('\\');
// Advance i to one before the end to balance i++ in loop.
i = end - 1;
} else if (arg[i] == '"') {
out.push_back('\\');
out.push_back('"');
} else {
out.push_back(arg[i]);
}
}
out.push_back('"');
return out;
}
} // namespace electron

View File

@@ -0,0 +1,20 @@
// Copyright (c) 2026 Microsoft GmbH.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ELECTRON_SHELL_COMMON_COMMAND_LINE_UTIL_WIN_H_
#define ELECTRON_SHELL_COMMON_COMMAND_LINE_UTIL_WIN_H_
#include <string>
namespace electron {
// Quotes |arg| using CommandLineToArgvW-compatible quoting rules so that
// the argument round-trips correctly through CreateProcess →
// CommandLineToArgvW. If no quoting is necessary the string is returned
// unchanged. See http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
std::wstring AddQuoteForArg(const std::wstring& arg);
} // namespace electron
#endif // ELECTRON_SHELL_COMMON_COMMAND_LINE_UTIL_WIN_H_

View File

@@ -56,29 +56,6 @@ ifdescribe(!process.mas)('autoUpdater module', function () {
).to.throw('Expected options object to contain a \'url\' string property in setFeedUrl call');
});
});
ifdescribe(process.platform === 'darwin' && process.arch !== 'arm64')('on Mac', function () {
it('emits an error when the application is unsigned', async () => {
const errorEvent = once(autoUpdater, 'error') as Promise<[Error]>;
autoUpdater.setFeedURL({ url: '' });
const [error] = await errorEvent;
expect(error.message).equal('Could not get code signature for running application');
});
it('does not throw if default is the serverType', () => {
// "Could not get code signature..." means the function got far enough to validate that serverType was OK.
expect(() => autoUpdater.setFeedURL({ url: '', serverType: 'default' })).to.throw('Could not get code signature for running application');
});
it('does not throw if json is the serverType', () => {
// "Could not get code signature..." means the function got far enough to validate that serverType was OK.
expect(() => autoUpdater.setFeedURL({ url: '', serverType: 'json' })).to.throw('Could not get code signature for running application');
});
it('does throw if an unknown string is the serverType', () => {
expect(() => autoUpdater.setFeedURL({ url: '', serverType: 'weow' as any })).to.throw('Expected serverType to be \'default\' or \'json\'');
});
});
});
describe('quitAndInstall', () => {

View File

@@ -12,7 +12,7 @@ import { AddressInfo } from 'node:net';
import * as os from 'node:os';
import * as path from 'node:path';
import { copyMacOSFixtureApp, getCodesignIdentity, shouldRunCodesignTests, signApp, spawn } from './lib/codesign-helpers';
import { copyMacOSFixtureApp, getCodesignIdentity, shouldRunCodesignTests, signApp, spawn, unsignApp } from './lib/codesign-helpers';
import { withTempDirectory } from './lib/fs-helpers';
import { ifdescribe, ifit } from './lib/spec-helpers';
@@ -146,6 +146,7 @@ ifdescribe(shouldRunCodesignTests)('autoUpdater behavior', function () {
ifit(process.arch !== 'arm64')('should fail to set the feed URL when the app is not signed', async () => {
await withTempDirectory(async (dir) => {
const appPath = await copyMacOSFixtureApp(dir);
await unsignApp(appPath);
const launchResult = await launchApp(appPath, ['http://myupdate']);
console.log(launchResult);
expect(launchResult.code).to.equal(1);
@@ -153,6 +154,35 @@ ifdescribe(shouldRunCodesignTests)('autoUpdater behavior', function () {
});
});
ifit(process.arch !== 'arm64')('should fail with code signature error when serverType is default and app is unsigned', async () => {
await withTempDirectory(async (dir) => {
const appPath = await copyMacOSFixtureApp(dir);
await unsignApp(appPath);
const launchResult = await launchApp(appPath, ['', 'default']);
expect(launchResult.code).to.equal(1);
expect(launchResult.out).to.include('Could not get code signature for running application');
});
});
ifit(process.arch !== 'arm64')('should fail with code signature error when serverType is json and app is unsigned', async () => {
await withTempDirectory(async (dir) => {
const appPath = await copyMacOSFixtureApp(dir);
await unsignApp(appPath);
const launchResult = await launchApp(appPath, ['', 'json']);
expect(launchResult.code).to.equal(1);
expect(launchResult.out).to.include('Could not get code signature for running application');
});
});
ifit(process.arch !== 'arm64')('should fail with serverType error when an invalid serverType is provided', async () => {
await withTempDirectory(async (dir) => {
const appPath = await copyMacOSFixtureApp(dir);
const launchResult = await launchApp(appPath, ['', 'weow']);
expect(launchResult.code).to.equal(1);
expect(launchResult.out).to.include("Expected serverType to be 'default' or 'json'");
});
});
it('should cleanly set the feed URL when the app is signed', async () => {
await withTempDirectory(async (dir) => {
const appPath = await copyMacOSFixtureApp(dir);

View File

@@ -6,13 +6,18 @@ process.on('uncaughtException', (err) => {
const { autoUpdater } = require('electron');
const feedUrl = process.argv[1];
const serverType = process.argv[2];
console.log('Setting Feed URL');
autoUpdater.setFeedURL({
url: feedUrl
});
const opts = { url: feedUrl };
if (serverType) {
opts.serverType = serverType;
}
autoUpdater.setFeedURL(opts);
console.log('Feed URL Set:', feedUrl);
console.log('Server Type Set:', serverType);
process.exit(0);

View File

@@ -0,0 +1,26 @@
const { app, BrowserWindow } = require('electron');
if (process.platform !== 'win32') {
process.exit(0);
}
app.on('window-all-closed', () => {
app.quit();
});
app.whenReady().then(() => {
let win = new BrowserWindow({
frame: false,
show: true
});
win.loadURL('about:blank');
setTimeout(() => {
win.close();
}, 5000);
win.on('closed', () => {
win = null;
});
});

View File

@@ -7,11 +7,8 @@ import * as path from 'node:path';
const features = process._linkedBinding('electron_common_features');
const fixturesPath = path.resolve(__dirname, '..', 'fixtures');
// Re-enable codesign tests for macOS x64
// Refs https://github.com/electron/electron/issues/48182
export const shouldRunCodesignTests =
process.platform === 'darwin' &&
!(process.env.CI) &&
!process.mas &&
!features.isComponentBuild();
@@ -83,3 +80,7 @@ export function spawn (cmd: string, args: string[], opts: any = {}) {
export function signApp (appPath: string, identity: string) {
return spawn('codesign', ['-s', identity, '--deep', '--force', appPath]);
};
export function unsignApp (appPath: string) {
return spawn('codesign', ['--remove-signature', '--deep', appPath]);
};