mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
test: add desktopCapturer icon validation (#50261)
* chore: testing of desktopCapturer can run on arm * fix: DesktopMediaListCaptureThread crash Fixed a crash when Windows calls ::CoCreateInstance() in the DesktopMediaListCaptureThread before COM is initialized. * test: added test for desktopCapturer fetchWindowIcons * chore: updating Chromium patch hash --------- Co-authored-by: Charles Kerr <charles@charleskerr.com>
This commit is contained in:
@@ -150,3 +150,4 @@ fix_use_fresh_lazynow_for_onendworkitemimpl_after_didruntask.patch
|
||||
fix_pulseaudio_stream_and_icon_names.patch
|
||||
fix_fire_menu_popup_start_for_dynamically_created_aria_menus.patch
|
||||
feat_allow_enabling_extensions_on_custom_protocols.patch
|
||||
fix_initialize_com_on_desktopmedialistcapturethread_on_windows.patch
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Charles Kerr <charles@charleskerr.com>
|
||||
Date: Tue, 24 Mar 2026 16:33:20 -0500
|
||||
Subject: fix: initialize COM on DesktopMediaListCaptureThread on Windows
|
||||
|
||||
On Windows, the DesktopMediaListCaptureThread runs with a UI message
|
||||
pump that doesn't initialize COM. When WebRTC's window capturer
|
||||
enumerates windows, interacting with UWP/Metro windows causes twinapi.dll
|
||||
to internally call ::CoCreateInstance(), which hits Chromium's
|
||||
DCheckedCoCreateInstance hook and triggers a crash.
|
||||
|
||||
This patch fixes the crash by ensuring COM is initialized on the
|
||||
capture thread by calling `init_com_with_mta(false)`.
|
||||
|
||||
diff --git a/chrome/browser/media/webrtc/native_desktop_media_list.cc b/chrome/browser/media/webrtc/native_desktop_media_list.cc
|
||||
index 9a8ebb4edfb92d9fe28ae4b87463a68547ea1ab3..13446d9849c54f1bfe515c3db4d69dd181ec6d39 100644
|
||||
--- a/chrome/browser/media/webrtc/native_desktop_media_list.cc
|
||||
+++ b/chrome/browser/media/webrtc/native_desktop_media_list.cc
|
||||
@@ -786,6 +786,13 @@ NativeDesktopMediaList::NativeDesktopMediaList(
|
||||
base::MessagePumpType thread_type = base::MessagePumpType::UI;
|
||||
#else
|
||||
base::MessagePumpType thread_type = base::MessagePumpType::DEFAULT;
|
||||
+#endif
|
||||
+#if BUILDFLAG(IS_WIN)
|
||||
+ // On Windows, window enumeration via webrtc::DesktopCapturer may interact
|
||||
+ // with UWP/Metro windows through twinapi.dll, which internally calls
|
||||
+ // CoCreateInstance. Initialize COM on this thread to prevent crashes in
|
||||
+ // Chromium's DCheckedCoCreateInstance hook.
|
||||
+ thread_.init_com_with_mta(false);
|
||||
#endif
|
||||
thread_.StartWithOptions(base::Thread::Options(thread_type, 0));
|
||||
|
||||
@@ -15,7 +15,7 @@ function getSourceTypes (): ('window' | 'screen')[] {
|
||||
return ['window', 'screen'];
|
||||
}
|
||||
|
||||
ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('desktopCapturer', () => {
|
||||
describe('desktopCapturer', () => {
|
||||
it('should return a non-empty array of sources', async () => {
|
||||
const sources = await desktopCapturer.getSources({ types: getSourceTypes() });
|
||||
expect(sources).to.be.an('array').that.is.not.empty();
|
||||
@@ -254,4 +254,61 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
|
||||
destroyWindows();
|
||||
}
|
||||
});
|
||||
|
||||
// Linux doesn't return any window sources.
|
||||
ifdescribe(process.platform !== 'linux')('fetchWindowIcons', function () {
|
||||
// Tests are sequentially dependent
|
||||
this.bail(true);
|
||||
let w: BrowserWindow;
|
||||
let testSource: Electron.DesktopCapturerSource | undefined;
|
||||
let appIcon: Electron.NativeImage | undefined;
|
||||
|
||||
before(async () => {
|
||||
w = new BrowserWindow({
|
||||
width: 200,
|
||||
height: 200,
|
||||
show: true,
|
||||
title: 'desktop-capturer-test-window'
|
||||
});
|
||||
await w.loadURL('about:blank');
|
||||
const sources = await desktopCapturer.getSources({
|
||||
types: ['window'],
|
||||
fetchWindowIcons: true
|
||||
});
|
||||
testSource = sources.find(
|
||||
s => s.name === 'desktop-capturer-test-window'
|
||||
);
|
||||
appIcon = testSource?.appIcon;
|
||||
});
|
||||
|
||||
after(() => {
|
||||
if (w) w.destroy();
|
||||
});
|
||||
|
||||
it('should find the test window in the list of captured sources', () => {
|
||||
expect(testSource, `The ${w.getTitle()} window was not found by desktopCapturer`).to.exist();
|
||||
});
|
||||
|
||||
it('should return a non-null appIcon for the captured window', () => {
|
||||
expect(appIcon, 'appIcon property is null or undefined').to.exist();
|
||||
});
|
||||
|
||||
it('should return an appIcon that is not an empty image', () => {
|
||||
expect(appIcon?.isEmpty()).to.be.false();
|
||||
});
|
||||
|
||||
it('should return an appIcon that encodes to a valid PNG data URL', () => {
|
||||
const url = appIcon?.toDataURL();
|
||||
expect(url).to.be.a('string');
|
||||
// This is header 'data:image/png;base64,' length;
|
||||
expect(url?.length).to.be.greaterThan(22);
|
||||
expect(url?.startsWith('data:image/png;base64,')).to.be.true();
|
||||
});
|
||||
|
||||
it('should return an appIcon with dimensions greater than 0x0 pixels', () => {
|
||||
const { width, height } = appIcon?.getSize() || { width: 0, height: 0 };
|
||||
expect(width).to.be.greaterThan(0);
|
||||
expect(height).to.be.greaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user