Compare commits

...

1 Commits

Author SHA1 Message Date
Shelley Vohr
19c382677b fix: paintWhenInitiallyHidden on Windows/Linux 2026-02-25 12:31:20 +01:00
3 changed files with 38 additions and 12 deletions

View File

@@ -49,6 +49,7 @@ BrowserWindow::BrowserWindow(gin::Arguments* args,
// when initially hidden
bool paint_when_initially_hidden = true;
options.Get(options::kPaintWhenInitiallyHidden, &paint_when_initially_hidden);
paint_when_initially_hidden_ = paint_when_initially_hidden;
if (!paint_when_initially_hidden) {
bool show = true;
options.Get(options::kShow, &show);
@@ -288,6 +289,17 @@ void BrowserWindow::OnWindowHide() {
BaseWindow::OnWindowHide();
}
void BrowserWindow::RenderViewReady() {
// When paintWhenInitiallyHidden is true and the native window has not been
// shown yet, tell the WebContents it is visible so the renderer's compositor
// starts producing frames. Without this the renderer's LayerTreeHost stays
// hidden and no CompositorFrames (and therefore no presentation callbacks)
// are ever produced, which means PerformanceObserver paint-timing entries
// (first-paint / first-contentful-paint) never fire.
if (paint_when_initially_hidden_ && !window()->IsVisible())
web_contents()->WasShown();
}
void BrowserWindow::Show() {
web_contents()->WasShown();
BaseWindow::Show();

View File

@@ -42,6 +42,7 @@ class BrowserWindow : public BaseWindow,
// content::WebContentsObserver:
void BeforeUnloadDialogCancelled() override;
void RenderViewReady() override;
void WebContentsDestroyed() override;
// ExtendedWebContentsObserver:
@@ -79,6 +80,12 @@ class BrowserWindow : public BaseWindow,
private:
// Helpers.
// When true and the window is created with show: false, the renderer is
// told it is visible as soon as it is ready so that PerformanceObserver
// painttiming entries (first-paint / first-contentful-paint) are
// produced even before the native window is shown.
bool paint_when_initially_hidden_ = false;
v8::Global<v8::Value> web_contents_;
v8::Global<v8::Value> web_contents_view_;
base::WeakPtr<api::WebContents> api_web_contents_;

View File

@@ -6721,28 +6721,35 @@ describe('BrowserWindow module', () => {
w.loadFile(path.join(fixtures, 'pages', 'send-after-node.html'));
});
// TODO(codebytere): fix on Windows and Linux too
ifdescribe(process.platform === 'darwin')('window.webContents initial paint', () => {
describe('window.webContents initial paint', () => {
afterEach(closeAllWindows);
it('paints when a window is initially hidden', async () => {
const w = new BrowserWindow({ show: false });
it('paints when a window is initially hidden with paintWhenInitiallyHidden', async () => {
const w = new BrowserWindow({
show: false,
paintWhenInitiallyHidden: true
});
await w.loadFile(path.join(fixtures, 'pages', 'a.html'));
const entries = await w.webContents.executeJavaScript(`
new Promise((resolve) => {
const observer = new PerformanceObserver((performance) => {
observer.disconnect();
resolve(performance.getEntries());
const observer = new PerformanceObserver((list) => {
const paintEntries = list.getEntries().filter(
e => e.name === 'first-paint' || e.name === 'first-contentful-paint'
);
if (paintEntries.length > 0) {
observer.disconnect();
resolve(paintEntries.map(e => e.name));
}
});
observer.observe({ entryTypes: ['paint'] });
});
const header = document.createElement('h1');
header.innerText = 'Paint me!!';
document.getElementById('div').appendChild(header);
const header = document.createElement('h1');
header.innerText = 'Paint me!!';
document.getElementById('div').appendChild(header);
});
`);
expect(JSON.stringify(entries)).to.eq('{}');
expect(entries).to.be.an('array').that.includes('first-contentful-paint');
});
});