Skip to content

Commit 35ca9dd

Browse files
[Reporting] set viewport to include clip area (#87253)
* [Reporting] set viewport to include clip area * remove getViewport * fix tests * simpler * fix 1 * revert * hacks * scope the logging variables * polish * hacky fix * quieter logging * make less hacky * fix functional test * revert lowering log level of browser console messages * revise comments * setViewport only to happen once * fix snapshot of layout type tests * fix comment text * Revert "setViewport only to happen once" This reverts commit 15977f9. * fix disgusting bug * use x/y ordering for width/height * fix fn test snapshots Co-authored-by: Kibana Machine <[email protected]>
1 parent 3e172d0 commit 35ca9dd

File tree

14 files changed

+91
-18
lines changed

14 files changed

+91
-18
lines changed

x-pack/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,14 +196,17 @@ export class HeadlessChromiumDriver {
196196
}
197197

198198
public async setViewport(
199-
{ width, height, zoom }: ViewZoomWidthHeight,
199+
{ width: _width, height: _height, zoom }: ViewZoomWidthHeight,
200200
logger: LevelLogger
201201
): Promise<void> {
202-
logger.debug(`Setting viewport to width: ${width}, height: ${height}, zoom: ${zoom}`);
202+
const width = Math.floor(_width);
203+
const height = Math.floor(_height);
204+
205+
logger.debug(`Setting viewport to: width=${width} height=${height} zoom=${zoom}`);
203206

204207
await this.page.setViewport({
205-
width: Math.floor(width / zoom),
206-
height: Math.floor(height / zoom),
208+
width,
209+
height,
207210
deviceScaleFactor: zoom,
208211
isMobile: false,
209212
});
@@ -243,7 +246,7 @@ export class HeadlessChromiumDriver {
243246
}
244247

245248
if (this._shouldUseCustomHeaders(conditionalHeaders.conditions, interceptedUrl)) {
246-
logger.debug(`Using custom headers for ${interceptedUrl}`);
249+
logger.trace(`Using custom headers for ${interceptedUrl}`);
247250
const headers = map(
248251
{
249252
...interceptedRequest.request.headers,
@@ -270,7 +273,7 @@ export class HeadlessChromiumDriver {
270273
}
271274
} else {
272275
const loggedUrl = isData ? this.truncateUrl(interceptedUrl) : interceptedUrl;
273-
logger.debug(`No custom headers for ${loggedUrl}`);
276+
logger.trace(`No custom headers for ${loggedUrl}`);
274277
try {
275278
await client.send('Fetch.continueRequest', { requestId });
276279
} catch (err) {

x-pack/plugins/reporting/server/browsers/chromium/driver_factory/args.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ export const args = ({ userDataDir, viewport, disableSandbox, proxy: proxyConfig
4141
'--disable-gpu',
4242
'--headless',
4343
'--hide-scrollbars',
44+
// NOTE: setting the window size does NOT set the viewport size: viewport and window size are different.
45+
// The viewport may later need to be resized depending on the position of the clip area.
46+
// These numbers come from the job parameters, so this is a close guess.
4447
`--window-size=${Math.floor(viewport.width)},${Math.floor(viewport.height)}`,
4548
];
4649

x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export class HeadlessChromiumDriverFactory {
6565
logger.info(`Creating browser page driver`);
6666

6767
const chromiumArgs = this.getChromiumArgs(viewport);
68+
logger.debug(`Chromium launch args set to: ${chromiumArgs}`);
6869

6970
let browser: puppeteer.Browser;
7071
let page: puppeteer.Page;

x-pack/plugins/reporting/server/export_types/printable_pdf/lib/generate_pdf.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export async function generatePdfObservableFactory(reporting: ReportingCore) {
4343
tracker.startLayout();
4444

4545
const layout = createLayout(captureConfig, layoutParams);
46+
logger.debug(`Layout: width=${layout.width} height=${layout.height}`);
4647
tracker.endLayout();
4748

4849
tracker.startScreenshots();

x-pack/plugins/reporting/server/lib/layouts/canvas_layout.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ export class CanvasLayout extends Layout implements LayoutInstance {
6464

6565
public getViewport() {
6666
return {
67-
height: this.scaledHeight,
68-
width: this.scaledWidth,
67+
height: this.height,
68+
width: this.width,
6969
zoom: ZOOM,
7070
};
7171
}

x-pack/plugins/reporting/server/lib/layouts/layout.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export abstract class Layout {
3333
pageSizeParams: PageSizeParams
3434
): CustomPageSize | PredefinedPageSize;
3535

36+
// Return the dimensions unscaled dimensions (before multiplying the zoom factor)
37+
// driver.setViewport() Adds a top and left margin to the viewport, and then multiplies by the scaling factor
3638
public abstract getViewport(itemsCount: number): ViewZoomWidthHeight | null;
3739

3840
public abstract getBrowserZoom(): number;

x-pack/plugins/reporting/server/lib/layouts/preserve_layout.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ export class PreserveLayout extends Layout implements LayoutInstance {
5151

5252
public getViewport() {
5353
return {
54-
height: this.scaledHeight,
55-
width: this.scaledWidth,
54+
height: this.height,
55+
width: this.width,
5656
zoom: ZOOM,
5757
};
5858
}

x-pack/plugins/reporting/server/lib/level_logger.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ export class LevelLogger implements GenericLevelLogger {
4949
this.getLogger(tags).debug(msg);
5050
}
5151

52+
public trace(msg: string, tags: string[] = []) {
53+
this.getLogger(tags).trace(msg);
54+
}
55+
5256
public info(msg: string, tags: string[] = []) {
5357
this.getLogger(tags).info(trimStr(msg));
5458
}

x-pack/plugins/reporting/server/lib/screenshots/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
export const DEFAULT_PAGELOAD_SELECTOR = '.application';
88

99
export const CONTEXT_GETNUMBEROFITEMS = 'GetNumberOfItems';
10+
export const CONTEXT_GETBROWSERDIMENSIONS = 'GetBrowserDimensions';
1011
export const CONTEXT_INJECTCSS = 'InjectCss';
1112
export const CONTEXT_WAITFORRENDER = 'WaitForRender';
1213
export const CONTEXT_GETTIMERANGE = 'GetTimeRange';

x-pack/plugins/reporting/server/lib/screenshots/get_screenshots.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,60 @@
77
import { i18n } from '@kbn/i18n';
88
import { LevelLogger, startTrace } from '../';
99
import { HeadlessChromiumDriver } from '../../browsers';
10+
import { LayoutInstance } from '../layouts';
1011
import { ElementsPositionAndAttribute, Screenshot } from './';
12+
import { CONTEXT_GETBROWSERDIMENSIONS } from './constants';
13+
14+
// In Puppeteer 5.4+, the viewport size limits what the screenshot can take, even if a clip is specified. The clip area must
15+
// be visible in the viewport. This workaround resizes the viewport to the actual content height and width.
16+
// NOTE: this will fire a window resize event
17+
const resizeToClipArea = async (
18+
item: ElementsPositionAndAttribute,
19+
browser: HeadlessChromiumDriver,
20+
zoom: number,
21+
logger: LevelLogger
22+
) => {
23+
// Check current viewport size
24+
const { width, height, left, top } = item.position.boundingClientRect; // the "unscaled" pixel sizes
25+
const [viewWidth, viewHeight] = await browser.evaluate(
26+
{
27+
fn: () => [document.body.clientWidth, document.body.clientHeight],
28+
args: [],
29+
},
30+
{ context: CONTEXT_GETBROWSERDIMENSIONS },
31+
logger
32+
);
33+
34+
logger.debug(`Browser viewport: width=${viewWidth} height=${viewHeight}`);
35+
36+
// Resize the viewport if the clip area is not visible
37+
if (viewWidth < width + left || viewHeight < height + top) {
38+
logger.debug(`Item's position is not within the viewport.`);
39+
40+
// add left and top margin to unscaled measurements
41+
const newWidth = width + left;
42+
const newHeight = height + top;
43+
44+
logger.debug(
45+
`Resizing browser viewport to: width=${newWidth} height=${newHeight} zoom=${zoom}`
46+
);
47+
48+
await browser.setViewport(
49+
{
50+
width: newWidth,
51+
height: newHeight,
52+
zoom,
53+
},
54+
logger
55+
);
56+
}
57+
58+
logger.debug(`Capturing item: width=${width} height=${height} left=${left} top=${top}`);
59+
};
1160

1261
export const getScreenshots = async (
1362
browser: HeadlessChromiumDriver,
63+
layout: LayoutInstance,
1464
elementsPositionAndAttributes: ElementsPositionAndAttribute[],
1565
logger: LevelLogger
1666
): Promise<Screenshot[]> => {
@@ -25,6 +75,8 @@ export const getScreenshots = async (
2575
for (let i = 0; i < elementsPositionAndAttributes.length; i++) {
2676
const endTrace = startTrace('get_screenshots', 'read');
2777
const item = elementsPositionAndAttributes[i];
78+
79+
await resizeToClipArea(item, browser, layout.getBrowserZoom(), logger);
2880
const base64EncodedData = await browser.screenshot(item.position);
2981

3082
screenshots.push({

0 commit comments

Comments
 (0)