mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00:00 
			
		
		
		
	browser(firefox): move screenshots to browser-side (#15230)
* `clip` option is always passed from the client code * with this change, we can no longer capture screenshot of a blinking caret; the browser-side API doesn't have this capability.
This commit is contained in:
		
							parent
							
								
									4e46ac2191
								
							
						
					
					
						commit
						e9d66535ba
					
				| @ -1,2 +1,2 @@ | ||||
| 1334 | ||||
| Changed: lushnikov@chromium.org Wed Jul  6 01:35:56 MSK 2022 | ||||
| 1335 | ||||
| Changed: lushnikov@chromium.org Wed Jul  6 20:30:28 MSK 2022 | ||||
|  | ||||
| @ -148,7 +148,6 @@ class PageAgent { | ||||
|         insertText: this._insertText.bind(this), | ||||
|         navigate: this._navigate.bind(this), | ||||
|         reload: this._reload.bind(this), | ||||
|         screenshot: this._screenshot.bind(this), | ||||
|         scrollIntoViewIfNeeded: this._scrollIntoViewIfNeeded.bind(this), | ||||
|         setCacheDisabled: this._setCacheDisabled.bind(this), | ||||
|         setFileInputFiles: this._setFileInputFiles.bind(this), | ||||
| @ -520,16 +519,6 @@ class PageAgent { | ||||
|     return {x: x1, y: y1, width: x2 - x1, height: y2 - y1}; | ||||
|   } | ||||
| 
 | ||||
|   async _screenshot({mimeType, clip, omitDeviceScaleFactor}) { | ||||
|     const content = this._messageManager.content; | ||||
|     if (clip) { | ||||
|       const data = takeScreenshot(content, clip.x, clip.y, clip.width, clip.height, mimeType, omitDeviceScaleFactor); | ||||
|       return {data}; | ||||
|     } | ||||
|     const data = takeScreenshot(content, content.scrollX, content.scrollY, content.innerWidth, content.innerHeight, mimeType, omitDeviceScaleFactor); | ||||
|     return {data}; | ||||
|   } | ||||
| 
 | ||||
|   async _dispatchKeyEvent({type, keyCode, code, key, repeat, location, text}) { | ||||
|     // key events don't fire if we are dragging.
 | ||||
|     if (this._dragging) { | ||||
| @ -900,31 +889,6 @@ class PageAgent { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function takeScreenshot(win, left, top, width, height, mimeType, omitDeviceScaleFactor) { | ||||
|   const MAX_SKIA_DIMENSIONS = 32767; | ||||
| 
 | ||||
|   // `win.devicePixelRatio` returns a non-overriden value to priveleged code.
 | ||||
|   // See https://bugzilla.mozilla.org/show_bug.cgi?id=1761032
 | ||||
|   // See https://phabricator.services.mozilla.com/D141323
 | ||||
|   const devicePixelRatio = win.browsingContext.overrideDPPX || win.devicePixelRatio; | ||||
|   const scale = omitDeviceScaleFactor ? 1 : devicePixelRatio; | ||||
|   const canvasWidth = width * scale; | ||||
|   const canvasHeight = height * scale; | ||||
| 
 | ||||
|   if (canvasWidth > MAX_SKIA_DIMENSIONS || canvasHeight > MAX_SKIA_DIMENSIONS) | ||||
|     throw new Error('Cannot take screenshot larger than ' + MAX_SKIA_DIMENSIONS); | ||||
| 
 | ||||
|   const canvas = win.document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas'); | ||||
|   canvas.width = canvasWidth; | ||||
|   canvas.height = canvasHeight; | ||||
| 
 | ||||
|   let ctx = canvas.getContext('2d'); | ||||
|   ctx.scale(scale, scale); | ||||
|   ctx.drawWindow(win, left, top, width, height, 'rgb(255,255,255)', ctx.DRAWWINDOW_DRAW_CARET); | ||||
|   const dataURL = canvas.toDataURL(mimeType); | ||||
|   return dataURL.substring(dataURL.indexOf(',') + 1); | ||||
| }; | ||||
| 
 | ||||
| var EXPORTED_SYMBOLS = ['PageAgent']; | ||||
| this.PageAgent = PageAgent; | ||||
| 
 | ||||
|  | ||||
| @ -8,6 +8,7 @@ const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js'); | ||||
| const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); | ||||
| const {NetworkObserver, PageNetwork} = ChromeUtils.import('chrome://juggler/content/NetworkObserver.js'); | ||||
| const {PageTarget} = ChromeUtils.import('chrome://juggler/content/TargetRegistry.js'); | ||||
| const {setTimeout} = ChromeUtils.import('resource://gre/modules/Timer.jsm'); | ||||
| 
 | ||||
| const Cc = Components.classes; | ||||
| const Ci = Components.interfaces; | ||||
| @ -302,8 +303,51 @@ class PageHandler { | ||||
|     return await this._contentPage.send('adoptNode', options); | ||||
|   } | ||||
| 
 | ||||
|   async ['Page.screenshot'](options) { | ||||
|     return await this._contentPage.send('screenshot', options); | ||||
|   async ['Page.screenshot']({ mimeType, clip, omitDeviceScaleFactor }) { | ||||
|     const rect = new DOMRect(clip.x, clip.y, clip.width, clip.height); | ||||
| 
 | ||||
|     const browsingContext = this._pageTarget.linkedBrowser().browsingContext; | ||||
|     // `win.devicePixelRatio` returns a non-overriden value to priveleged code.
 | ||||
|     // See https://bugzilla.mozilla.org/show_bug.cgi?id=1761032
 | ||||
|     // See https://phabricator.services.mozilla.com/D141323
 | ||||
|     const devicePixelRatio = browsingContext.overrideDPPX || this._pageTarget._window.devicePixelRatio; | ||||
|     const scale = omitDeviceScaleFactor ? 1 : devicePixelRatio; | ||||
|     const canvasWidth = rect.width * scale; | ||||
|     const canvasHeight = rect.height * scale; | ||||
| 
 | ||||
|     const MAX_CANVAS_DIMENSIONS = 32767; | ||||
|     const MAX_CANVAS_AREA = 472907776; | ||||
|     if (canvasWidth > MAX_CANVAS_DIMENSIONS || canvasHeight > MAX_CANVAS_DIMENSIONS) | ||||
|       throw new Error('Cannot take screenshot larger than ' + MAX_CANVAS_DIMENSIONS); | ||||
|     if (canvasWidth * canvasHeight > MAX_CANVAS_AREA) | ||||
|       throw new Error('Cannot take screenshot with more than ' + MAX_CANVAS_AREA + ' pixels'); | ||||
| 
 | ||||
|     let snapshot; | ||||
|     while (!snapshot) { | ||||
|       try { | ||||
|         //TODO(fission): browsingContext will change in case of cross-group navigation.
 | ||||
|         snapshot = await browsingContext.currentWindowGlobal.drawSnapshot( | ||||
|           rect, | ||||
|           scale, | ||||
|           "rgb(255,255,255)" | ||||
|         ); | ||||
|       } catch (e) { | ||||
|         // The currentWindowGlobal.drawSnapshot might throw
 | ||||
|         // NS_ERROR_LOSS_OF_SIGNIFICANT_DATA if called during navigation.
 | ||||
|         // wait a little and re-try.
 | ||||
|         await new Promise(x => setTimeout(x, 50)); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     const win = browsingContext.topChromeWindow.ownerGlobal; | ||||
|     const canvas = win.document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas'); | ||||
|     canvas.width = canvasWidth; | ||||
|     canvas.height = canvasHeight; | ||||
|     let ctx = canvas.getContext('2d'); | ||||
|     ctx.drawImage(snapshot, 0, 0); | ||||
|     snapshot.close(); | ||||
|     const dataURL = canvas.toDataURL(mimeType); | ||||
|     return { data: dataURL.substring(dataURL.indexOf(',') + 1) }; | ||||
|   } | ||||
| 
 | ||||
|   async ['Page.getContentQuads'](options) { | ||||
|  | ||||
| @ -857,7 +857,7 @@ const Page = { | ||||
|     'screenshot': { | ||||
|       params: { | ||||
|         mimeType: t.Enum(['image/png', 'image/jpeg']), | ||||
|         clip: t.Optional(pageTypes.Clip), | ||||
|         clip: pageTypes.Clip, | ||||
|         omitDeviceScaleFactor: t.Optional(t.Boolean), | ||||
|       }, | ||||
|       returns: { | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| 1334 | ||||
| Changed: lushnikov@chromium.org Wed Jul  6 00:25:34 MSK 2022 | ||||
| 1335 | ||||
| Changed: lushnikov@chromium.org Wed Jul  6 20:30:28 MSK 2022 | ||||
|  | ||||
| @ -148,7 +148,6 @@ class PageAgent { | ||||
|         insertText: this._insertText.bind(this), | ||||
|         navigate: this._navigate.bind(this), | ||||
|         reload: this._reload.bind(this), | ||||
|         screenshot: this._screenshot.bind(this), | ||||
|         scrollIntoViewIfNeeded: this._scrollIntoViewIfNeeded.bind(this), | ||||
|         setCacheDisabled: this._setCacheDisabled.bind(this), | ||||
|         setFileInputFiles: this._setFileInputFiles.bind(this), | ||||
| @ -520,16 +519,6 @@ class PageAgent { | ||||
|     return {x: x1, y: y1, width: x2 - x1, height: y2 - y1}; | ||||
|   } | ||||
| 
 | ||||
|   async _screenshot({mimeType, clip, omitDeviceScaleFactor}) { | ||||
|     const content = this._messageManager.content; | ||||
|     if (clip) { | ||||
|       const data = takeScreenshot(content, clip.x, clip.y, clip.width, clip.height, mimeType, omitDeviceScaleFactor); | ||||
|       return {data}; | ||||
|     } | ||||
|     const data = takeScreenshot(content, content.scrollX, content.scrollY, content.innerWidth, content.innerHeight, mimeType, omitDeviceScaleFactor); | ||||
|     return {data}; | ||||
|   } | ||||
| 
 | ||||
|   async _dispatchKeyEvent({type, keyCode, code, key, repeat, location, text}) { | ||||
|     // key events don't fire if we are dragging.
 | ||||
|     if (this._dragging) { | ||||
| @ -900,31 +889,6 @@ class PageAgent { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function takeScreenshot(win, left, top, width, height, mimeType, omitDeviceScaleFactor) { | ||||
|   const MAX_SKIA_DIMENSIONS = 32767; | ||||
| 
 | ||||
|   // `win.devicePixelRatio` returns a non-overriden value to priveleged code.
 | ||||
|   // See https://bugzilla.mozilla.org/show_bug.cgi?id=1761032
 | ||||
|   // See https://phabricator.services.mozilla.com/D141323
 | ||||
|   const devicePixelRatio = win.browsingContext.overrideDPPX || win.devicePixelRatio; | ||||
|   const scale = omitDeviceScaleFactor ? 1 : devicePixelRatio; | ||||
|   const canvasWidth = width * scale; | ||||
|   const canvasHeight = height * scale; | ||||
| 
 | ||||
|   if (canvasWidth > MAX_SKIA_DIMENSIONS || canvasHeight > MAX_SKIA_DIMENSIONS) | ||||
|     throw new Error('Cannot take screenshot larger than ' + MAX_SKIA_DIMENSIONS); | ||||
| 
 | ||||
|   const canvas = win.document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas'); | ||||
|   canvas.width = canvasWidth; | ||||
|   canvas.height = canvasHeight; | ||||
| 
 | ||||
|   let ctx = canvas.getContext('2d'); | ||||
|   ctx.scale(scale, scale); | ||||
|   ctx.drawWindow(win, left, top, width, height, 'rgb(255,255,255)', ctx.DRAWWINDOW_DRAW_CARET); | ||||
|   const dataURL = canvas.toDataURL(mimeType); | ||||
|   return dataURL.substring(dataURL.indexOf(',') + 1); | ||||
| }; | ||||
| 
 | ||||
| var EXPORTED_SYMBOLS = ['PageAgent']; | ||||
| this.PageAgent = PageAgent; | ||||
| 
 | ||||
|  | ||||
| @ -8,6 +8,7 @@ const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js'); | ||||
| const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); | ||||
| const {NetworkObserver, PageNetwork} = ChromeUtils.import('chrome://juggler/content/NetworkObserver.js'); | ||||
| const {PageTarget} = ChromeUtils.import('chrome://juggler/content/TargetRegistry.js'); | ||||
| const {setTimeout} = ChromeUtils.import('resource://gre/modules/Timer.jsm'); | ||||
| 
 | ||||
| const Cc = Components.classes; | ||||
| const Ci = Components.interfaces; | ||||
| @ -302,8 +303,51 @@ class PageHandler { | ||||
|     return await this._contentPage.send('adoptNode', options); | ||||
|   } | ||||
| 
 | ||||
|   async ['Page.screenshot'](options) { | ||||
|     return await this._contentPage.send('screenshot', options); | ||||
|   async ['Page.screenshot']({ mimeType, clip, omitDeviceScaleFactor }) { | ||||
|     const rect = new DOMRect(clip.x, clip.y, clip.width, clip.height); | ||||
| 
 | ||||
|     const browsingContext = this._pageTarget.linkedBrowser().browsingContext; | ||||
|     // `win.devicePixelRatio` returns a non-overriden value to priveleged code.
 | ||||
|     // See https://bugzilla.mozilla.org/show_bug.cgi?id=1761032
 | ||||
|     // See https://phabricator.services.mozilla.com/D141323
 | ||||
|     const devicePixelRatio = browsingContext.overrideDPPX || this._pageTarget._window.devicePixelRatio; | ||||
|     const scale = omitDeviceScaleFactor ? 1 : devicePixelRatio; | ||||
|     const canvasWidth = rect.width * scale; | ||||
|     const canvasHeight = rect.height * scale; | ||||
| 
 | ||||
|     const MAX_CANVAS_DIMENSIONS = 32767; | ||||
|     const MAX_CANVAS_AREA = 472907776; | ||||
|     if (canvasWidth > MAX_CANVAS_DIMENSIONS || canvasHeight > MAX_CANVAS_DIMENSIONS) | ||||
|       throw new Error('Cannot take screenshot larger than ' + MAX_CANVAS_DIMENSIONS); | ||||
|     if (canvasWidth * canvasHeight > MAX_CANVAS_AREA) | ||||
|       throw new Error('Cannot take screenshot with more than ' + MAX_CANVAS_AREA + ' pixels'); | ||||
| 
 | ||||
|     let snapshot; | ||||
|     while (!snapshot) { | ||||
|       try { | ||||
|         //TODO(fission): browsingContext will change in case of cross-group navigation.
 | ||||
|         snapshot = await browsingContext.currentWindowGlobal.drawSnapshot( | ||||
|           rect, | ||||
|           scale, | ||||
|           "rgb(255,255,255)" | ||||
|         ); | ||||
|       } catch (e) { | ||||
|         // The currentWindowGlobal.drawSnapshot might throw
 | ||||
|         // NS_ERROR_LOSS_OF_SIGNIFICANT_DATA if called during navigation.
 | ||||
|         // wait a little and re-try.
 | ||||
|         await new Promise(x => setTimeout(x, 50)); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     const win = browsingContext.topChromeWindow.ownerGlobal; | ||||
|     const canvas = win.document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas'); | ||||
|     canvas.width = canvasWidth; | ||||
|     canvas.height = canvasHeight; | ||||
|     let ctx = canvas.getContext('2d'); | ||||
|     ctx.drawImage(snapshot, 0, 0); | ||||
|     snapshot.close(); | ||||
|     const dataURL = canvas.toDataURL(mimeType); | ||||
|     return { data: dataURL.substring(dataURL.indexOf(',') + 1) }; | ||||
|   } | ||||
| 
 | ||||
|   async ['Page.getContentQuads'](options) { | ||||
|  | ||||
| @ -857,7 +857,7 @@ const Page = { | ||||
|     'screenshot': { | ||||
|       params: { | ||||
|         mimeType: t.Enum(['image/png', 'image/jpeg']), | ||||
|         clip: t.Optional(pageTypes.Clip), | ||||
|         clip: pageTypes.Clip, | ||||
|         omitDeviceScaleFactor: t.Optional(t.Boolean), | ||||
|       }, | ||||
|       returns: { | ||||
|  | ||||
| @ -433,8 +433,6 @@ export class FFPage implements PageDelegate { | ||||
|         height: viewportRect!.height, | ||||
|       }; | ||||
|     } | ||||
|     // TODO: remove fullPage option from Page.screenshot.
 | ||||
|     // TODO: remove Page.getBoundingBox method.
 | ||||
|     progress.throwIfAborted(); | ||||
|     const { data } = await this._session.send('Page.screenshot', { | ||||
|       mimeType: ('image/' + format) as ('image/png' | 'image/jpeg'), | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Andrey Lushnikov
						Andrey Lushnikov