diff --git a/packages/puppeteer-core/src/api/ElementHandle.ts b/packages/puppeteer-core/src/api/ElementHandle.ts index 4a27cc55d21..43fec58e375 100644 --- a/packages/puppeteer-core/src/api/ElementHandle.ts +++ b/packages/puppeteer-core/src/api/ElementHandle.ts @@ -17,10 +17,15 @@ import type { NodeFor, } from '../common/types.js'; import type {KeyInput} from '../common/USKeyboardLayout.js'; -import {isString, withSourcePuppeteerURLIfNone} from '../common/util.js'; +import { + debugError, + isString, + withSourcePuppeteerURLIfNone, +} from '../common/util.js'; import {assert} from '../util/assert.js'; import {AsyncIterableUtil} from '../util/AsyncIterableUtil.js'; import {throwIfDisposed} from '../util/decorators.js'; +import {AsyncDisposableStack} from '../util/disposable.js'; import {_isElementHandle} from './ElementHandleSymbol.js'; import type { @@ -1341,6 +1346,31 @@ export abstract class ElementHandle< const page = this.frame.page(); + // If the element is larger than the viewport, `captureBeyondViewport` will + // _not_ affect element rendering, so we need to adjust the viewport to + // properly render the element. + const viewport = page.viewport() ?? { + width: clip.width, + height: clip.height, + }; + await using stack = new AsyncDisposableStack(); + if (clip.width > viewport.width || clip.height > viewport.height) { + await this.frame.page().setViewport({ + ...viewport, + width: Math.max(viewport.width, Math.ceil(clip.width)), + height: Math.max(viewport.height, Math.ceil(clip.height)), + }); + + stack.defer(async () => { + try { + await this.frame.page().setViewport(viewport); + } catch (error) { + debugError(error); + } + }); + } + + // Only scroll the element into view if the user wants it. if (scrollIntoView) { await this.scrollIntoViewIfNeeded();