Compare commits

..

10 Commits

Author SHA1 Message Date
trop[bot]
ad8f2f8d5d test: add tests dbus notification images (#43946)
* test: add tests dbus notification images

Provide a NativeImage icon in the notification tests and then inspect
the DBus message payload's `image_data` hint to see if it's correct.
This adds test coverage for LibnotifyNotification::Show() and for
GdkPixbufFromSkBitmap().

Co-authored-by: Charles Kerr <charles@charleskerr.com>

* chore: use the same notification_icon.png as in main

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-25 16:48:24 -05:00
trop[bot]
53cc8aef9b fix: close all open sheets before closing window on macOS (#43952)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Beutner <beutner.john@gmail.com>
2024-09-25 13:30:56 -05:00
trop[bot]
0ffafbc295 test: ensure sender-pid hint is set in Linux notifications (#43949)
test: expect a `sender-pid` hint in Linux notifications.

This PR ensures that the `sender-pid` hint is set for new notifications.
It also updates the spec to confirm that DBus receives the hint and that
it has the correct value.

This fixes a spec failure when running libnotify >= 0.7.12 (2022-05-05).
Starting with that version, libnotify started injecting `sender-pid` if
not provided by the client. So our tests received a slightly different
DBus payload depending on what version of libnotify was installed,
causing our deep-equals tests to fail.

By always providing and testing the `sender-pid` hint, our behavior and
tests should be consistent across distros.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-25 13:09:32 -05:00
trop[bot]
134176a1d1 fix: detach native view when its removed from parent on macOS (#43922)
Right now DelayedNativeViewHost attaches its underlying native view
when it's being attached to a widget but it doesn't detach it when
it's being detached. It may lead to use-after-free and crash.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Cezary Kulakowski <cezary@openfin.co>
2024-09-25 13:17:59 +02:00
trop[bot]
d2bd4cf91c fix: crash when focusing WebView webContents (#43932)
fix: crash when focusing WebView

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2024-09-25 06:16:49 -05:00
trop[bot]
1af736bc15 fix: -Wunsafe-buffer-usage warning in asar_util's ReadFileToString() (#43930)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-25 11:21:47 +02:00
trop[bot]
f609d77e29 refactor: add SerialChooserController::web_contents_ (#43920)
Add a `base::WeakPtr<WebContents>` field to SerialChooserController and
stop subclassing from WebContentsObserver. This follows the Observer docs:

> don't create a `WebContentsObserver` just to be able to check
> for a null `WebContentsObserver::web_contents()`.
> Use a `base::WeakPtr<WebContents>` instead.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-24 08:26:29 -05:00
trop[bot]
ae6d5c4661 refactor: hide printing impl details in api::WebContents (#43917)
* refactor: move api::WebContents::OnGetDeviceNameToUse() into an anonymous namespace

Co-authored-by: Charles Kerr <charles@charleskerr.com>

* refactor: move api::WebContents::OnPDFCreated() into an anonymous namespace

Co-authored-by: Charles Kerr <charles@charleskerr.com>

* refactor: remove unused #include

Co-authored-by: Charles Kerr <charles@charleskerr.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-24 10:11:56 +02:00
trop[bot]
cdcfe49592 refactor: prefer member initializers in asar structs (#43918)
prefactor: prefer member initializers in asar::Archive

prefactor: prefer member initializers in asar::Archive::FileInfo

prefactor: prefer member initializers in asar::IntegrityPayload

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-24 10:11:50 +02:00
trop[bot]
d35b848f95 refactor: remove C-style void arg type for no-arg functions (#43919)
A small cleanup to remove use of the C-style function declaration idiom.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-24 00:36:20 -07:00
18 changed files with 132 additions and 101 deletions

View File

@@ -75,6 +75,7 @@ if (is_linux) {
"notify_notification_set_timeout",
"notify_notification_set_urgency",
"notify_notification_set_hint_string",
"notify_notification_set_hint",
"notify_notification_show",
"notify_notification_close",
]

View File

@@ -2892,14 +2892,16 @@ bool WebContents::IsCurrentlyAudible() {
}
#if BUILDFLAG(ENABLE_PRINTING)
void WebContents::OnGetDeviceNameToUse(
base::Value::Dict print_settings,
printing::CompletionCallback print_callback,
// <error, device_name>
std::pair<std::string, std::u16string> info) {
namespace {
void OnGetDeviceNameToUse(base::WeakPtr<content::WebContents> web_contents,
base::Value::Dict print_settings,
printing::CompletionCallback print_callback,
// <error, device_name>
std::pair<std::string, std::u16string> info) {
// The content::WebContents might be already deleted at this point, and the
// PrintViewManagerElectron class does not do null check.
if (!web_contents()) {
if (!web_contents) {
if (print_callback)
std::move(print_callback).Run(false, "failed");
return;
@@ -2921,15 +2923,40 @@ void WebContents::OnGetDeviceNameToUse(
}
auto* print_view_manager =
PrintViewManagerElectron::FromWebContents(web_contents());
PrintViewManagerElectron::FromWebContents(web_contents.get());
if (!print_view_manager)
return;
content::RenderFrameHost* rfh = GetRenderFrameHostToUse(web_contents());
content::RenderFrameHost* rfh = GetRenderFrameHostToUse(web_contents.get());
print_view_manager->PrintNow(rfh, std::move(print_settings),
std::move(print_callback));
}
void OnPDFCreated(gin_helper::Promise<v8::Local<v8::Value>> promise,
print_to_pdf::PdfPrintResult print_result,
scoped_refptr<base::RefCountedMemory> data) {
if (print_result != print_to_pdf::PdfPrintResult::kPrintSuccess) {
promise.RejectWithErrorMessage(
"Failed to generate PDF: " +
print_to_pdf::PdfPrintResultToString(print_result));
return;
}
v8::Isolate* isolate = promise.isolate();
gin_helper::Locker locker(isolate);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(
v8::Local<v8::Context>::New(isolate, promise.GetContext()));
v8::Local<v8::Value> buffer =
node::Buffer::Copy(isolate, reinterpret_cast<const char*>(data->front()),
data->size())
.ToLocalChecked();
promise.Resolve(buffer);
}
} // namespace
void WebContents::Print(gin::Arguments* args) {
auto options = gin_helper::Dictionary::CreateEmpty(args->isolate());
base::Value::Dict settings;
@@ -3089,9 +3116,8 @@ void WebContents::Print(gin::Arguments* args) {
print_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE, base::BindOnce(&GetDeviceNameToUse, device_name),
base::BindOnce(&WebContents::OnGetDeviceNameToUse,
weak_factory_.GetWeakPtr(), std::move(settings),
std::move(callback)));
base::BindOnce(&OnGetDeviceNameToUse, web_contents()->GetWeakPtr(),
std::move(settings), std::move(callback)));
}
// Partially duplicated and modified from
@@ -3149,36 +3175,10 @@ v8::Local<v8::Promise> WebContents::PrintToPDF(const base::Value& settings) {
params->params->document_cookie = unique_id.value_or(0);
manager->PrintToPdf(rfh, page_ranges, std::move(params),
base::BindOnce(&WebContents::OnPDFCreated, GetWeakPtr(),
std::move(promise)));
base::BindOnce(&OnPDFCreated, std::move(promise)));
return handle;
}
void WebContents::OnPDFCreated(
gin_helper::Promise<v8::Local<v8::Value>> promise,
print_to_pdf::PdfPrintResult print_result,
scoped_refptr<base::RefCountedMemory> data) {
if (print_result != print_to_pdf::PdfPrintResult::kPrintSuccess) {
promise.RejectWithErrorMessage(
"Failed to generate PDF: " +
print_to_pdf::PdfPrintResultToString(print_result));
return;
}
v8::Isolate* isolate = promise.isolate();
gin_helper::Locker locker(isolate);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(
v8::Local<v8::Context>::New(isolate, promise.GetContext()));
v8::Local<v8::Value> buffer =
node::Buffer::Copy(isolate, reinterpret_cast<const char*>(data->front()),
data->size())
.ToLocalChecked();
promise.Resolve(buffer);
}
#endif
void WebContents::AddWorkSpace(gin::Arguments* args,
@@ -3320,6 +3320,12 @@ void WebContents::Focus() {
if (owner_window())
owner_window()->Focus(true);
#endif
// WebView uses WebContentsViewChildFrame, which doesn't have a Focus impl
// and triggers a fatal NOTREACHED.
if (is_guest())
return;
web_contents()->Focus();
}
@@ -3748,7 +3754,8 @@ void WebContents::SetBackgroundColor(std::optional<SkColor> maybe_color) {
content::RenderWidgetHostView* rwhv = rfh->GetView();
if (rwhv) {
// RenderWidgetHostView doesn't allow setting an alpha that's not 0 or 255.
// RenderWidgetHostView doesn't allow setting an alpha that's not 0 or
// 255.
rwhv->SetBackgroundColor(is_opaque ? color : SK_ColorTRANSPARENT);
static_cast<content::RenderWidgetHostViewBase*>(rwhv)
->SetContentBackgroundColor(color);

View File

@@ -45,10 +45,6 @@
#include "shell/common/gin_helper/pinnable.h"
#include "ui/base/models/image_model.h"
#if BUILDFLAG(ENABLE_PRINTING)
#include "shell/browser/printing/print_view_manager_electron.h"
#endif
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
#include "extensions/common/mojom/view_type.mojom-forward.h"
@@ -246,16 +242,9 @@ class WebContents final : public ExclusiveAccessContext,
void HandleNewRenderFrame(content::RenderFrameHost* render_frame_host);
#if BUILDFLAG(ENABLE_PRINTING)
void OnGetDeviceNameToUse(base::Value::Dict print_settings,
printing::CompletionCallback print_callback,
// <error, device_name>
std::pair<std::string, std::u16string> info);
void Print(gin::Arguments* args);
// Print current page as PDF.
v8::Local<v8::Promise> PrintToPDF(const base::Value& settings);
void OnPDFCreated(gin_helper::Promise<v8::Local<v8::Value>> promise,
print_to_pdf::PdfPrintResult print_result,
scoped_refptr<base::RefCountedMemory> data);
#endif
void SetNextChildWebPreferences(const gin_helper::Dictionary);

View File

@@ -9,6 +9,7 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "device/bluetooth/bluetooth_device.h"

View File

@@ -352,8 +352,11 @@ void NativeWindowMac::Close() {
// [window_ performClose:nil], the window won't close properly
// even after the user has ended the sheet.
// Ensure it's closed before calling [window_ performClose:nil].
if ([window_ attachedSheet])
// If multiple sheets are open, they must all be closed.
while ([window_ attachedSheet]) {
[window_ endSheet:[window_ attachedSheet]];
}
DCHECK_EQ([[window_ sheets] count], 0UL);
// window_ could be nil after performClose.
bool should_notify = is_modal() && parent() && IsVisible();

View File

@@ -10,6 +10,7 @@
#include "base/files/file_enumerator.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/process/process_handle.h"
#include "base/strings/utf_string_conversions.h"
#include "shell/browser/notifications/notification_delegate.h"
#include "shell/browser/ui/gtk_util.h"
@@ -145,6 +146,10 @@ void LibnotifyNotification::Show(const NotificationOptions& options) {
notification_, "desktop-entry", desktop_id.c_str());
}
libnotify_loader_.notify_notification_set_hint(
notification_, "sender-pid",
g_variant_new_int64(base::GetCurrentProcId()));
GError* error = nullptr;
libnotify_loader_.notify_notification_show(notification_, &error);
if (error) {

View File

@@ -87,17 +87,17 @@ class OffScreenRenderWidgetHostView
void InitAsChild(gfx::NativeView) override;
void SetSize(const gfx::Size&) override;
void SetBounds(const gfx::Rect&) override;
gfx::NativeView GetNativeView(void) override;
gfx::NativeViewAccessible GetNativeViewAccessible(void) override;
gfx::NativeView GetNativeView() override;
gfx::NativeViewAccessible GetNativeViewAccessible() override;
ui::TextInputClient* GetTextInputClient() override;
void Focus() override {}
bool HasFocus() override;
uint32_t GetCaptureSequenceNumber() const override;
bool IsSurfaceAvailableForCopy(void) override;
void Hide(void) override;
bool IsShowing(void) override;
bool IsSurfaceAvailableForCopy() override;
void Hide() override;
bool IsShowing() override;
void EnsureSurfaceSynchronizedForWebTest() override;
gfx::Rect GetViewBounds(void) override;
gfx::Rect GetViewBounds() override;
gfx::Size GetVisibleViewportSize() override;
void SetInsets(const gfx::Insets&) override {}
void SetBackgroundColor(SkColor color) override;
@@ -107,7 +107,7 @@ class OffScreenRenderWidgetHostView
bool request_unadjusted_movement) override;
blink::mojom::PointerLockResult ChangePointerLock(
bool request_unadjusted_movement) override;
void UnlockPointer(void) override {}
void UnlockPointer() override {}
void TakeFallbackContentFrom(content::RenderWidgetHostView* view) override;
#if BUILDFLAG(IS_MAC)
void SetActive(bool active) override {}
@@ -135,10 +135,10 @@ class OffScreenRenderWidgetHostView
void SetIsLoading(bool is_loading) override {}
void TextInputStateChanged(const ui::mojom::TextInputState& params) override {
}
void ImeCancelComposition(void) override {}
void ImeCancelComposition() override {}
void RenderProcessGone() override;
void ShowWithVisibility(content::PageVisibilityState page_visibility) final;
void Destroy(void) override;
void Destroy() override;
void UpdateTooltipUnderCursor(const std::u16string&) override {}
input::CursorManager* GetCursorManager() override;
void CopyFromSurface(
@@ -147,7 +147,7 @@ class OffScreenRenderWidgetHostView
base::OnceCallback<void(const SkBitmap&)> callback) override;
display::ScreenInfo GetScreenInfo() const override;
void TransformPointToRootSurface(gfx::PointF* point) override {}
gfx::Rect GetBoundsInRootWindow(void) override;
gfx::Rect GetBoundsInRootWindow() override;
std::optional<content::DisplayFeature> GetDisplayFeature() override;
void SetDisplayFeatureForTesting(
const content::DisplayFeature* display_feature) override {}

View File

@@ -110,17 +110,18 @@ SerialChooserController::SerialChooserController(
content::SerialChooser::Callback callback,
content::WebContents* web_contents,
base::WeakPtr<ElectronSerialDelegate> serial_delegate)
: WebContentsObserver(web_contents),
: web_contents_{web_contents ? web_contents->GetWeakPtr()
: base::WeakPtr<content::WebContents>()},
filters_(std::move(filters)),
allowed_bluetooth_service_class_ids_(
std::move(allowed_bluetooth_service_class_ids)),
callback_(std::move(callback)),
serial_delegate_(serial_delegate),
render_frame_host_id_(render_frame_host->GetGlobalId()) {
origin_ = web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin();
origin_ = web_contents_->GetPrimaryMainFrame()->GetLastCommittedOrigin();
chooser_context_ = SerialChooserContextFactory::GetForBrowserContext(
web_contents->GetBrowserContext())
web_contents_->GetBrowserContext())
->AsWeakPtr();
DCHECK(chooser_context_);
chooser_context_->GetPortManager()->GetDevices(base::BindOnce(
@@ -133,10 +134,10 @@ SerialChooserController::~SerialChooserController() {
}
api::Session* SerialChooserController::GetSession() {
if (!web_contents()) {
if (!web_contents_) {
return nullptr;
}
return api::Session::FromBrowserContext(web_contents()->GetBrowserContext());
return api::Session::FromBrowserContext(web_contents_->GetBrowserContext());
}
void SerialChooserController::OnPortAdded(
@@ -148,7 +149,7 @@ void SerialChooserController::OnPortAdded(
api::Session* session = GetSession();
if (session) {
session->Emit("serial-port-added", port.Clone(), web_contents());
session->Emit("serial-port-added", port.Clone(), web_contents_.get());
}
}
@@ -157,10 +158,8 @@ void SerialChooserController::OnPortRemoved(
const auto it = std::ranges::find(ports_, port.token,
&device::mojom::SerialPortInfo::token);
if (it != ports_.end()) {
api::Session* session = GetSession();
if (session) {
session->Emit("serial-port-removed", port.Clone(), web_contents());
}
if (api::Session* session = GetSession())
session->Emit("serial-port-removed", port.Clone(), web_contents_.get());
ports_.erase(it);
}
}
@@ -203,10 +202,9 @@ void SerialChooserController::OnGetDevices(
}
bool prevent_default = false;
api::Session* session = GetSession();
if (session) {
if (api::Session* session = GetSession()) {
prevent_default = session->Emit(
"select-serial-port", ports_, web_contents(),
"select-serial-port", ports_, web_contents_.get(),
base::BindRepeating(&SerialChooserController::OnDeviceChosen,
weak_factory_.GetWeakPtr()));
}

View File

@@ -12,7 +12,6 @@
#include "base/scoped_observation.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/serial_chooser.h"
#include "content/public/browser/web_contents_observer.h"
#include "services/device/public/mojom/serial.mojom-forward.h"
#include "shell/browser/serial/serial_chooser_context.h"
#include "third_party/blink/public/mojom/serial/serial.mojom-forward.h"
@@ -32,8 +31,7 @@ class ElectronSerialDelegate;
// SerialChooserController provides data for the Serial API permission prompt.
class SerialChooserController final
: private SerialChooserContext::PortObserver,
private content::WebContentsObserver {
: private SerialChooserContext::PortObserver {
public:
SerialChooserController(
content::RenderFrameHost* render_frame_host,
@@ -64,6 +62,8 @@ class SerialChooserController final
void RunCallback(device::mojom::SerialPortInfoPtr port);
void OnDeviceChosen(const std::string& port_id);
base::WeakPtr<content::WebContents> web_contents_;
std::vector<blink::mojom::SerialPortFilterPtr> filters_;
std::vector<::device::BluetoothUUID> allowed_bluetooth_service_class_ids_;
content::SerialChooser::Callback callback_;

View File

@@ -15,6 +15,8 @@ DelayedNativeViewHost::~DelayedNativeViewHost() = default;
void DelayedNativeViewHost::ViewHierarchyChanged(
const views::ViewHierarchyChangedDetails& details) {
if (!details.is_add && native_view())
Detach();
NativeViewHost::ViewHierarchyChanged(details);
if (details.is_add && GetWidget() && !native_view())
Attach(native_view_);

View File

@@ -156,17 +156,14 @@ bool FillFileInfoWithNode(Archive::FileInfo* info,
} // namespace
IntegrityPayload::IntegrityPayload()
: algorithm(HashAlgorithm::kNone), block_size(0) {}
IntegrityPayload::IntegrityPayload() = default;
IntegrityPayload::~IntegrityPayload() = default;
IntegrityPayload::IntegrityPayload(const IntegrityPayload& other) = default;
Archive::FileInfo::FileInfo()
: unpacked(false), executable(false), size(0), offset(0) {}
Archive::FileInfo::FileInfo() = default;
Archive::FileInfo::~FileInfo() = default;
Archive::Archive(const base::FilePath& path)
: initialized_(false), path_(path), file_(base::File::FILE_OK) {
Archive::Archive(const base::FilePath& path) : path_{path} {
electron::ScopedAllowBlockingForElectron allow_blocking;
file_.Initialize(path_, base::File::FLAG_OPEN | base::File::FLAG_READ);
#if BUILDFLAG(IS_WIN)

View File

@@ -31,9 +31,9 @@ struct IntegrityPayload {
IntegrityPayload();
~IntegrityPayload();
IntegrityPayload(const IntegrityPayload& other);
HashAlgorithm algorithm;
HashAlgorithm algorithm = HashAlgorithm::kNone;
std::string hash;
uint32_t block_size;
uint32_t block_size = 0U;
std::vector<std::string> blocks;
};
@@ -44,10 +44,10 @@ class Archive {
struct FileInfo {
FileInfo();
~FileInfo();
bool unpacked;
bool executable;
uint32_t size;
uint64_t offset;
bool unpacked = false;
bool executable = false;
uint32_t size = 0U;
uint64_t offset = 0U;
std::optional<IntegrityPayload> integrity;
};
@@ -100,10 +100,10 @@ class Archive {
base::FilePath path() const { return path_; }
private:
bool initialized_;
bool initialized_ = false;
bool header_validated_ = false;
const base::FilePath path_;
base::File file_;
base::File file_{base::File::FILE_OK};
int fd_ = -1;
uint32_t header_size_ = 0;
std::optional<base::Value::Dict> header_;

View File

@@ -126,11 +126,8 @@ bool ReadFileToString(const base::FilePath& path, std::string* contents) {
return false;
contents->resize(info.size);
if (static_cast<int>(info.size) !=
src.Read(info.offset, const_cast<char*>(contents->data()),
contents->size())) {
if (!src.ReadAndCheck(info.offset, base::as_writable_byte_span(*contents)))
return false;
}
if (info.integrity)
ValidateIntegrityOrDie(base::as_byte_span(*contents), *info.integrity);

View File

@@ -47,11 +47,11 @@ class DeleteFileProgressSink : public IFileOperationProgressSink {
private:
// IFileOperationProgressSink
ULONG STDMETHODCALLTYPE AddRef(void) override;
ULONG STDMETHODCALLTYPE Release(void) override;
ULONG STDMETHODCALLTYPE AddRef() override;
ULONG STDMETHODCALLTYPE Release() override;
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
LPVOID* ppvObj) override;
HRESULT STDMETHODCALLTYPE StartOperations(void) override;
HRESULT STDMETHODCALLTYPE StartOperations() override;
HRESULT STDMETHODCALLTYPE FinishOperations(HRESULT) override;
HRESULT STDMETHODCALLTYPE PreRenameItem(DWORD, IShellItem*, LPCWSTR) override;
HRESULT STDMETHODCALLTYPE
@@ -90,9 +90,9 @@ class DeleteFileProgressSink : public IFileOperationProgressSink {
HRESULT,
IShellItem*) override;
HRESULT STDMETHODCALLTYPE UpdateProgress(UINT, UINT) override;
HRESULT STDMETHODCALLTYPE ResetTimer(void) override;
HRESULT STDMETHODCALLTYPE PauseTimer(void) override;
HRESULT STDMETHODCALLTYPE ResumeTimer(void) override;
HRESULT STDMETHODCALLTYPE ResetTimer() override;
HRESULT STDMETHODCALLTYPE PauseTimer() override;
HRESULT STDMETHODCALLTYPE ResumeTimer() override;
ULONG m_cRef;
};

View File

@@ -116,6 +116,14 @@ describe('BrowserWindow module', () => {
await closed;
});
it('should work if called when multiple messageBoxes are showing', async () => {
const closed = once(w, 'closed');
dialog.showMessageBox(w, { message: 'Hello Error' });
dialog.showMessageBox(w, { message: 'Hello Error' });
w.close();
await closed;
});
it('closes window without rounded corners', async () => {
await closeWindow(w);
w = new BrowserWindow({ show: false, frame: false, roundedCorners: false });

View File

@@ -9,8 +9,12 @@
import { expect } from 'chai';
import * as dbus from 'dbus-native';
import { app } from 'electron/main';
import { nativeImage } from 'electron/common';
import { ifdescribe } from './lib/spec-helpers';
import { promisify } from 'node:util';
import * as path from 'node:path';
const fixturesPath = path.join(__dirname, 'fixtures');
const skip = process.platform !== 'linux' ||
process.arch === 'ia32' ||
@@ -92,6 +96,7 @@ ifdescribe(!skip)('Notification module (dbus)', () => {
title: 'title',
subtitle: 'subtitle',
body: 'body',
icon: nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'notification_icon.png')),
replyPlaceholder: 'replyPlaceholder',
sound: 'sound',
closeButtonText: 'closeButtonText'
@@ -117,7 +122,9 @@ ifdescribe(!skip)('Notification module (dbus)', () => {
actions: [],
hints: {
append: 'true',
image_data: [3, 3, 12, true, 8, 4, Buffer.from([255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 76, 255, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 38, 255, 255, 0, 0, 0, 255, 0, 0, 0, 0])],
'desktop-entry': appName,
'sender-pid': process.pid,
urgency: 1
}
});

View File

@@ -1206,6 +1206,22 @@ describe('webContents module', () => {
expect(currentFocused).to.be.true();
expect(childFocused).to.be.false();
});
it('does not crash when focusing a WebView webContents', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true,
webviewTag: true
}
});
w.show();
await w.loadURL('data:text/html,<webview src="data:text/html,hi"></webview>');
const wc = webContents.getAllWebContents().find((wc) => wc.getType() === 'webview')!;
expect(() => wc.focus()).to.not.throw();
});
});
const moveFocusToDevTools = async (win: BrowserWindow) => {

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B