Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: webContents.capturePage() for hidden windows on Windows/Linux #40185

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 12 additions & 11 deletions shell/browser/api/electron_api_web_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -469,9 +469,16 @@ base::IDMap<WebContents*>& GetAllWebContents() {
void OnCapturePageDone(gin_helper::Promise<gfx::Image> promise,
base::ScopedClosureRunner capture_handle,
const SkBitmap& bitmap) {
auto ui_task_runner = content::GetUIThreadTaskRunner({});
if (!ui_task_runner->RunsTasksInCurrentSequence()) {
ui_task_runner->PostTask(
FROM_HERE, base::BindOnce(&OnCapturePageDone, std::move(promise),
std::move(capture_handle), bitmap));
return;
}

// Hack to enable transparency in captured image
promise.Resolve(gfx::Image::CreateFrom1xBitmap(bitmap));

capture_handle.RunAndReset();
}

Expand Down Expand Up @@ -3462,22 +3469,16 @@ v8::Local<v8::Promise> WebContents::CapturePage(gin::Arguments* args) {
}

auto* const view = web_contents()->GetRenderWidgetHostView();
if (!view) {
if (!view || view->GetViewBounds().size().IsEmpty()) {
promise.Resolve(gfx::Image());
return handle;
}

#if !BUILDFLAG(IS_MAC)
// If the view's renderer is suspended this may fail on Windows/Linux -
// bail if so. See CopyFromSurface in
// content/public/browser/render_widget_host_view.h.
auto* rfh = web_contents()->GetPrimaryMainFrame();
if (rfh &&
rfh->GetVisibilityState() == blink::mojom::PageVisibilityState::kHidden) {
promise.Resolve(gfx::Image());
if (!view->IsSurfaceAvailableForCopy()) {
promise.RejectWithErrorMessage(
"Current display surface not available for capture");
return handle;
}
#endif // BUILDFLAG(IS_MAC)

auto capture_handle = web_contents()->IncrementCapturerCount(
rect.size(), stay_hidden, stay_awake);
Expand Down
32 changes: 18 additions & 14 deletions spec/api-browser-window-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2224,7 +2224,22 @@ describe('BrowserWindow module', () => {
expect(visible).to.equal('hidden');
});

it('resolves after the window is hidden', async () => {
it('resolves when the window is occluded', async () => {
const w1 = new BrowserWindow({ show: false });
w1.loadFile(path.join(fixtures, 'pages', 'a.html'));
await once(w1, 'ready-to-show');
w1.show();

const w2 = new BrowserWindow({ show: false });
w2.loadFile(path.join(fixtures, 'pages', 'a.html'));
await once(w2, 'ready-to-show');
w2.show();

const visibleImage = await w1.capturePage();
expect(visibleImage.isEmpty()).to.equal(false);
});

it('resolves when the window is not visible', async () => {
const w = new BrowserWindow({ show: false });
w.loadFile(path.join(fixtures, 'pages', 'a.html'));
await once(w, 'ready-to-show');
Expand All @@ -2233,21 +2248,10 @@ describe('BrowserWindow module', () => {
const visibleImage = await w.capturePage();
expect(visibleImage.isEmpty()).to.equal(false);

w.hide();
w.minimize();

const hiddenImage = await w.capturePage();
const isEmpty = process.platform !== 'darwin';
expect(hiddenImage.isEmpty()).to.equal(isEmpty);
});

it('resolves after the window is hidden and capturer count is non-zero', async () => {
const w = new BrowserWindow({ show: false });
w.webContents.setBackgroundThrottling(false);
w.loadFile(path.join(fixtures, 'pages', 'a.html'));
await once(w, 'ready-to-show');

const image = await w.capturePage();
expect(image.isEmpty()).to.equal(false);
expect(hiddenImage.isEmpty()).to.equal(false);
});

it('preserves transparency', async () => {
Expand Down