Repository: electron/electron
Pull request: fix: apply `zoomFactor` from `setWindowOpenHandler` to `window.open()` windows

#### Description of Change

When a window is opened via `window.open()`, Chromium creates the child `WebContents` internally. Electron wraps it using a constructor that passes an empty options dictionary to `InitZoomController`, so the `zoomFactor` from `overrideBrowserWindowOptions` was never applied.

Fix this by applying `zoomFactor` in `CreateFromWebPreferences` when an existing `webContents` is being wrapped into a `BrowserWindow`. Both the default zoom factor (for future navigations) and the current zoom level (for the already-loaded page) are set.

#### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [x] PR description included
- [x] `npm test` passes
- [x] tests are [changed or added](https://github.com/electron/electron/blob/main/docs/development/testing.md)
- [x] [PR release notes](https://github.com/electron/clerk/blob/main/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/main/README.md#examples).

#### Release Notes

Notes: Fixed an issue where setting `zoomFactor` in `setWindowOpenHandler`'s `overrideBrowserWindowOptions` had no effect on windows opened via `window.open()`.

Changed files:
filename: shell/browser/api/electron_api_web_contents.cc
patch: @@ -4746,6 +4746,19 @@ gin_helper::Handle<WebContents> WebContents::CreateFromWebPreferences(
       existing_preferences->SetFromDictionary(web_preferences_dict);
       web_contents->SetBackgroundColor(
           existing_preferences->GetBackgroundColor());
+
+      double zoom_factor;
+      if (web_preferences.Get(options::kZoomFactor, &zoom_factor)) {
+        auto* zoom_controller = WebContentsZoomController::FromWebContents(
+            web_contents->web_contents());
+        if (zoom_controller) {
+          zoom_controller->SetDefaultZoomFactor(zoom_factor);
+          // Also set the current zoom level immediately, since the page
+          // has already navigated by the time we wrap the webContents.
+          zoom_controller->SetZoomLevel(
+              blink::ZoomFactorToZoomLevel(zoom_factor));
+        }
+      }
     }
   } else {
     // Create one if not.

filename: spec/api-browser-window-spec.ts
patch: @@ -3977,6 +3977,28 @@ describe('BrowserWindow module', () => {
         expect(webPreferences!.contextIsolation).to.equal(false);
       });
 
+      it('should apply zoomFactor from setWindowOpenHandler overrideBrowserWindowOptions', async () => {
+        const w = new BrowserWindow({
+          show: false,
+          webPreferences: {
+            sandbox: true
+          }
+        });
+
+        w.webContents.setWindowOpenHandler(() => ({
+          action: 'allow',
+          overrideBrowserWindowOptions: {
+            webPreferences: {
+              zoomFactor: 2.0
+            }
+          }
+        }));
+        w.loadFile(path.join(fixtures, 'api', 'new-window.html'));
+        const [childWindow] = await once(w.webContents, 'did-create-window') as [BrowserWindow, any];
+        await once(childWindow.webContents, 'did-finish-load');
+        expect(childWindow.webContents.getZoomFactor()).to.be.closeTo(2.0, 0.1);
+      });
+
       it('should set ipc event sender correctly', async () => {
         const w = new BrowserWindow({
           show: false,