Repository: electron/electron (language: cpp)

Issue:
[Bug]: setWindowOpenHandler results in incorrect dark mode background color for text/plain content
### Preflight Checklist

- [X] I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/main/CONTRIBUTING.md) for this project.
- [X] I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/main/CODE_OF_CONDUCT.md) that this project adheres to.
- [X] I have searched the [issue tracker](https://www.github.com/electron/electron/issues) for a bug report that matches the one I want to file, without success.

### Electron Version

Electron 21.3.1, 22.0.0, and the latest 24.0.0-nightly.20221201

### What operating system are you using?

macOS 13 and Windows 11

### What arch are you using?

arm64 (including Apple Silicon)

### Last Known Working Electron version

n/a

### Expected Behavior

When the system is in dark mode, and a popup is opened via `window.open` to a URL returning content of type `text/plain`, the resulting popup window should have a black background with white text. This happens correctly unless `mainWindow.webContents.setWindowOpenHandler` is called.

See https://www.loom.com/share/4af414156762410e994b5e2a56d997c7 for the correct behavior when `mainWindow.webContents.setWindowOpenHandler` is commented out.

![image](https://user-images.githubusercontent.com/776117/205159936-c9a7719a-8bad-47e7-84f4-9af693eb08ee.png)


### Actual Behavior

With the call to `mainWindow.webContents.setWindowOpenHandler` uncommented, the popup window incorrectly has white text with a white background. See https://www.loom.com/share/15aadf785f3740c5b8f07239ea1e46ed for the actual behavior.

![image](https://user-images.githubusercontent.com/776117/205159884-fa522568-c10d-4f71-a878-98c3d08b0265.png)


### Testcase Gist URL

https://gist.github.com/36925a2377d0b8c7c67abd9816c2f411


Discussion / hints:
I think this is related to https://github.com/electron/electron/pull/28054. cc @MarshallOfSound 
Text in the opened window first turned white starting in 19.0.0-nightly.20220325.
I'm investigating root cause, but in the mean time, this is a workaround which causes the behavior to be the same whether `setWindowOpenHandler` is set or not:

```js
mainWindow.webContents.setWindowOpenHandler(() => ({action: 'allow', overrideBrowserWindowOptions: { backgroundColor: '000000' }}))
```

The `backgroundColor` value doesn't matter, as it will be immediately replaced by the expected background color for dark mode.

Fix patch:
diff --git a/lib/browser/api/web-contents.ts b/lib/browser/api/web-contents.ts
index 14a9c086acee0..88503a7e72825 100644
--- a/lib/browser/api/web-contents.ts
+++ b/lib/browser/api/web-contents.ts
@@ -519,17 +519,10 @@ WebContents.prototype._callWindowOpenHandler = function (event: Electron.Event,
     event.preventDefault();
     return defaultResponse;
   } else if (response.action === 'allow') {
-    if (typeof response.overrideBrowserWindowOptions === 'object' && response.overrideBrowserWindowOptions !== null) {
-      return {
-        browserWindowConstructorOptions: response.overrideBrowserWindowOptions,
-        outlivesOpener: typeof response.outlivesOpener === 'boolean' ? response.outlivesOpener : false
-      };
-    } else {
-      return {
-        browserWindowConstructorOptions: {},
-        outlivesOpener: typeof response.outlivesOpener === 'boolean' ? response.outlivesOpener : false
-      };
-    }
+    return {
+      browserWindowConstructorOptions: typeof response.overrideBrowserWindowOptions === 'object' ? response.overrideBrowserWindowOptions : null,
+      outlivesOpener: typeof response.outlivesOpener === 'boolean' ? response.outlivesOpener : false
+    };
   } else {
     event.preventDefault();
     console.error('The window open handler response must be an object with an \'action\' property of \'allow\' or \'deny\'.');
diff --git a/shell/browser/web_contents_preferences.cc b/shell/browser/web_contents_preferences.cc
index 4371fb0e7030c..de2bf9953b479 100644
--- a/shell/browser/web_contents_preferences.cc
+++ b/shell/browser/web_contents_preferences.cc
@@ -226,7 +226,7 @@ void WebContentsPreferences::SetFromDictionary(
   // preferences don't save a transparency option,
   // apply any existing transparency setting to background_color_
   bool transparent;
-  if (web_preferences.Get(options::kTransparent, &transparent)) {
+  if (web_preferences.Get(options::kTransparent, &transparent) && transparent) {
     background_color_ = SK_ColorTRANSPARENT;
   }
   std::string background_color;
diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts
index 5dea298430a7d..fa3f74ea5ddcb 100644
--- a/spec/api-browser-window-spec.ts
+++ b/spec/api-browser-window-spec.ts
@@ -5988,7 +5988,7 @@ describe('BrowserWindow module', () => {
     });
 
     // Linux and arm64 platforms (WOA and macOS) do not return any capture sources
-    ifit(process.platform === 'darwin' && process.arch !== 'x64')('should not display a visible background', async () => {
+    ifit(process.platform === 'darwin' && process.arch === 'x64')('should not display a visible background', async () => {
       const display = screen.getPrimaryDisplay();
 
       const backgroundWindow = new BrowserWindow({
@@ -6061,6 +6061,32 @@ describe('BrowserWindow module', () => {
 
       expect(areColorsSimilar(centerColor, HexColors.PURPLE)).to.be.true();
     });
+
+    // Linux and arm64 platforms (WOA and macOS) do not return any capture sources
+    ifit(process.platform === 'darwin' && process.arch === 'x64')('should not make background transparent if falsy', async () => {
+      const display = screen.getPrimaryDisplay();
+
+      for (const transparent of [false, undefined]) {
+        const window = new BrowserWindow({
+          ...display.bounds,
+          transparent
+        });
+
+        await once(window, 'show');
+        await window.webContents.loadURL('data:text/html,<head><meta name="color-scheme" content="dark"></head>');
+
+        await setTimeout(500);
+        const screenCapture = await captureScreen();
+        const centerColor = getPixelColor(screenCapture, {
+          x: display.size.width / 2,
+          y: display.size.height / 2
+        });
+        window.close();
+
+        // color-scheme is set to dark so background should not be white
+        expect(areColorsSimilar(centerColor, HexColors.WHITE)).to.be.false();
+      }
+    });
   });
 
   describe('"backgroundColor" option', () => {
diff --git a/spec/guest-window-manager-spec.ts b/spec/guest-window-manager-spec.ts
index 41968c934ff9d..606de060b426d 100644
--- a/spec/guest-window-manager-spec.ts
+++ b/spec/guest-window-manager-spec.ts
@@ -1,7 +1,10 @@
-import { BrowserWindow } from 'electron';
+import { BrowserWindow, screen } from 'electron';
 import { expect, assert } from 'chai';
+import { areColorsSimilar, captureScreen, HexColors, getPixelColor } from './lib/screen-helpers';
+import { ifit } from './lib/spec-helpers';
 import { closeAllWindows } from './lib/window-helpers';
 import { once } from 'events';
+import { setTimeout as setTimeoutAsync } from 'timers/promises';
 
 describe('webContents.setWindowOpenHandler', () => {
   let browserWindow: BrowserWindow;
@@ -193,4 +196,31 @@ describe('webContents.setWindowOpenHandler', () => {
     browserWindow.webContents.executeJavaScript("window.open('https://127.0.0.1')");
     expect(await browserWindow.webContents.executeJavaScript('42')).to.equal(42);
   });
+
+  // Linux and arm64 platforms (WOA and macOS) do not return any capture sources
+  ifit(process.platform === 'darwin' && process.arch === 'x64')('should not make child window background transparent', (done) => {
+    browserWindow.webContents.setWindowOpenHandler(() => ({ action: 'allow' }));
+
+    browserWindow.webContents.once('did-create-window', async (childWindow) => {
+      const display = screen.getPrimaryDisplay();
+      childWindow.setBounds(display.bounds);
+      await childWindow.webContents.executeJavaScript("const meta = document.createElement('meta'); meta.name = 'color-scheme'; meta.content = 'dark'; document.head.appendChild(meta); true;");
+      await setTimeoutAsync(1000);
+      const screenCapture = await captureScreen();
+      const centerColor = getPixelColor(screenCapture, {
+        x: display.size.width / 2,
+        y: display.size.height / 2
+      });
+
+      try {
+        // color-scheme is set to dark so background should not be white
+        expect(areColorsSimilar(centerColor, HexColors.WHITE)).to.be.false();
+        done();
+      } catch (err) {
+        done(err);
+      }
+    });
+
+    browserWindow.webContents.executeJavaScript("window.open('about:blank') && true");
+  });
 });
diff --git a/spec/lib/screen-helpers.ts b/spec/lib/screen-helpers.ts
index c641b6a49cdc0..4d7665eade0b3 100644
--- a/spec/lib/screen-helpers.ts
+++ b/spec/lib/screen-helpers.ts
@@ -8,7 +8,8 @@ export enum HexColors {
   GREEN = '#00b140',
   PURPLE = '#6a0dad',
   RED = '#ff0000',
-  BLUE = '#0000ff'
+  BLUE = '#0000ff',
+  WHITE = '#ffffff'
 };
 
 /**


Test patch: