Bug 677310 - Thumbnails are lost when switching to private browsing mode; r=dietrich

This commit is contained in:
Tim Taubert 2011-09-01 15:17:27 +02:00
Родитель 0c839a3e27
Коммит a13ddf90c3
11 изменённых файлов: 433 добавлений и 247 удалений

Просмотреть файл

@ -99,47 +99,27 @@ let Storage = {
saveTab: function Storage_saveTab(tab, data) {
Utils.assert(tab, "tab");
if (data != null) {
let imageData = data.imageData;
// Remove imageData from payload
delete data.imageData;
if (imageData != null)
ThumbnailStorage.saveThumbnail(tab, imageData);
}
this._sessionStore.setTabValue(tab, this.TAB_DATA_IDENTIFIER,
JSON.stringify(data));
},
// ----------
// Function: getTabData
// Load tab data from session store and return it. Asynchrously loads the tab's
// thumbnail from the cache and calls <callback>(imageData) when done.
getTabData: function Storage_getTabData(tab, callback) {
// Load tab data from session store and return it.
getTabData: function Storage_getTabData(tab) {
Utils.assert(tab, "tab");
Utils.assert(typeof callback == "function", "callback arg must be a function");
let existingData = null;
try {
let tabData = this._sessionStore.getTabValue(tab, this.TAB_DATA_IDENTIFIER);
if (tabData != "") {
if (tabData != "")
existingData = JSON.parse(tabData);
}
} catch (e) {
// getTabValue will fail if the property doesn't exist.
Utils.log(e);
}
if (existingData) {
ThumbnailStorage.loadThumbnail(
tab, existingData.url,
function(status, imageData) {
callback(imageData);
}
);
}
return existingData;
},

Просмотреть файл

@ -69,6 +69,7 @@ function TabItem(tab, options) {
let $div = iQ(div);
this._cachedImageData = null;
this._thumbnailNeedsSaving = false;
this.canvasSizeForced = false;
this.$thumb = iQ('.thumb', $div);
this.$fav = iQ('.favicon', $div);
@ -80,19 +81,23 @@ function TabItem(tab, options) {
this.tabCanvas = new TabCanvas(this.tab, this.$canvas[0]);
let self = this;
// when we paint onto the canvas make sure our thumbnail gets saved
this.tabCanvas.addSubscriber("painted", function () {
self._thumbnailNeedsSaving = true;
});
this.defaultSize = new Point(TabItems.tabWidth, TabItems.tabHeight);
this._hidden = false;
this.isATabItem = true;
this.keepProportional = true;
this._hasBeenDrawn = false;
this._reconnected = false;
this.isDragging = false;
this.isStacked = false;
this.url = "";
var self = this;
this.isDragging = false;
// Read off the total vertical and horizontal padding on the tab container
// and cache this value, as it must be the same for every TabItem.
if (Utils.isEmptyObject(TabItems.tabItemPadding)) {
@ -194,11 +199,14 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
//
// Parameters:
// tabData - the tab data
showCachedData: function TabItem_showCachedData(tabData) {
this._cachedImageData = tabData.imageData;
// imageData - the image data
showCachedData: function TabItem_showCachedData(tabData, imageData) {
this._cachedImageData = imageData;
this.$cachedThumb.attr("src", this._cachedImageData).show();
this.$canvas.css({opacity: 0.0});
this.$canvas.css({opacity: 0});
this.$tabTitle.text(tabData.title ? tabData.title : "");
this._sendToSubscribers("showingCachedData");
},
// ----------
@ -214,39 +222,23 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// ----------
// Function: getStorageData
// Get data to be used for persistent storage of this object.
//
// Parameters:
// getImageData - true to include thumbnail pixels (and page title as well); default false
getStorageData: function TabItem_getStorageData(getImageData) {
let imageData = null;
if (getImageData) {
if (this._cachedImageData)
imageData = this._cachedImageData;
else if (this.tabCanvas)
imageData = this.tabCanvas.toImageData();
}
getStorageData: function TabItem_getStorageData() {
return {
url: this.tab.linkedBrowser.currentURI.spec,
groupID: (this.parent ? this.parent.id : 0),
imageData: imageData,
title: getImageData && this.tab.label || null
title: this.tab.label
};
},
// ----------
// Function: save
// Store persistent for this object.
//
// Parameters:
// saveImageData - true to include thumbnail pixels (and page title as well); default false
save: function TabItem_save(saveImageData) {
save: function TabItem_save() {
try {
if (!this.tab || this.tab.parentNode == null || !this._reconnected) // too soon/late to save
return;
var data = this.getStorageData(saveImageData);
let data = this.getStorageData();
if (TabItems.storageSanity(data))
Storage.saveTab(this.tab, data);
} catch(e) {
@ -254,6 +246,91 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
}
},
// ----------
// Function: loadThumbnail
// Loads the tabItems thumbnail.
loadThumbnail: function TabItem_loadThumbnail(tabData) {
Utils.assert(tabData, "invalid or missing argument <tabData>");
let self = this;
function TabItem_loadThumbnail_callback(error, imageData) {
// we could have been unlinked while waiting for the thumbnail to load
if (error || !imageData || !self.tab)
return;
self._sendToSubscribers("loadedCachedImageData");
// If we have a cached image, then show it if the loaded URL matches
// what the cache is from, OR the loaded URL is blank, which means
// that the page hasn't loaded yet.
let currentUrl = self.tab.linkedBrowser.currentURI.spec;
if (tabData.url == currentUrl || currentUrl == "about:blank")
self.showCachedData(tabData, imageData);
}
ThumbnailStorage.loadThumbnail(tabData.url, TabItem_loadThumbnail_callback);
},
// ----------
// Function: saveThumbnail
// Saves the tabItems thumbnail.
saveThumbnail: function TabItem_saveThumbnail(options) {
if (!this.tabCanvas)
return;
// nothing to do if the thumbnail hasn't changed
if (!this._thumbnailNeedsSaving)
return;
// check the storage policy to see if we're allowed to store the thumbnail
if (!StoragePolicy.canStoreThumbnailForTab(this.tab)) {
this._sendToSubscribers("deniedToSaveImageData");
return;
}
let url = this.tab.linkedBrowser.currentURI.spec;
let delayed = this._saveThumbnailDelayed;
let synchronously = (options && options.synchronously);
// is there a delayed save waiting?
if (delayed) {
// check if url has changed since last call to saveThumbnail
if (!synchronously && url == delayed.url)
return;
// url has changed in the meantime, clear the timeout
clearTimeout(delayed.timeout);
}
let self = this;
function callback(error) {
if (!error) {
self._thumbnailNeedsSaving = false;
self._sendToSubscribers("savedCachedImageData");
}
}
function doSaveThumbnail() {
self._saveThumbnailDelayed = null;
// we could have been unlinked in the meantime
if (!self.tabCanvas)
return;
let imageData = self.tabCanvas.toImageData();
ThumbnailStorage.saveThumbnail(url, imageData, callback, options);
}
if (synchronously) {
doSaveThumbnail();
} else {
let timeout = setTimeout(doSaveThumbnail, 2000);
this._saveThumbnailDelayed = {url: url, timeout: timeout};
}
},
// ----------
// Function: _reconnect
// Load the reciever's persistent data from storage. If there is none,
@ -262,29 +339,12 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
Utils.assertThrow(!this._reconnected, "shouldn't already be reconnected");
Utils.assertThrow(this.tab, "should have a xul:tab");
let tabData = null;
let self = this;
let imageDataCb = function(imageData) {
// we could have been unlinked while waiting for the thumbnail to load
if (!self.tab)
return;
let tabData = Storage.getTabData(this.tab);
Utils.assertThrow(tabData, "tabData");
tabData.imageData = imageData;
let currentUrl = self.tab.linkedBrowser.currentURI.spec;
// If we have a cached image, then show it if the loaded URL matches
// what the cache is from, OR the loaded URL is blank, which means
// that the page hasn't loaded yet.
if (tabData.imageData &&
(tabData.url == currentUrl || currentUrl == 'about:blank')) {
self.showCachedData(tabData);
}
};
// getTabData returns the sessionstore contents, but passes
// a callback to run when the thumbnail is finally loaded.
tabData = Storage.getTabData(this.tab, imageDataCb);
if (tabData && TabItems.storageSanity(tabData)) {
this.loadThumbnail(tabData);
if (self.parent)
self.parent.remove(self, {immediately: true});
@ -936,6 +996,7 @@ let TabItems = {
tabItem._lastTabUpdateTime = this._lastUpdateTime;
tabItem.tabCanvas.paint();
tabItem.saveThumbnail();
// ___ cache
if (tabItem.isShowingCachedData())
@ -1146,13 +1207,22 @@ let TabItems = {
// ----------
// Function: saveAll
// Saves all open <TabItem>s.
//
// Parameters:
// saveImageData - true to include thumbnail pixels (and page title as well); default false
saveAll: function TabItems_saveAll(saveImageData) {
var items = this.getItems();
items.forEach(function(item) {
item.save(saveImageData);
saveAll: function TabItems_saveAll() {
let tabItems = this.getItems();
tabItems.forEach(function TabItems_saveAll_forEach(tabItem) {
tabItem.save();
});
},
// ----------
// Function: saveAllThumbnails
// Saves thumbnails of all open <TabItem>s.
saveAllThumbnails: function TabItems_saveAllThumbnails(options) {
let tabItems = this.getItems();
tabItems.forEach(function TabItems_saveAllThumbnails_forEach(tabItem) {
tabItem.saveThumbnail(options);
});
},
@ -1342,7 +1412,7 @@ function TabCanvas(tab, canvas) {
this.canvas = canvas;
};
TabCanvas.prototype = {
TabCanvas.prototype = Utils.extend(new Subscribable(), {
// ----------
// Function: toString
// Prints [TabCanvas (tab)] for debug use
@ -1386,6 +1456,8 @@ TabCanvas.prototype = {
// Draw directly to the destination canvas
this._drawWindow(ctx, w, h, bgColor);
}
this._sendToSubscribers("painted");
},
// ----------
@ -1454,4 +1526,4 @@ TabCanvas.prototype = {
toImageData: function TabCanvas_toImageData() {
return this.canvas.toDataURL("image/png");
}
};
});

Просмотреть файл

@ -84,20 +84,38 @@ let ThumbnailStorage = {
// Opens a cache entry for the given <url> and requests access <access>.
// Calls <successCallback>(entry) when the entry was successfully opened with
// requested access rights. Otherwise calls <errorCallback>().
_openCacheEntry: function ThumbnailStorage__openCacheEntry(url, access, successCallback, errorCallback) {
let onCacheEntryAvailable = function(entry, accessGranted, status) {
//
// Parameters:
// url - the url to use as the storage key
// access - access flags, see Ci.nsICache.ACCESS_*
// successCallback - the callback to be called on success
// errorCallback - the callback to be called when an error occured
// options - an object with additional parameters, see below
//
// Possible options:
// synchronously - set to true to force sync mode
_openCacheEntry:
function ThumbnailStorage__openCacheEntry(url, access, successCallback,
errorCallback, options) {
Utils.assert(url, "invalid or missing argument <url>");
Utils.assert(access, "invalid or missing argument <access>");
Utils.assert(successCallback, "invalid or missing argument <successCallback>");
Utils.assert(errorCallback, "invalid or missing argument <errorCallback>");
function onCacheEntryAvailable(entry, accessGranted, status) {
if (entry && access == accessGranted && Components.isSuccessCode(status)) {
successCallback(entry);
} else {
entry && entry.close();
if (entry)
entry.close();
errorCallback();
}
}
let key = this.CACHE_PREFIX + url;
// switch to synchronous mode if parent window is about to close
if (UI.isDOMWindowClosing) {
if (options && options.synchronously) {
let entry = this._cacheSession.openCacheEntry(key, access, true);
let status = Cr.NS_OK;
onCacheEntryAvailable(entry, entry.accessGranted, status);
@ -109,47 +127,38 @@ let ThumbnailStorage = {
// ----------
// Function: saveThumbnail
// Saves the <imageData> to the cache using the given <url> as key.
// Calls <callback>(status, data) when finished, passing true or false
// (indicating whether the operation succeeded).
saveThumbnail: function ThumbnailStorage_saveThumbnail(tab, imageData, callback) {
Utils.assert(tab, "tab");
Utils.assert(imageData, "imageData");
if (!StoragePolicy.canStoreThumbnailForTab(tab)) {
tab._tabViewTabItem._sendToSubscribers("deniedToCacheImageData");
if (callback)
callback(false);
return;
}
// Saves the given thumbnail in the cache.
//
// Parameters:
// url - the url to use as the storage key
// imageData - the image data to save for the given key
// callback - the callback that is called when the operation is finished
// options - an object with additional parameters, see below
//
// Possible options:
// synchronously - set to true to force sync mode
saveThumbnail:
function ThumbnailStorage_saveThumbnail(url, imageData, callback, options) {
Utils.assert(url, "invalid or missing argument <url>");
Utils.assert(imageData, "invalid or missing argument <imageData>");
Utils.assert(callback, "invalid or missing argument <callback>");
let synchronously = (options && options.synchronously);
let self = this;
let completed = function(status) {
if (callback)
callback(status);
if (status) {
// Notify subscribers
tab._tabViewTabItem._sendToSubscribers("savedCachedImageData");
} else {
Utils.log("Error while saving thumbnail: " + e);
}
};
let onCacheEntryAvailable = function(entry) {
function onCacheEntryAvailable(entry) {
let outputStream = entry.openOutputStream(0);
let cleanup = function() {
function cleanup() {
outputStream.close();
entry.close();
}
// switch to synchronous mode if parent window is about to close
if (UI.isDOMWindowClosing) {
// synchronous mode
if (synchronously) {
outputStream.write(imageData, imageData.length);
cleanup();
completed(true);
callback();
return;
}
@ -158,43 +167,32 @@ let ThumbnailStorage = {
gNetUtil.asyncCopy(inputStream, outputStream, function (result) {
cleanup();
inputStream.close();
completed(Components.isSuccessCode(result));
callback(Components.isSuccessCode(result) ? "" : "failure");
});
}
let onCacheEntryUnavailable = function() {
completed(false);
function onCacheEntryUnavailable() {
callback("unavailable");
}
this._openCacheEntry(tab.linkedBrowser.currentURI.spec,
Ci.nsICache.ACCESS_WRITE, onCacheEntryAvailable,
onCacheEntryUnavailable);
this._openCacheEntry(url, Ci.nsICache.ACCESS_WRITE, onCacheEntryAvailable,
onCacheEntryUnavailable, options);
},
// ----------
// Function: loadThumbnail
// Asynchrously loads image data from the cache using the given <url> as key.
// Calls <callback>(status, data) when finished, passing true or false
// (indicating whether the operation succeeded) and the retrieved image data.
loadThumbnail: function ThumbnailStorage_loadThumbnail(tab, url, callback) {
Utils.assert(tab, "tab");
Utils.assert(url, "url");
Utils.assert(typeof callback == "function", "callback arg must be a function");
// Loads a thumbnail from the cache.
//
// Parameters:
// url - the url to use as the storage key
// callback - the callback that is called when the operation is finished
loadThumbnail: function ThumbnailStorage_loadThumbnail(url, callback) {
Utils.assert(url, "invalid or missing argument <url>");
Utils.assert(callback, "invalid or missing argument <callback>");
let self = this;
let completed = function(status, imageData) {
callback(status, imageData);
if (status) {
// Notify subscribers
tab._tabViewTabItem._sendToSubscribers("loadedCachedImageData");
} else {
Utils.log("Error while loading thumbnail");
}
}
let onCacheEntryAvailable = function(entry) {
function onCacheEntryAvailable(entry) {
let imageChunks = [];
let nativeInputStream = entry.openInputStream(0);
@ -228,16 +226,16 @@ let ThumbnailStorage = {
}
cleanup();
completed(isSuccess, imageData);
callback(isSuccess ? "" : "failure", imageData);
});
}
let onCacheEntryUnavailable = function() {
completed(false);
function onCacheEntryUnavailable() {
callback("unavailable");
}
this._openCacheEntry(url, Ci.nsICache.ACCESS_READ,
onCacheEntryAvailable, onCacheEntryUnavailable);
this._openCacheEntry(url, Ci.nsICache.ACCESS_READ, onCacheEntryAvailable,
onCacheEntryUnavailable);
}
}

Просмотреть файл

@ -283,13 +283,16 @@ let UI = {
gWindow.addEventListener("SSWindowClosing", function onWindowClosing() {
gWindow.removeEventListener("SSWindowClosing", onWindowClosing, false);
// XXX bug #635975 - don't unlink the tab if the dom window is closing.
self.isDOMWindowClosing = true;
if (self.isTabViewVisible())
GroupItems.removeHiddenGroups();
TabItems.saveAll();
TabItems.saveAllThumbnails({synchronously: true});
Storage.saveActiveGroupName(gWindow);
TabItems.saveAll(true);
self._save();
}, false);
@ -716,6 +719,11 @@ let UI = {
if (data == "enter" || data == "exit") {
hideSearch();
self._privateBrowsing.transitionMode = data;
// make sure to save all thumbnails that haven't been saved yet
// before we enter the private browsing mode
if (data == "enter")
TabItems.saveAllThumbnails({synchronously: true});
}
} else if (topic == "private-browsing-transition-complete") {
// We use .transitionMode here, as aData is empty.

Просмотреть файл

@ -82,7 +82,6 @@ _BROWSER_FILES = \
browser_tabview_bug600812.js \
browser_tabview_bug602432.js \
browser_tabview_bug604098.js \
browser_tabview_bug604699.js \
browser_tabview_bug606657.js \
browser_tabview_bug606905.js \
browser_tabview_bug607108.js \
@ -154,6 +153,7 @@ _BROWSER_FILES = \
browser_tabview_bug669694.js \
browser_tabview_bug673196.js \
browser_tabview_bug673729.js \
browser_tabview_bug677310.js \
browser_tabview_bug679853.js \
browser_tabview_bug681599.js \
browser_tabview_click_group.js \
@ -170,6 +170,7 @@ _BROWSER_FILES = \
browser_tabview_snapping.js \
browser_tabview_startup_transitions.js \
browser_tabview_storage_policy.js \
browser_tabview_thumbnail_storage.js \
browser_tabview_undo_group.js \
dummy_page.html \
head.js \

Просмотреть файл

@ -34,7 +34,9 @@ function setupTwo(win) {
// force all canvases to update, and hook in imageData save detection
tabItems.forEach(function(tabItem) {
contentWindow.TabItems.update(tabItem.tab);
// mark thumbnail as dirty
tabItem.tabCanvas.paint();
tabItem.addSubscriber("savedCachedImageData", function onSaved(item) {
item.removeSubscriber("savedCachedImageData", onSaved);
@ -81,8 +83,8 @@ function setupTwo(win) {
let count = tabItems.length;
tabItems.forEach(function(tabItem) {
tabItem.addSubscriber("loadedCachedImageData", function onLoaded() {
tabItem.removeSubscriber("loadedCachedImageData", onLoaded);
tabItem.addSubscriber("showingCachedData", function onLoaded() {
tabItem.removeSubscriber("showingCachedData", onLoaded);
ok(tabItem.isShowingCachedData(),
"Tab item is showing cached data and is just connected. " +
tabItem.tab.linkedBrowser.currentURI.spec);

Просмотреть файл

@ -1,85 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
let url = "http://www.example.com/";
let cw;
let tab = gBrowser.tabs[0];
let finishTest = function () {
is(1, gBrowser.tabs.length, "there is one tab, only");
ok(!TabView.isVisible(), "tabview is not visible");
finish();
}
waitForExplicitFinish();
let testErroneousLoading = function () {
cw.ThumbnailStorage.loadThumbnail(tab, url, function (status, data) {
ok(!status, "thumbnail entry failed to load");
is(null, data, "no thumbnail data received");
next();
});
}
let testAsynchronousSaving = function () {
let saved = false;
let data = "thumbnail-data-asynchronous";
cw.ThumbnailStorage.saveThumbnail(tab, data, function (status) {
ok(status, "thumbnail entry was saved");
ok(saved, "thumbnail was saved asynchronously");
cw.ThumbnailStorage.loadThumbnail(tab, url, function (status, imageData) {
ok(status, "thumbnail entry was loaded");
is(imageData, data, "valid thumbnail data received");
next();
});
});
saved = true;
}
let testSynchronousSaving = function () {
let saved = false;
let data = "thumbnail-data-synchronous";
cw.UI.isDOMWindowClosing = true;
registerCleanupFunction(function () cw.UI.isDOMWindowClosing = false);
cw.ThumbnailStorage.saveThumbnail(tab, data, function (status) {
ok(status, "thumbnail entry was saved");
ok(!saved, "thumbnail was saved synchronously");
cw.ThumbnailStorage.loadThumbnail(tab, url, function (status, imageData) {
ok(status, "thumbnail entry was loaded");
is(imageData, data, "valid thumbnail data received");
cw.UI.isDOMWindowClosing = false;
next();
});
});
saved = true;
}
let tests = [testErroneousLoading, testAsynchronousSaving, testSynchronousSaving];
let next = function () {
let test = tests.shift();
if (test)
test();
else
hideTabView(finishTest);
}
tab.linkedBrowser.loadURI(url);
afterAllTabsLoaded(function() {
showTabView(function () {
registerCleanupFunction(function () TabView.hide());
cw = TabView.getContentWindow();
next();
});
});
}

Просмотреть файл

@ -22,8 +22,8 @@ function test() {
tabItem.addSubscriber("savedCachedImageData", function onSaved() {
tabItem.removeSubscriber("savedCachedImageData", onSaved);
tabItem.addSubscriber("loadedCachedImageData", function onLoaded() {
tabItem.removeSubscriber("loadedCachedImageData", onLoaded);
tabItem.addSubscriber("showingCachedData", function onLoaded() {
tabItem.removeSubscriber("showingCachedData", onLoaded);
ok(tabItem.isShowingCachedData(), 'tabItem shows cached data');
testChangeUrlAfterReconnect();
@ -33,6 +33,7 @@ function test() {
});
cw.Storage.saveTab(tab, data);
tabItem.saveThumbnail();
});
});
}

Просмотреть файл

@ -0,0 +1,48 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let pb = Cc["@mozilla.org/privatebrowsing;1"].
getService(Ci.nsIPrivateBrowsingService);
function test() {
let thumbnailsSaved = false;
waitForExplicitFinish();
registerCleanupFunction(function () {
ok(thumbnailsSaved, "thumbs have been saved before entering pb mode");
pb.privateBrowsingEnabled = false;
});
afterAllTabsLoaded(function () {
showTabView(function () {
hideTabView(function () {
let numConditions = 2;
function check() {
if (--numConditions)
return;
togglePrivateBrowsing(finish);
}
let tabItem = gBrowser.tabs[0]._tabViewTabItem;
// save all thumbnails synchronously to cancel all delayed thumbnail
// saves that might be active
tabItem.saveThumbnail({synchronously: true});
// force a tabCanvas paint to flag the thumbnail as dirty
tabItem.tabCanvas.paint();
tabItem.addSubscriber("savedCachedImageData", function onSaved() {
tabItem.removeSubscriber("savedCachedImageData", onSaved);
thumbnailsSaved = true;
check();
});
togglePrivateBrowsing(check);
});
});
});
}

Просмотреть файл

@ -39,8 +39,8 @@ function test1() {
ok(!contentWindow.StoragePolicy.canStoreThumbnailForTab(newTab),
"Should not save the thumbnail for tab");
whenDeniedToCacheImageData(tabItem, test2);
tabItem.save(true);
whenDeniedToSaveImageData(tabItem, test2);
tabItem.saveThumbnail({synchronously: true});
HttpRequestObserver.cacheControlValue = null;
});
@ -59,7 +59,7 @@ function test2() {
"Should save the thumbnail for tab");
whenSavedCachedImageData(tabItem, test3);
tabItem.save(true);
tabItem.saveThumbnail({synchronously: true});
});
}
@ -77,7 +77,7 @@ function test3() {
"Should save the thumbnail for tab");
whenSavedCachedImageData(tabItem, test4);
tabItem.save(true);
tabItem.saveThumbnail({synchronously: true});
});
}
@ -95,7 +95,7 @@ function test4() {
"Should save the thumbnail for tab");
whenSavedCachedImageData(tabItem, test5);
tabItem.save(true);
tabItem.saveThumbnail({synchronously: true});
});
}
@ -109,13 +109,13 @@ function test5() {
ok(!contentWindow.StoragePolicy.canStoreThumbnailForTab(newTab),
"Should not save the thumbnail for tab");
whenDeniedToCacheImageData(tabItem, function () {
whenDeniedToSaveImageData(tabItem, function () {
hideTabView(function () {
gBrowser.removeTab(gBrowser.tabs[1]);
finish();
});
});
tabItem.save(true);
tabItem.saveThumbnail({synchronously: true});
});
newTab.linkedBrowser.loadURI("https://example.com/");
@ -147,9 +147,9 @@ function whenSavedCachedImageData(tabItem, callback) {
});
}
function whenDeniedToCacheImageData(tabItem, callback) {
tabItem.addSubscriber("deniedToCacheImageData", function onDenied() {
tabItem.removeSubscriber("deniedToCacheImageData", onDenied);
function whenDeniedToSaveImageData(tabItem, callback) {
tabItem.addSubscriber("deniedToSaveImageData", function onDenied() {
tabItem.removeSubscriber("deniedToSaveImageData", onDenied);
callback();
});
}

Просмотреть файл

@ -0,0 +1,161 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let tests = [testRawSyncSave, testRawAsyncSave, testRawLoadError,
testAsyncSave, testSyncSave, testOverrideAsyncSave,
testSaveCleanThumbnail];
function test() {
waitForExplicitFinish();
loadTabView(next);
}
function testRawSyncSave() {
let cw = TabView.getContentWindow();
let url = "http://example.com/sync-url";
let data = "thumbnail-data-sync";
let saved = false;
cw.ThumbnailStorage.saveThumbnail(url, data, function (error) {
ok(!error, "thumbnail entry was saved");
ok(!saved, "thumbnail was saved synchronously");
cw.ThumbnailStorage.loadThumbnail(url, function (error, imageData) {
ok(!error, "thumbnail entry was loaded");
is(imageData, data, "valid thumbnail data received");
next();
});
}, {synchronously: true});
saved = true;
}
function testRawAsyncSave() {
let cw = TabView.getContentWindow();
let url = "http://example.com/async-url";
let data = "thumbnail-data-async";
let saved = false;
cw.ThumbnailStorage.saveThumbnail(url, data, function (error) {
ok(!error, "thumbnail entry was saved");
ok(saved, "thumbnail was saved asynchronously");
cw.ThumbnailStorage.loadThumbnail(url, function (error, imageData) {
ok(!error, "thumbnail entry was loaded");
is(imageData, data, "valid thumbnail data received");
next();
});
});
saved = true;
}
function testRawLoadError() {
let cw = TabView.getContentWindow();
cw.ThumbnailStorage.loadThumbnail("non-existant-url", function (error, data) {
ok(error, "thumbnail entry failed to load");
is(null, data, "no thumbnail data received");
next();
});
}
function testSyncSave() {
let tabItem = gBrowser.tabs[0]._tabViewTabItem;
// set the thumbnail to dirty
tabItem.tabCanvas.paint();
let saved = false;
whenThumbnailSaved(tabItem, function () {
ok(!saved, "thumbnail was saved synchronously");
next();
});
tabItem.saveThumbnail({synchronously: true});
saved = true;
}
function testAsyncSave() {
let tabItem = gBrowser.tabs[0]._tabViewTabItem;
// set the thumbnail to dirty
tabItem.tabCanvas.paint();
let saved = false;
whenThumbnailSaved(tabItem, function () {
ok(saved, "thumbnail was saved asynchronously");
next();
});
tabItem.saveThumbnail();
saved = true;
}
function testOverrideAsyncSave() {
let tabItem = gBrowser.tabs[0]._tabViewTabItem;
// set the thumbnail to dirty
tabItem.tabCanvas.paint();
// initiate async save
tabItem.saveThumbnail();
let saveCount = 0;
whenThumbnailSaved(tabItem, function () {
saveCount = 1;
});
tabItem.saveThumbnail({synchronously: true});
is(saveCount, 1, "thumbnail got saved once");
next();
}
function testSaveCleanThumbnail() {
let tabItem = gBrowser.tabs[0]._tabViewTabItem;
// set the thumbnail to dirty
tabItem.tabCanvas.paint();
let saveCount = 0;
whenThumbnailSaved(tabItem, function () saveCount++);
tabItem.saveThumbnail({synchronously: true});
tabItem.saveThumbnail({synchronously: true});
is(saveCount, 1, "thumbnail got saved once, only");
next();
}
// ----------
function whenThumbnailSaved(tabItem, callback) {
tabItem.addSubscriber("savedCachedImageData", function onSaved() {
tabItem.removeSubscriber("savedCachedImageData", onSaved);
callback();
});
}
// ----------
function loadTabView(callback) {
afterAllTabsLoaded(function () {
showTabView(function () {
hideTabView(callback);
});
});
}
// ----------
function next() {
let test = tests.shift();
if (test) {
info("* running " + test.name + "...");
test();
} else {
finish();
}
}