@@ -70,21 +70,22 @@ class FullPageScreenshot extends FRGatherer {
70
70
71
71
/**
72
72
* @param {LH.Gatherer.FRTransitionalContext } context
73
+ * @param {{height: number, width: number, mobile: boolean} } deviceMetrics
73
74
* @return {Promise<LH.Artifacts.FullPageScreenshot['screenshot']> }
74
75
*/
75
- async _takeScreenshot ( context ) {
76
+ async _takeScreenshot ( context , deviceMetrics ) {
76
77
const session = context . driver . defaultSession ;
77
78
const maxTextureSize = await this . getMaxTextureSize ( context ) ;
78
79
const metrics = await session . sendCommand ( 'Page.getLayoutMetrics' ) ;
79
80
80
- // Width should match emulated width, without considering content overhang .
81
- // Both layoutViewport and visualViewport capture this. visualViewport accounts
82
- // for page zoom/scale, which we currently don't account for (or expect). So we use layoutViewport.width.
83
- // Note: If the page is zoomed, many assumptions fail.
84
- / /
85
- // Height should be as tall as the content. So we use contentSize.height
86
- const width = Math . min ( metrics . layoutViewport . clientWidth , maxTextureSize ) ;
87
- const height = Math . min ( metrics . contentSize . height , maxTextureSize ) ;
81
+ // Height should be as tall as the content.
82
+ // Scale the emulated height to reach the content height.
83
+ const fullHeight = Math . round (
84
+ deviceMetrics . height *
85
+ metrics . contentSize . height /
86
+ metrics . layoutViewport . clientHeight
87
+ ) ;
88
+ const height = Math . min ( fullHeight , maxTextureSize ) ;
88
89
89
90
// Setup network monitor before we change the viewport.
90
91
const networkMonitor = new NetworkMonitor ( session ) ;
@@ -98,13 +99,10 @@ class FullPageScreenshot extends FRGatherer {
98
99
await networkMonitor . enable ( ) ;
99
100
100
101
await session . sendCommand ( 'Emulation.setDeviceMetricsOverride' , {
101
- // If we're gathering with mobile screenEmulation on (overlay scrollbars, etc), continue to use that for this screenshot.
102
- mobile : context . settings . screenEmulation . mobile ,
103
- height,
104
- width,
102
+ mobile : deviceMetrics . mobile ,
105
103
deviceScaleFactor : 1 ,
106
- scale : 1 ,
107
- screenOrientation : { angle : 0 , type : 'portraitPrimary' } ,
104
+ height ,
105
+ width : 0 , // Leave width unchanged
108
106
} ) ;
109
107
110
108
// Now that the viewport is taller, give the page some time to fetch new resources that
@@ -127,7 +125,7 @@ class FullPageScreenshot extends FRGatherer {
127
125
128
126
return {
129
127
data,
130
- width,
128
+ width : deviceMetrics . width ,
131
129
height,
132
130
} ;
133
131
}
@@ -185,29 +183,37 @@ class FullPageScreenshot extends FRGatherer {
185
183
const session = context . driver . defaultSession ;
186
184
const executionContext = context . driver . executionContext ;
187
185
const settings = context . settings ;
188
-
189
- // In case some other program is controlling emulation, remember what the device looks
190
- // like now and reset after gatherer is done.
191
- let observedDeviceMetrics ;
192
186
const lighthouseControlsEmulation = ! settings . screenEmulation . disabled ;
187
+
188
+ // Make a copy so we don't modify the config settings.
189
+ /** @type {{width: number, height: number, deviceScaleFactor: number, mobile: boolean} } */
190
+ const deviceMetrics = { ...settings . screenEmulation } ;
191
+
192
+ // In case some other program is controlling emulation, remember what the device looks like now and reset after gatherer is done.
193
+ // If we're gathering with mobile screenEmulation on (overlay scrollbars, etc), continue to use that for this screenshot.
193
194
if ( ! lighthouseControlsEmulation ) {
194
- observedDeviceMetrics = await executionContext . evaluate ( getObservedDeviceMetrics , {
195
+ const observedDeviceMetrics = await executionContext . evaluate ( getObservedDeviceMetrics , {
195
196
args : [ ] ,
196
197
useIsolation : true ,
197
198
deps : [ kebabCaseToCamelCase ] ,
198
199
} ) ;
200
+ deviceMetrics . height = observedDeviceMetrics . height ;
201
+ deviceMetrics . width = observedDeviceMetrics . width ;
202
+ deviceMetrics . deviceScaleFactor = observedDeviceMetrics . deviceScaleFactor ;
203
+ // If screen emulation is disabled, use formFactor to determine if we are on mobile.
204
+ deviceMetrics . mobile = settings . formFactor === 'mobile' ;
199
205
}
200
206
201
207
try {
202
208
return {
203
- screenshot : await this . _takeScreenshot ( context ) ,
209
+ screenshot : await this . _takeScreenshot ( context , deviceMetrics ) ,
204
210
nodes : await this . _resolveNodes ( context ) ,
205
211
} ;
206
212
} finally {
207
213
// Revert resized page.
208
214
if ( lighthouseControlsEmulation ) {
209
215
await emulation . emulate ( session , settings ) ;
210
- } else if ( observedDeviceMetrics ) {
216
+ } else {
211
217
// Best effort to reset emulation to what it was.
212
218
// https://github.com/GoogleChrome/lighthouse/pull/10716#discussion_r428970681
213
219
// TODO: seems like this would be brittle. Should at least work for devtools, but what
@@ -216,8 +222,10 @@ class FullPageScreenshot extends FRGatherer {
216
222
// and then just call that to reset?
217
223
// https://github.com/GoogleChrome/lighthouse/issues/11122
218
224
await session . sendCommand ( 'Emulation.setDeviceMetricsOverride' , {
219
- mobile : settings . formFactor === 'mobile' ,
220
- ...observedDeviceMetrics ,
225
+ mobile : deviceMetrics . mobile ,
226
+ deviceScaleFactor : deviceMetrics . deviceScaleFactor ,
227
+ height : deviceMetrics . height ,
228
+ width : 0 , // Leave width unchanged
221
229
} ) ;
222
230
}
223
231
}
0 commit comments