Bug 1576911 - Make ThumbnailsChild a JSWindowActorChild instead of ActorChild r=Gijs

Differential Revision: https://phabricator.services.mozilla.com/D45957

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Carson Greene 2019-11-06 23:32:35 +00:00
parent 6e2cb1503d
commit 64dd6427d1
13 changed files with 245 additions and 331 deletions

View File

@ -43,9 +43,9 @@ var tabPreviews = {
let browser = aTab.linkedBrowser;
let uri = browser.currentURI.spec;
let canvas = PageThumbs.createCanvas(window);
PageThumbs.shouldStoreThumbnail(browser, aDoStore => {
PageThumbs.shouldStoreThumbnail(browser).then(aDoStore => {
if (aDoStore && aShouldCache) {
PageThumbs.captureAndStore(browser, function() {
PageThumbs.captureAndStore(browser).then(function() {
let img = new Image();
img.src = PageThumbs.getThumbnailURL(uri);
aTab.__thumbnail = img;
@ -53,12 +53,14 @@ var tabPreviews = {
canvas.getContext("2d").drawImage(img, 0, 0);
});
} else {
PageThumbs.captureToCanvas(browser, canvas, () => {
if (aShouldCache) {
aTab.__thumbnail = canvas;
aTab.__thumbnail_lastURI = uri;
}
});
PageThumbs.captureToCanvas(browser, canvas)
.then(() => {
if (aShouldCache) {
aTab.__thumbnail = canvas;
aTab.__thumbnail_lastURI = uri;
}
})
.catch(e => Cu.reportError(e));
}
});
return canvas;

View File

@ -314,7 +314,9 @@ var StarUI = {
}
let canvas = PageThumbs.createCanvas(window);
PageThumbs.captureToCanvas(gBrowser.selectedBrowser, canvas);
PageThumbs.captureToCanvas(gBrowser.selectedBrowser, canvas).catch(e =>
Cu.reportError(e)
);
document.mozSetImageElement("editBookmarkPanelImageCanvas", canvas);
},

View File

@ -132,11 +132,9 @@ var gBrowserThumbnails = {
if (!aBrowser.currentURI || !topSites.includes(aBrowser.currentURI.spec)) {
return;
}
this._shouldCapture(aBrowser, function(aResult) {
if (aResult) {
PageThumbs.captureAndStoreIfStale(aBrowser);
}
});
if (await this._shouldCapture(aBrowser)) {
await PageThumbs.captureAndStoreIfStale(aBrowser);
}
},
_delayedCapture: function Thumbnails_delayedCapture(aBrowser) {
@ -163,16 +161,15 @@ var gBrowserThumbnails = {
this._timeouts.set(aBrowser, { isTimeout: true, id: timeoutId });
},
_shouldCapture: function Thumbnails_shouldCapture(aBrowser, aCallback) {
_shouldCapture: async function Thumbnails_shouldCapture(aBrowser) {
// Capture only if it's the currently selected tab and not an about: page.
if (
aBrowser != gBrowser.selectedBrowser ||
gBrowser.currentURI.schemeIs("about")
) {
aCallback(false);
return;
return false;
}
PageThumbs.shouldStoreThumbnail(aBrowser, aCallback);
return PageThumbs.shouldStoreThumbnail(aBrowser);
},
_cancelDelayedCapture: function Thumbnails_cancelDelayedCapture(aBrowser) {

View File

@ -470,11 +470,15 @@
}
// PageThumb is async with e10s but that's fine
// since we can update the image during the dnd.
PageThumbs.captureToCanvas(browser, canvas, captureListener);
PageThumbs.captureToCanvas(browser, canvas)
.then(captureListener)
.catch(e => Cu.reportError(e));
} else {
// For the non e10s case we can just use PageThumbs
// sync, so let's use the canvas for setDragImage.
PageThumbs.captureToCanvas(browser, canvas);
PageThumbs.captureToCanvas(browser, canvas).catch(e =>
Cu.reportError(e)
);
dragImageOffset = dragImageOffset * scale;
}
dt.setDragImage(toDrag, dragImageOffset, dragImageOffset);

View File

@ -235,19 +235,16 @@ PreviewController.prototype = {
* Capture a new thumbnail image for this preview. Called by the controller
* in response to a request for a new thumbnail image.
*/
updateCanvasPreview(aFullScale, aCallback) {
updateCanvasPreview(aFullScale) {
// Update our cached browser dims so that delayed resize
// events don't trigger another invalidation if this tab becomes active.
this.cacheBrowserDims();
PageThumbs.captureToCanvas(
this.linkedBrowser,
this.canvasPreview,
aCallback,
{ fullScale: aFullScale }
);
AeroPeek.resetCacheTimer();
return PageThumbs.captureToCanvas(this.linkedBrowser, this.canvasPreview, {
fullScale: aFullScale,
}).catch(e => Cu.reportError(e));
// If we're updating the canvas, then we're in the middle of a peek so
// don't discard the cache of previews.
AeroPeek.resetCacheTimer();
},
updateTitleAndTooltip() {
@ -288,7 +285,7 @@ PreviewController.prototype = {
requestPreview(aTaskbarCallback) {
// Grab a high res content preview
this.resetCanvasPreview();
this.updateCanvasPreview(true, aPreviewCanvas => {
this.updateCanvasPreview(true).then(aPreviewCanvas => {
let winWidth = this.win.width;
let winHeight = this.win.height;
@ -340,7 +337,7 @@ PreviewController.prototype = {
*/
requestThumbnail(aTaskbarCallback, aRequestedWidth, aRequestedHeight) {
this.resizeCanvasPreview(aRequestedWidth, aRequestedHeight);
this.updateCanvasPreview(false, aThumbnailCanvas => {
this.updateCanvasPreview(false).then(aThumbnailCanvas => {
aTaskbarCallback.done(aThumbnailCanvas, false);
});
},

View File

@ -5,10 +5,6 @@
"use strict";
var EXPORTED_SYMBOLS = ["ThumbnailsChild"];
const { ActorChild } = ChromeUtils.import(
"resource://gre/modules/ActorChild.jsm"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(
@ -17,65 +13,39 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/PageThumbUtils.jsm"
);
class ThumbnailsChild extends ActorChild {
class ThumbnailsChild extends JSWindowActorChild {
receiveMessage(message) {
if (message.name == "Browser:Thumbnail:Request") {
/**
* Remote thumbnail request handler for PageThumbs thumbnails.
*/
let snapshot;
let args = message.data.additionalArgs;
let fullScale = args ? args.fullScale : false;
if (fullScale) {
snapshot = PageThumbUtils.createSnapshotThumbnail(
this.content,
null,
args
);
} else {
let snapshotWidth = message.data.canvasWidth;
let snapshotHeight = message.data.canvasHeight;
snapshot = PageThumbUtils.createCanvas(
this.content,
snapshotWidth,
snapshotHeight
);
PageThumbUtils.createSnapshotThumbnail(this.content, snapshot, args);
switch (message.name) {
case "Browser:Thumbnail:ContentSize": {
return PageThumbUtils.getContentSize(this.contentWindow);
}
snapshot.toBlob(aBlob => {
this.mm.sendAsyncMessage("Browser:Thumbnail:Response", {
thumbnail: aBlob,
id: message.data.id,
});
});
} else if (message.name == "Browser:Thumbnail:CheckState") {
/**
* Remote isSafeForCapture request handler for PageThumbs.
*/
Services.tm.idleDispatchToMainThread(() => {
let result = PageThumbUtils.shouldStoreContentThumbnail(
this.content,
this.mm.docShell
case "Browser:Thumbnail:CheckState": {
/**
* Remote isSafeForCapture request handler for PageThumbs.
*/
return new Promise(resolve =>
Services.tm.idleDispatchToMainThread(() => {
let result = PageThumbUtils.shouldStoreContentThumbnail(
this.contentWindow,
this.browsingContext.docShell
);
resolve(result);
})
);
this.mm.sendAsyncMessage("Browser:Thumbnail:CheckState:Response", {
result,
});
});
} else if (message.name == "Browser:Thumbnail:GetOriginalURL") {
/**
* Remote GetOriginalURL request handler for PageThumbs.
*/
let channel = this.mm.docShell.currentDocumentChannel;
let channelError = PageThumbUtils.isChannelErrorResponse(channel);
let originalURL;
try {
originalURL = channel.originalURI.spec;
} catch (ex) {}
this.mm.sendAsyncMessage("Browser:Thumbnail:GetOriginalURL:Response", {
channelError,
originalURL,
});
}
case "Browser:Thumbnail:GetOriginalURL": {
/**
* Remote GetOriginalURL request handler for PageThumbs.
*/
let channel = this.browsingContext.docShell.currentDocumentChannel;
let channelError = PageThumbUtils.isChannelErrorResponse(channel);
let originalURL;
try {
originalURL = channel.originalURI.spec;
} catch (ex) {}
return { channelError, originalURL };
}
}
return undefined;
}
}

View File

@ -12,8 +12,6 @@ const LATEST_STORAGE_VERSION = 3;
const EXPIRATION_MIN_CHUNK_SIZE = 50;
const EXPIRATION_INTERVAL_SECS = 3600;
var gRemoteThumbId = 0;
// If a request for a thumbnail comes in and we find one that is "stale"
// (or don't find one at all) we automatically queue a request to generate a
// new one.
@ -175,11 +173,13 @@ var PageThumbs = {
return new Promise(resolve => {
let canvas = this.createCanvas(aBrowser.ownerGlobal);
this.captureToCanvas(aBrowser, canvas, () => {
canvas.toBlob(blob => {
resolve(blob, this.contentType);
});
});
this.captureToCanvas(aBrowser, canvas)
.then(() => {
canvas.toBlob(blob => {
resolve(blob, this.contentType);
});
})
.catch(e => Cu.reportError(e));
});
},
@ -192,23 +192,19 @@ var PageThumbs = {
* @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.
* @param aArgs (optional) Additional named parameters:
* fullScale - request that a non-downscaled image be returned.
*/
captureToCanvas(aBrowser, aCanvas, aCallback, aArgs) {
async captureToCanvas(aBrowser, aCanvas, aArgs) {
let telemetryCaptureTime = new Date();
let args = {
fullScale: aArgs ? aArgs.fullScale : false,
};
this._captureToCanvas(aBrowser, aCanvas, args, aCanvas => {
return this._captureToCanvas(aBrowser, aCanvas, args).then(() => {
Services.telemetry
.getHistogramById("FX_THUMBNAILS_CAPTURE_TIME_MS")
.add(new Date() - telemetryCaptureTime);
if (aCallback) {
aCallback(aCanvas);
}
return aCanvas;
});
},
@ -220,69 +216,52 @@ var PageThumbs = {
* content being displayed.
*
* @param aBrowser The target browser
* @param aCallback(aResult) A callback invoked once security checks have
* completed. aResult is a boolean indicating the combined result of the
* security checks performed.
*/
shouldStoreThumbnail(aBrowser, aCallback) {
async shouldStoreThumbnail(aBrowser) {
// Don't capture in private browsing mode.
if (PrivateBrowsingUtils.isBrowserPrivate(aBrowser)) {
aCallback(false);
return;
return false;
}
if (aBrowser.isRemoteBrowser) {
let mm = aBrowser.messageManager;
let resultFunc = function(aMsg) {
mm.removeMessageListener(
"Browser:Thumbnail:CheckState:Response",
resultFunc
if (aBrowser.browsingContext.currentWindowGlobal) {
let thumbnailsActor = aBrowser.browsingContext.currentWindowGlobal.getActor(
"Thumbnails"
);
aCallback(aMsg.data.result);
};
mm.addMessageListener(
"Browser:Thumbnail:CheckState:Response",
resultFunc
);
try {
mm.sendAsyncMessage("Browser:Thumbnail:CheckState");
} catch (ex) {
Cu.reportError(ex);
// If the message manager is not able send our message, taking a content
// screenshot is also not going to work: return false.
resultFunc({ data: { result: false } });
return thumbnailsActor
.sendQuery("Browser:Thumbnail:CheckState")
.catch(err => {
return false;
});
}
} else {
aCallback(
PageThumbUtils.shouldStoreContentThumbnail(
aBrowser.contentDocument,
aBrowser.docShell
)
);
return false;
}
return PageThumbUtils.shouldStoreContentThumbnail(
aBrowser.contentDocument,
aBrowser.docShell
);
},
// 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(aBrowser, aCanvas, aArgs, aCallback) {
async _captureToCanvas(aBrowser, aCanvas, aArgs) {
if (aBrowser.isRemoteBrowser) {
(async () => {
let data = await 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;
let thumbnail = await this._captureRemoteThumbnail(
aBrowser,
aCanvas.width,
aCanvas.height,
aArgs
);
// 'thumbnail' can be null if the browser has navigated away after starting
// the thumbnail request, so we check it here.
if (thumbnail) {
let ctx = thumbnail.getContext("2d");
let imgData = ctx.getImageData(0, 0, thumbnail.width, thumbnail.height);
aCanvas.width = thumbnail.width;
aCanvas.height = thumbnail.height;
aCanvas.getContext("2d").putImageData(imgData, 0, 0);
if (aCallback) {
aCallback(aCanvas);
}
})();
return;
}
return aCanvas;
}
// The content is a local page, grab a thumbnail sync.
PageThumbUtils.createSnapshotThumbnail(
@ -290,10 +269,7 @@ var PageThumbs = {
aCanvas,
aArgs
);
if (aCallback) {
aCallback(aCanvas);
}
return aCanvas;
},
/**
@ -306,65 +282,48 @@ var PageThumbs = {
* fullScale - request that a non-downscaled image be returned.
* @return a promise
*/
_captureRemoteThumbnail(aBrowser, aWidth, aHeight, aArgs) {
return new Promise(resolve => {
// The index we send with the request so we can identify the
// correct response.
let index = gRemoteThumbId++;
// Thumbnail request response handler
let mm = aBrowser.messageManager;
// Browser:Thumbnail:Response handler
let thumbFunc = function(aMsg) {
// Ignore events unrelated to our request
if (aMsg.data.id != index) {
return;
}
mm.removeMessageListener("Browser:Thumbnail:Response", thumbFunc);
let imageBlob = aMsg.data.thumbnail;
let doc = aBrowser.parentElement.ownerDocument;
let reader = new FileReader();
reader.addEventListener("loadend", function() {
let image = doc.createElementNS(PageThumbUtils.HTML_NAMESPACE, "img");
image.onload = function() {
let thumbnail = doc.createElementNS(
PageThumbUtils.HTML_NAMESPACE,
"canvas"
);
thumbnail.width = image.naturalWidth;
thumbnail.height = image.naturalHeight;
let ctx = thumbnail.getContext("2d");
ctx.drawImage(image, 0, 0);
resolve({
thumbnail,
});
};
image.src = reader.result;
});
// xxx wish there was a way to skip this encoding step
reader.readAsDataURL(imageBlob);
};
// Send a thumbnail request
mm.addMessageListener("Browser:Thumbnail:Response", thumbFunc);
mm.sendAsyncMessage("Browser:Thumbnail:Request", {
canvasWidth: aWidth,
canvasHeight: aHeight,
background: PageThumbUtils.THUMBNAIL_BG_COLOR,
id: index,
additionalArgs: aArgs,
});
});
async _captureRemoteThumbnail(aBrowser, aWidth, aHeight, aArgs) {
if (!aBrowser.browsingContext) {
return null;
}
let thumbnailsActor = aBrowser.browsingContext.currentWindowGlobal.getActor(
"Thumbnails"
);
let [contentWidth, contentHeight] = await thumbnailsActor.sendQuery(
"Browser:Thumbnail:ContentSize"
);
let fullScale = aArgs ? aArgs.fullScale : false;
let scale = fullScale
? 1
: Math.min(Math.max(aWidth / contentWidth, aHeight / contentHeight), 1);
let image = await aBrowser.drawSnapshot(
0,
0,
contentWidth,
contentHeight,
scale,
PageThumbUtils.THUMBNAIL_BG_COLOR
);
if (aBrowser.parentElement) {
let doc = aBrowser.parentElement.ownerDocument;
let thumbnail = doc.createElementNS(
PageThumbUtils.HTML_NAMESPACE,
"canvas"
);
thumbnail.width = aWidth;
thumbnail.height = aHeight;
let ctx = thumbnail.getContext("2d");
ctx.drawImage(image, 0, 0);
return thumbnail;
}
return null;
},
/**
* Captures a thumbnail for the given browser and stores it to the cache.
* @param aBrowser The browser to capture a thumbnail for.
* @param aCallback The function to be called when finished (optional).
*/
captureAndStore: function PageThumbs_captureAndStore(aBrowser, aCallback) {
captureAndStore: async function PageThumbs_captureAndStore(aBrowser) {
if (!this._prefEnabled()) {
return;
}
@ -373,41 +332,30 @@ var PageThumbs = {
let originalURL;
let channelError = false;
(async () => {
if (!aBrowser.isRemoteBrowser) {
let channel = aBrowser.docShell.currentDocumentChannel;
originalURL = channel.originalURI.spec;
// see if this was an error response.
channelError = PageThumbUtils.isChannelErrorResponse(channel);
} else {
let resp = await new Promise(resolve => {
let mm = aBrowser.messageManager;
let respName = "Browser:Thumbnail:GetOriginalURL:Response";
mm.addMessageListener(respName, function onResp(msg) {
mm.removeMessageListener(respName, onResp);
resolve(msg.data);
});
mm.sendAsyncMessage("Browser:Thumbnail:GetOriginalURL");
});
originalURL = resp.originalURL || url;
channelError = resp.channelError;
}
if (!aBrowser.isRemoteBrowser) {
let channel = aBrowser.docShell.currentDocumentChannel;
originalURL = channel.originalURI.spec;
// see if this was an error response.
channelError = PageThumbUtils.isChannelErrorResponse(channel);
} else {
let thumbnailsActor = aBrowser.browsingContext.currentWindowGlobal.getActor(
"Thumbnails"
);
let resp = await thumbnailsActor.sendQuery(
"Browser:Thumbnail:GetOriginalURL"
);
let isSuccess = true;
try {
let blob = await this.captureToBlob(aBrowser);
let buffer = await TaskUtils.readBlob(blob);
await this._store(originalURL, url, buffer, channelError);
} catch (ex) {
Cu.reportError(
"Exception thrown during thumbnail capture: '" + ex + "'"
);
isSuccess = false;
}
if (aCallback) {
aCallback(isSuccess);
}
})();
originalURL = resp.originalURL || url;
channelError = resp.channelError;
}
try {
let blob = await this.captureToBlob(aBrowser);
let buffer = await TaskUtils.readBlob(blob);
await this._store(originalURL, url, buffer, channelError);
} catch (ex) {
Cu.reportError("Exception thrown during thumbnail capture: '" + ex + "'");
}
},
/**
@ -417,33 +365,30 @@ var PageThumbs = {
* such notifications if they want to be notified of an updated thumbnail.
*
* @param aBrowser The content window of this browser will be captured.
* @param aCallback The function to be called when finished (optional).
*/
captureAndStoreIfStale: function PageThumbs_captureAndStoreIfStale(
aBrowser,
aCallback
captureAndStoreIfStale: async function PageThumbs_captureAndStoreIfStale(
aBrowser
) {
if (!aBrowser.currentURI) {
return false;
}
let url = aBrowser.currentURI.spec;
PageThumbsStorage.isFileRecentForURL(url).then(
recent => {
if (
!recent &&
// Careful, the call to PageThumbsStorage is async, so the browser may
// have navigated away from the URL or even closed.
aBrowser.currentURI &&
aBrowser.currentURI.spec == url
) {
this.captureAndStore(aBrowser, aCallback);
} else if (aCallback) {
aCallback(true);
}
},
err => {
if (aCallback) {
aCallback(false);
}
}
);
let recent;
try {
recent = await PageThumbsStorage.isFileRecentForURL(url);
} catch {
return false;
}
if (
!recent &&
// Careful, the call to PageThumbsStorage is async, so the browser may
// have navigated away from the URL or even closed.
aBrowser.currentURI &&
aBrowser.currentURI.spec == url
) {
await this.captureAndStore(aBrowser);
}
return true;
},
/**
@ -456,42 +401,35 @@ var PageThumbs = {
* @param aData An ArrayBuffer containing the image data.
* @param aNoOverwrite If true and files for the URLs already exist, the files
* will not be overwritten.
* @return {Promise}
*/
_store: function PageThumbs__store(
_store: async function PageThumbs__store(
aOriginalURL,
aFinalURL,
aData,
aNoOverwrite
) {
return (async function() {
let telemetryStoreTime = new Date();
await PageThumbsStorage.writeData(aFinalURL, aData, aNoOverwrite);
Services.telemetry
.getHistogramById("FX_THUMBNAILS_STORE_TIME_MS")
.add(new Date() - telemetryStoreTime);
let telemetryStoreTime = new Date();
await PageThumbsStorage.writeData(aFinalURL, aData, aNoOverwrite);
Services.telemetry
.getHistogramById("FX_THUMBNAILS_STORE_TIME_MS")
.add(new Date() - telemetryStoreTime);
Services.obs.notifyObservers(null, "page-thumbnail:create", aFinalURL);
// We've been redirected. Create a copy of the current thumbnail for
// the redirect source. We need to do this because:
//
// 1) Users can drag any kind of links onto the newtab page. If those
// links redirect to a different URL then we want to be able to
// provide thumbnails for both of them.
//
// 2) The newtab page should actually display redirect targets, only.
// Because of bug 559175 this information can get lost when using
// Sync and therefore also redirect sources appear on the newtab
// page. We also want thumbnails for those.
if (aFinalURL != aOriginalURL) {
await PageThumbsStorage.copy(aFinalURL, aOriginalURL, aNoOverwrite);
Services.obs.notifyObservers(
null,
"page-thumbnail:create",
aOriginalURL
);
}
})();
Services.obs.notifyObservers(null, "page-thumbnail:create", aFinalURL);
// We've been redirected. Create a copy of the current thumbnail for
// the redirect source. We need to do this because:
//
// 1) Users can drag any kind of links onto the newtab page. If those
// links redirect to a different URL then we want to be able to
// provide thumbnails for both of them.
//
// 2) The newtab page should actually display redirect targets, only.
// Because of bug 559175 this information can get lost when using
// Sync and therefore also redirect sources appear on the newtab
// page. We also want thumbnails for those.
if (aFinalURL != aOriginalURL) {
await PageThumbsStorage.copy(aFinalURL, aOriginalURL, aNoOverwrite);
Services.obs.notifyObservers(null, "page-thumbnail:create", aOriginalURL);
}
},
/**

View File

@ -12,7 +12,7 @@ function* runTests() {
yield BrowserTestUtils.waitForContentEvent(browser, "DOMContentLoaded");
yield new Promise(resolve => {
PageThumbs.shouldStoreThumbnail(browser, aResult => {
PageThumbs.shouldStoreThumbnail(browser).then(aResult => {
ok(!aResult, "we're not going to capture an error page");
resolve();
});

View File

@ -64,13 +64,12 @@ function testCombination(combi, url, aCombinations, aResult) {
let tab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, url));
let browser = gBrowser.selectedBrowser;
BrowserTestUtils.browserLoaded(browser).then(() => {
BrowserTestUtils.browserLoaded(browser).then(async () => {
let msg = JSON.stringify(combi) + " == " + aResult;
PageThumbs.shouldStoreThumbnail(browser, aIsSafeSite => {
is(aIsSafeSite, aResult, msg);
gBrowser.removeTab(tab);
// Continue with the next combination.
checkCombinations(aCombinations, aResult);
});
let aIsSafeSite = await PageThumbs.shouldStoreThumbnail(browser);
is(aIsSafeSite, aResult, msg);
gBrowser.removeTab(tab);
// Continue with the next combination.
checkCombinations(aCombinations, aResult);
});
}

View File

@ -64,12 +64,12 @@ function* simpleCaptureTest() {
let browser = gBrowser.selectedBrowser;
// Capture the screenshot.
PageThumbs.captureAndStore(browser, function() {
PageThumbs.captureAndStore(browser).then(function() {
// We've got a capture so should have seen the observer.
is(numNotifications, 1, "got notification of item being created.");
// The capture is now "fresh" - so requesting the URL should not cause
// a new capture.
PageThumbs.captureAndStoreIfStale(browser, function() {
PageThumbs.captureAndStoreIfStale(browser).then(function() {
is(
numNotifications,
1,
@ -105,7 +105,9 @@ function* capIfStaleErrorResponseUpdateTest() {
// As we set the thumbnail very stale, allowing 1 second of "slop" here
// works around this while still keeping the test valid.
let now = Date.now() - 1000;
PageThumbs.captureAndStoreIfStale(gBrowser.selectedBrowser, () => {
PageThumbs.captureAndStoreIfStale(gBrowser.selectedBrowser).then(() => {
PageThumbsStorageService.getFilePathForURL(URL);
ok(getThumbnailModifiedTime(URL) < now, "modified time should be < now");
retrieveImageDataForURL(URL, function([r, g, b]) {
is("" + [r, g, b], "" + [0, 255, 0], "thumbnail is still green");
@ -136,7 +138,7 @@ function* capIfStaleGoodResponseUpdateTest() {
// As we set the thumbnail very stale, allowing 1 second of "slop" here
// works around this while still keeping the test valid.
let now = Date.now() - 1000;
PageThumbs.captureAndStoreIfStale(browser, () => {
PageThumbs.captureAndStoreIfStale(browser).then(() => {
ok(getThumbnailModifiedTime(URL) >= now, "modified time should be >= now");
// the captureAndStoreIfStale request saw a 200 response with the red body,
// so we expect to see the red version here.

View File

@ -157,18 +157,17 @@ function whenLoaded(aElement, aCallback = next) {
* @param aBlue The blue component's intensity.
* @param aMessage The info message to print when comparing the pixel color.
*/
function captureAndCheckColor(aRed, aGreen, aBlue, aMessage) {
async function captureAndCheckColor(aRed, aGreen, aBlue, aMessage) {
let browser = gBrowser.selectedBrowser;
// We'll get oranges if the expiration filter removes the file during the
// test.
dontExpireThumbnailURLs([browser.currentURI.spec]);
// Capture the screenshot.
PageThumbs.captureAndStore(browser, function() {
retrieveImageDataForURL(browser.currentURI.spec, function([r, g, b]) {
is("" + [r, g, b], "" + [aRed, aGreen, aBlue], aMessage);
next();
});
await PageThumbs.captureAndStore(browser);
retrieveImageDataForURL(browser.currentURI.spec, function([r, g, b]) {
is("" + [r, g, b], "" + [aRed, aGreen, aBlue], aMessage);
next();
});
}

View File

@ -685,7 +685,10 @@
}
get browsingContext() {
return this.frameLoader.browsingContext;
if (this.frameLoader) {
return this.frameLoader.browsingContext;
}
return null;
}
/**
* Note that this overrides webNavigation on XULFrameElement, and duplicates the return value for the non-remote case
@ -2086,11 +2089,17 @@
);
}
drawSnapshot(x, y, w, h, scale, backgroundColor) {
if (!this.frameLoader) {
throw Components.Exception("No frame loader.", Cr.NS_ERROR_FAILURE);
async drawSnapshot(x, y, w, h, scale, backgroundColor) {
let rect = new DOMRect(x, y, w, h);
try {
return this.browsingContext.currentWindowGlobal.drawSnapshot(
rect,
scale,
backgroundColor
);
} catch (e) {
return false;
}
return this.frameLoader.drawSnapshot(x, y, w, h, scale, backgroundColor);
}
dropLinks(aLinks, aTriggeringPrincipal) {

View File

@ -381,6 +381,12 @@ let ACTORS = {
allFrames: true,
},
Thumbnails: {
child: {
moduleURI: "resource://gre/actors/ThumbnailsChild.jsm",
},
},
Zoom: {
parent: {
moduleURI: "resource://gre/actors/ZoomParent.jsm",
@ -478,17 +484,6 @@ let LEGACY_ACTORS = {
},
},
Thumbnails: {
child: {
module: "resource://gre/actors/ThumbnailsChild.jsm",
messages: [
"Browser:Thumbnail:Request",
"Browser:Thumbnail:CheckState",
"Browser:Thumbnail:GetOriginalURL",
],
},
},
UnselectedTabHover: {
child: {
module: "resource://gre/actors/UnselectedTabHoverChild.jsm",