diff --git a/browser/modules/WindowsPreviewPerTab.jsm b/browser/modules/WindowsPreviewPerTab.jsm index be723fb6be1b..cd22afb8b91f 100644 --- a/browser/modules/WindowsPreviewPerTab.jsm +++ b/browser/modules/WindowsPreviewPerTab.jsm @@ -296,11 +296,14 @@ PreviewController.prototype = { ctx.drawWindow(this.win.win, 0, 0, winWidth, winHeight, "rgba(0,0,0,0)"); // Draw the content are into the composite canvas at the right location. - ctx.drawImage(aPreviewCanvas, this.browserDims.x, this.browserDims.y, aPreviewCanvas.width, aPreviewCanvas.height); + ctx.drawImage(aPreviewCanvas, this.browserDims.x, this.browserDims.y, + aPreviewCanvas.width, aPreviewCanvas.height); ctx.restore(); // Deliver the resulting composite canvas to Windows - this.win.tabbrowser.previewTab(this.tab, function () { aTaskbarCallback.done(composite, false); }); + this.win.tabbrowser.previewTab(this.tab, function () { + aTaskbarCallback.done(composite, false); + }); }); }, @@ -379,7 +382,7 @@ function TabWindow(win) { this.win.addEventListener(this.winEvents[i], this, false); this.tabbrowser.addTabsProgressListener(this); - + AeroPeek.windows.push(this); let tabs = this.tabbrowser.tabs; for (let i = 0; i < tabs.length; i++) diff --git a/toolkit/components/thumbnails/PageThumbUtils.jsm b/toolkit/components/thumbnails/PageThumbUtils.jsm index 0f3da6f97a15..efb705ad1555 100644 --- a/toolkit/components/thumbnails/PageThumbUtils.jsm +++ b/toolkit/components/thumbnails/PageThumbUtils.jsm @@ -139,50 +139,55 @@ this.PageThumbUtils = { * jagged pixels to represent text. * * @params aWindow - the window to create a snapshot of. - * @params aDestCanvas (optional) a destination canvas to draw the final snapshot to. + * @params aDestCanvas destination canvas to draw the final + * snapshot to. Can be null. + * @param aArgs (optional) Additional named parameters: + * fullScale - request that a non-downscaled image be returned. * @return Canvas with a scaled thumbnail of the window. */ - createSnapshotThumbnail: function(aWindow, aDestCanvas = null) { + createSnapshotThumbnail: function(aWindow, aDestCanvas, aArgs) { if (Cu.isCrossProcessWrapper(aWindow)) { throw new Error('Do not pass cpows here.'); } - + let fullScale = aArgs ? aArgs.fullScale : false; let [contentWidth, contentHeight] = this.getContentSize(aWindow); let [thumbnailWidth, thumbnailHeight] = aDestCanvas ? [aDestCanvas.width, aDestCanvas.height] : this.getThumbnailSize(aWindow); + + // If the caller wants a fullscale image, set the desired thumbnail dims + // to the dims of content and (if provided) size the incoming canvas to + // support our results. + if (fullScale) { + thumbnailWidth = contentWidth; + thumbnailHeight = contentHeight; + if (aDestCanvas) { + aDestCanvas.width = contentWidth; + aDestCanvas.height = contentHeight; + } + } + let intermediateWidth = thumbnailWidth * 2; let intermediateHeight = thumbnailHeight * 2; let skipDownscale = false; - let snapshotCanvas = undefined; - // Our intermediate thumbnail is bigger than content, - // which can happen on hiDPI devices like a retina macbook pro. - // In those cases, just render at the final size. - if ((intermediateWidth >= contentWidth) || - (intermediateHeight >= contentHeight)) { + // If the intermediate thumbnail is larger than content dims (hiDPI + // devices can experience this) or a full preview is requested render + // at the final thumbnail size. + if ((intermediateWidth >= contentWidth || + intermediateHeight >= contentHeight) || fullScale) { intermediateWidth = thumbnailWidth; intermediateHeight = thumbnailHeight; skipDownscale = true; - snapshotCanvas = aDestCanvas; } - // If we've been given a large preallocated canvas, so - // just render once into the destination canvas. - if (aDestCanvas && - ((aDestCanvas.width >= intermediateWidth) || - (aDestCanvas.height >= intermediateHeight))) { - intermediateWidth = aDestCanvas.width; - intermediateHeight = aDestCanvas.height; - skipDownscale = true; - snapshotCanvas = aDestCanvas; - } + // Create an intermediate surface + let snapshotCanvas = this.createCanvas(aWindow, intermediateWidth, + intermediateHeight); - if (!snapshotCanvas) { - snapshotCanvas = this.createCanvas(aWindow, intermediateWidth, intermediateHeight); - } - - // This is step 1. + // Step 1: capture the image at the intermediate dims. For thumbnails + // this is twice the thumbnail size, for fullScale images this is at + // content dims. // Also by default, canvas does not draw the scrollbars, so no need to // remove the scrollbar sizes. let scale = Math.min(Math.max(intermediateWidth / contentWidth, @@ -195,18 +200,21 @@ this.PageThumbUtils = { PageThumbUtils.THUMBNAIL_BG_COLOR, snapshotCtx.DRAWWINDOW_DO_NOT_FLUSH); snapshotCtx.restore(); - if (skipDownscale) { - return snapshotCanvas; - } - // Part 2: Assumes that the snapshot is 2x the thumbnail size - let finalCanvas = aDestCanvas || this.createCanvas(aWindow, thumbnailWidth, thumbnailHeight); + // Part 2: Downscale from our intermediate dims to the final thumbnail + // dims and copy the result to aDestCanvas. If the caller didn't + // provide a target canvas, create a new canvas and return it. + let finalCanvas = aDestCanvas || + this.createCanvas(aWindow, thumbnailWidth, thumbnailHeight); let finalCtx = finalCanvas.getContext("2d"); finalCtx.save(); - finalCtx.scale(0.5, 0.5); + if (!skipDownscale) { + finalCtx.scale(0.5, 0.5); + } finalCtx.drawImage(snapshotCanvas, 0, 0); finalCtx.restore(); + return finalCanvas; }, diff --git a/toolkit/components/thumbnails/PageThumbs.jsm b/toolkit/components/thumbnails/PageThumbs.jsm index 711d8cf8f23a..954ee3d0ff82 100644 --- a/toolkit/components/thumbnails/PageThumbs.jsm +++ b/toolkit/components/thumbnails/PageThumbs.jsm @@ -207,13 +207,20 @@ this.PageThumbs = { * canvas asynchronously. Pass aCallback to receive an async callback after * canvas painting has completed. * @param aBrowser The browser to capture a thumbnail from. - * @param aCanvas The canvas to draw to. + * @param aCanvas The canvas to draw to. The thumbnail will be scaled to match + * the dimensions of this canvas. If callers pass a 0x0 canvas, the canvas + * will be resized to default thumbnail dimensions just prior to painting. * @param aCallback (optional) A callback invoked once the thumbnail has been - * rendered to aCanvas. + * rendered to aCanvas. + * @param aArgs (optional) Additional named parameters: + * fullScale - request that a non-downscaled image be returned. */ - captureToCanvas: function PageThumbs_captureToCanvas(aBrowser, aCanvas, aCallback) { + captureToCanvas: function (aBrowser, aCanvas, aCallback, aArgs) { let telemetryCaptureTime = new Date(); - this._captureToCanvas(aBrowser, aCanvas, function () { + let args = { + fullScale: aArgs ? aArgs.fullScale : false + }; + this._captureToCanvas(aBrowser, aCanvas, args, (aCanvas) => { Services.telemetry .getHistogramById("FX_THUMBNAILS_CAPTURE_TIME_MS") .add(new Date() - telemetryCaptureTime); @@ -257,14 +264,17 @@ this.PageThumbs = { // The background thumbnail service captures to canvas but doesn't want to // participate in this service's telemetry, which is why this method exists. - _captureToCanvas: function (aBrowser, aCanvas, aCallback) { + _captureToCanvas: function (aBrowser, aCanvas, aArgs, aCallback) { if (aBrowser.isRemoteBrowser) { Task.spawn(function* () { let data = - yield this._captureRemoteThumbnail(aBrowser, aCanvas); + yield this._captureRemoteThumbnail(aBrowser, aCanvas.width, + aCanvas.height, aArgs); let canvas = data.thumbnail; let ctx = canvas.getContext("2d"); let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); + aCanvas.width = canvas.width; + aCanvas.height = canvas.height; aCanvas.getContext("2d").putImageData(imgData, 0, 0); if (aCallback) { aCallback(aCanvas); @@ -272,8 +282,10 @@ this.PageThumbs = { }.bind(this)); return; } - - aCanvas = PageThumbUtils.createSnapshotThumbnail(aBrowser.contentWindow, aCanvas); + // The content is a local page, grab a thumbnail sync. + PageThumbUtils.createSnapshotThumbnail(aBrowser.contentWindow, + aCanvas, + aArgs); if (aCallback) { aCallback(aCanvas); @@ -284,10 +296,13 @@ this.PageThumbs = { * Asynchrnously render an appropriately scaled thumbnail to canvas. * * @param aBrowser The browser to capture a thumbnail from. - * @param aCanvas The canvas to draw to. + * @param aWidth The desired canvas width. + * @param aHeight The desired canvas height. + * @param aArgs (optional) Additional named parameters: + * fullScale - request that a non-downscaled image be returned. * @return a promise */ - _captureRemoteThumbnail: function (aBrowser, aCanvas) { + _captureRemoteThumbnail: function (aBrowser, aWidth, aHeight, aArgs) { let deferred = Promise.defer(); // The index we send with the request so we can identify the @@ -329,10 +344,11 @@ this.PageThumbs = { // Send a thumbnail request mm.addMessageListener("Browser:Thumbnail:Response", thumbFunc); mm.sendAsyncMessage("Browser:Thumbnail:Request", { - canvasWidth: aCanvas.width, - canvasHeight: aCanvas.height, + canvasWidth: aWidth, + canvasHeight: aHeight, background: PageThumbUtils.THUMBNAIL_BG_COLOR, - id: index + id: index, + additionalArgs: aArgs }); return deferred.promise; diff --git a/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js b/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js index 342847b74975..61386de5fdcc 100644 --- a/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js +++ b/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js @@ -133,7 +133,7 @@ const backgroundPageThumbsContent = { let canvasDrawDate = new Date(); - let finalCanvas = PageThumbUtils.createSnapshotThumbnail(content); + let finalCanvas = PageThumbUtils.createSnapshotThumbnail(content, null); capture.canvasDrawTime = new Date() - canvasDrawDate; finalCanvas.toBlob(blob => { diff --git a/toolkit/content/browser-child.js b/toolkit/content/browser-child.js index 11aca00401e3..cdc8bad0d66a 100644 --- a/toolkit/content/browser-child.js +++ b/toolkit/content/browser-child.js @@ -467,10 +467,18 @@ addMessageListener("UpdateCharacterSet", function (aMessage) { * Remote thumbnail request handler for PageThumbs thumbnails. */ addMessageListener("Browser:Thumbnail:Request", function (aMessage) { - let snapshotWidth = aMessage.data.canvasWidth; - let snapshotHeight = aMessage.data.canvasHeight; - let canvas = PageThumbUtils.createCanvas(content, snapshotWidth, snapshotHeight); - let snapshot = PageThumbUtils.createSnapshotThumbnail(content, canvas); + let snapshot; + let args = aMessage.data.additionalArgs; + let fullScale = args ? args.fullScale : false; + if (fullScale) { + snapshot = PageThumbUtils.createSnapshotThumbnail(content, null, args); + } else { + let snapshotWidth = aMessage.data.canvasWidth; + let snapshotHeight = aMessage.data.canvasHeight; + snapshot = + PageThumbUtils.createCanvas(content, snapshotWidth, snapshotHeight); + PageThumbUtils.createSnapshotThumbnail(content, snapshot, args); + } snapshot.toBlob(function (aBlob) { sendAsyncMessage("Browser:Thumbnail:Response", {