Compare commits

..

1 Commits

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

View File

@@ -124,7 +124,7 @@ async function main () {
// Get the merge base with the target branch
let mergeBase;
try {
mergeBase = execSync(`git merge-base HEAD origin/${targetBranch}`, {
mergeBase = execSync(`git merge-base ${currentBranch} origin/${targetBranch}`, {
cwd: ELECTRON_DIR,
encoding: 'utf8'
}).trim();

View File

@@ -368,9 +368,6 @@ def upload_io_to_github(release, filename, filepath, version):
for c in iter(lambda: upload_process.stdout.read(1), b""):
sys.stdout.buffer.write(c)
sys.stdout.flush()
upload_process.wait()
if upload_process.returncode != 0:
sys.exit(upload_process.returncode)
if "GITHUB_OUTPUT" in os.environ:
output_path = os.environ["GITHUB_OUTPUT"]

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

@@ -9,7 +9,6 @@
#include "base/environment.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "content/public/browser/browser_task_traits.h"
@@ -280,8 +279,7 @@ void OnDeploymentCompleted(std::unique_ptr<DeploymentCallbackData> data,
HRESULT error_code;
hr = async_info->get_ErrorCode(&error_code);
if (SUCCEEDED(hr)) {
error +=
" (" + base::NumberToString(static_cast<int>(error_code)) + ")";
error += " (" + std::to_string(static_cast<int>(error_code)) + ")";
}
}
}
@@ -800,10 +798,10 @@ v8::Local<v8::Value> GetPackageInfo() {
ABI::Windows::ApplicationModel::PackageVersion pkg_version;
hr = package_id->get_Version(&pkg_version);
if (SUCCEEDED(hr)) {
std::string version = base::NumberToString(pkg_version.Major) + "." +
base::NumberToString(pkg_version.Minor) + "." +
base::NumberToString(pkg_version.Build) + "." +
base::NumberToString(pkg_version.Revision);
std::string version = std::to_string(pkg_version.Major) + "." +
std::to_string(pkg_version.Minor) + "." +
std::to_string(pkg_version.Build) + "." +
std::to_string(pkg_version.Revision);
result.Set("version", version);
}
}

View File

@@ -23,7 +23,6 @@
#include "base/json/json_reader.h"
#include "base/no_destructor.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/current_thread.h"
#include "base/threading/scoped_blocking_call.h"
@@ -2660,7 +2659,7 @@ void WebContents::RestoreHistory(
thrower.ThrowError(
"Failed to restore navigation history: Invalid navigation entry at "
"index " +
base::NumberToString(index) + ".");
std::to_string(index) + ".");
return;
}

View File

@@ -18,7 +18,6 @@
#include "base/environment.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
@@ -193,7 +192,7 @@ void V8OOMErrorCallback(const char* location, const v8::OOMDetails& details) {
#if !IS_MAS_BUILD()
electron::crash_keys::SetCrashKey("electron.v8-oom.is_heap_oom",
base::NumberToString(details.is_heap_oom));
std::to_string(details.is_heap_oom));
if (location) {
electron::crash_keys::SetCrashKey("electron.v8-oom.location", location);
}

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');
});
});