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

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

@ -69,6 +69,7 @@ function TabItem(tab, options) {
let $div = iQ(div); let $div = iQ(div);
this._cachedImageData = null; this._cachedImageData = null;
this._thumbnailNeedsSaving = false;
this.canvasSizeForced = false; this.canvasSizeForced = false;
this.$thumb = iQ('.thumb', $div); this.$thumb = iQ('.thumb', $div);
this.$fav = iQ('.favicon', $div); this.$fav = iQ('.favicon', $div);
@ -80,19 +81,23 @@ function TabItem(tab, options) {
this.tabCanvas = new TabCanvas(this.tab, this.$canvas[0]); 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.defaultSize = new Point(TabItems.tabWidth, TabItems.tabHeight);
this._hidden = false; this._hidden = false;
this.isATabItem = true; this.isATabItem = true;
this.keepProportional = true; this.keepProportional = true;
this._hasBeenDrawn = false; this._hasBeenDrawn = false;
this._reconnected = false; this._reconnected = false;
this.isDragging = false;
this.isStacked = false; this.isStacked = false;
this.url = ""; this.url = "";
var self = this;
this.isDragging = false;
// Read off the total vertical and horizontal padding on the tab container // 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. // and cache this value, as it must be the same for every TabItem.
if (Utils.isEmptyObject(TabItems.tabItemPadding)) { if (Utils.isEmptyObject(TabItems.tabItemPadding)) {
@ -194,11 +199,14 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// //
// Parameters: // Parameters:
// tabData - the tab data // tabData - the tab data
showCachedData: function TabItem_showCachedData(tabData) { // imageData - the image data
this._cachedImageData = tabData.imageData; showCachedData: function TabItem_showCachedData(tabData, imageData) {
this._cachedImageData = imageData;
this.$cachedThumb.attr("src", this._cachedImageData).show(); 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.$tabTitle.text(tabData.title ? tabData.title : "");
this._sendToSubscribers("showingCachedData");
}, },
// ---------- // ----------
@ -214,39 +222,23 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// ---------- // ----------
// Function: getStorageData // Function: getStorageData
// Get data to be used for persistent storage of this object. // Get data to be used for persistent storage of this object.
// getStorageData: function TabItem_getStorageData() {
// 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();
}
return { return {
url: this.tab.linkedBrowser.currentURI.spec, url: this.tab.linkedBrowser.currentURI.spec,
groupID: (this.parent ? this.parent.id : 0), groupID: (this.parent ? this.parent.id : 0),
imageData: imageData, title: this.tab.label
title: getImageData && this.tab.label || null
}; };
}, },
// ---------- // ----------
// Function: save // Function: save
// Store persistent for this object. // Store persistent for this object.
// save: function TabItem_save() {
// Parameters:
// saveImageData - true to include thumbnail pixels (and page title as well); default false
save: function TabItem_save(saveImageData) {
try { try {
if (!this.tab || this.tab.parentNode == null || !this._reconnected) // too soon/late to save if (!this.tab || this.tab.parentNode == null || !this._reconnected) // too soon/late to save
return; return;
var data = this.getStorageData(saveImageData); let data = this.getStorageData();
if (TabItems.storageSanity(data)) if (TabItems.storageSanity(data))
Storage.saveTab(this.tab, data); Storage.saveTab(this.tab, data);
} catch(e) { } 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 // Function: _reconnect
// Load the reciever's persistent data from storage. If there is none, // 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._reconnected, "shouldn't already be reconnected");
Utils.assertThrow(this.tab, "should have a xul:tab"); Utils.assertThrow(this.tab, "should have a xul:tab");
let tabData = null;
let self = this; let self = this;
let imageDataCb = function(imageData) { let tabData = Storage.getTabData(this.tab);
// we could have been unlinked while waiting for the thumbnail to load
if (!self.tab)
return;
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)) { if (tabData && TabItems.storageSanity(tabData)) {
this.loadThumbnail(tabData);
if (self.parent) if (self.parent)
self.parent.remove(self, {immediately: true}); self.parent.remove(self, {immediately: true});
@ -936,6 +996,7 @@ let TabItems = {
tabItem._lastTabUpdateTime = this._lastUpdateTime; tabItem._lastTabUpdateTime = this._lastUpdateTime;
tabItem.tabCanvas.paint(); tabItem.tabCanvas.paint();
tabItem.saveThumbnail();
// ___ cache // ___ cache
if (tabItem.isShowingCachedData()) if (tabItem.isShowingCachedData())
@ -1146,13 +1207,22 @@ let TabItems = {
// ---------- // ----------
// Function: saveAll // Function: saveAll
// Saves all open <TabItem>s. // Saves all open <TabItem>s.
// saveAll: function TabItems_saveAll() {
// Parameters: let tabItems = this.getItems();
// saveImageData - true to include thumbnail pixels (and page title as well); default false
saveAll: function TabItems_saveAll(saveImageData) { tabItems.forEach(function TabItems_saveAll_forEach(tabItem) {
var items = this.getItems(); tabItem.save();
items.forEach(function(item) { });
item.save(saveImageData); },
// ----------
// 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; this.canvas = canvas;
}; };
TabCanvas.prototype = { TabCanvas.prototype = Utils.extend(new Subscribable(), {
// ---------- // ----------
// Function: toString // Function: toString
// Prints [TabCanvas (tab)] for debug use // Prints [TabCanvas (tab)] for debug use
@ -1386,6 +1456,8 @@ TabCanvas.prototype = {
// Draw directly to the destination canvas // Draw directly to the destination canvas
this._drawWindow(ctx, w, h, bgColor); this._drawWindow(ctx, w, h, bgColor);
} }
this._sendToSubscribers("painted");
}, },
// ---------- // ----------
@ -1454,4 +1526,4 @@ TabCanvas.prototype = {
toImageData: function TabCanvas_toImageData() { toImageData: function TabCanvas_toImageData() {
return this.canvas.toDataURL("image/png"); return this.canvas.toDataURL("image/png");
} }
}; });

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

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

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

@ -283,13 +283,16 @@ let UI = {
gWindow.addEventListener("SSWindowClosing", function onWindowClosing() { gWindow.addEventListener("SSWindowClosing", function onWindowClosing() {
gWindow.removeEventListener("SSWindowClosing", onWindowClosing, false); gWindow.removeEventListener("SSWindowClosing", onWindowClosing, false);
// XXX bug #635975 - don't unlink the tab if the dom window is closing.
self.isDOMWindowClosing = true; self.isDOMWindowClosing = true;
if (self.isTabViewVisible()) if (self.isTabViewVisible())
GroupItems.removeHiddenGroups(); GroupItems.removeHiddenGroups();
TabItems.saveAll();
TabItems.saveAllThumbnails({synchronously: true});
Storage.saveActiveGroupName(gWindow); Storage.saveActiveGroupName(gWindow);
TabItems.saveAll(true);
self._save(); self._save();
}, false); }, false);
@ -716,6 +719,11 @@ let UI = {
if (data == "enter" || data == "exit") { if (data == "enter" || data == "exit") {
hideSearch(); hideSearch();
self._privateBrowsing.transitionMode = data; 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") { } else if (topic == "private-browsing-transition-complete") {
// We use .transitionMode here, as aData is empty. // We use .transitionMode here, as aData is empty.

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

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

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

@ -34,7 +34,9 @@ function setupTwo(win) {
// force all canvases to update, and hook in imageData save detection // force all canvases to update, and hook in imageData save detection
tabItems.forEach(function(tabItem) { tabItems.forEach(function(tabItem) {
contentWindow.TabItems.update(tabItem.tab); // mark thumbnail as dirty
tabItem.tabCanvas.paint();
tabItem.addSubscriber("savedCachedImageData", function onSaved(item) { tabItem.addSubscriber("savedCachedImageData", function onSaved(item) {
item.removeSubscriber("savedCachedImageData", onSaved); item.removeSubscriber("savedCachedImageData", onSaved);
@ -81,8 +83,8 @@ function setupTwo(win) {
let count = tabItems.length; let count = tabItems.length;
tabItems.forEach(function(tabItem) { tabItems.forEach(function(tabItem) {
tabItem.addSubscriber("loadedCachedImageData", function onLoaded() { tabItem.addSubscriber("showingCachedData", function onLoaded() {
tabItem.removeSubscriber("loadedCachedImageData", onLoaded); tabItem.removeSubscriber("showingCachedData", onLoaded);
ok(tabItem.isShowingCachedData(), ok(tabItem.isShowingCachedData(),
"Tab item is showing cached data and is just connected. " + "Tab item is showing cached data and is just connected. " +
tabItem.tab.linkedBrowser.currentURI.spec); 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.addSubscriber("savedCachedImageData", function onSaved() {
tabItem.removeSubscriber("savedCachedImageData", onSaved); tabItem.removeSubscriber("savedCachedImageData", onSaved);
tabItem.addSubscriber("loadedCachedImageData", function onLoaded() { tabItem.addSubscriber("showingCachedData", function onLoaded() {
tabItem.removeSubscriber("loadedCachedImageData", onLoaded); tabItem.removeSubscriber("showingCachedData", onLoaded);
ok(tabItem.isShowingCachedData(), 'tabItem shows cached data'); ok(tabItem.isShowingCachedData(), 'tabItem shows cached data');
testChangeUrlAfterReconnect(); testChangeUrlAfterReconnect();
@ -33,6 +33,7 @@ function test() {
}); });
cw.Storage.saveTab(tab, data); 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), ok(!contentWindow.StoragePolicy.canStoreThumbnailForTab(newTab),
"Should not save the thumbnail for tab"); "Should not save the thumbnail for tab");
whenDeniedToCacheImageData(tabItem, test2); whenDeniedToSaveImageData(tabItem, test2);
tabItem.save(true); tabItem.saveThumbnail({synchronously: true});
HttpRequestObserver.cacheControlValue = null; HttpRequestObserver.cacheControlValue = null;
}); });
@ -59,7 +59,7 @@ function test2() {
"Should save the thumbnail for tab"); "Should save the thumbnail for tab");
whenSavedCachedImageData(tabItem, test3); whenSavedCachedImageData(tabItem, test3);
tabItem.save(true); tabItem.saveThumbnail({synchronously: true});
}); });
} }
@ -77,7 +77,7 @@ function test3() {
"Should save the thumbnail for tab"); "Should save the thumbnail for tab");
whenSavedCachedImageData(tabItem, test4); whenSavedCachedImageData(tabItem, test4);
tabItem.save(true); tabItem.saveThumbnail({synchronously: true});
}); });
} }
@ -95,7 +95,7 @@ function test4() {
"Should save the thumbnail for tab"); "Should save the thumbnail for tab");
whenSavedCachedImageData(tabItem, test5); whenSavedCachedImageData(tabItem, test5);
tabItem.save(true); tabItem.saveThumbnail({synchronously: true});
}); });
} }
@ -109,13 +109,13 @@ function test5() {
ok(!contentWindow.StoragePolicy.canStoreThumbnailForTab(newTab), ok(!contentWindow.StoragePolicy.canStoreThumbnailForTab(newTab),
"Should not save the thumbnail for tab"); "Should not save the thumbnail for tab");
whenDeniedToCacheImageData(tabItem, function () { whenDeniedToSaveImageData(tabItem, function () {
hideTabView(function () { hideTabView(function () {
gBrowser.removeTab(gBrowser.tabs[1]); gBrowser.removeTab(gBrowser.tabs[1]);
finish(); finish();
}); });
}); });
tabItem.save(true); tabItem.saveThumbnail({synchronously: true});
}); });
newTab.linkedBrowser.loadURI("https://example.com/"); newTab.linkedBrowser.loadURI("https://example.com/");
@ -147,9 +147,9 @@ function whenSavedCachedImageData(tabItem, callback) {
}); });
} }
function whenDeniedToCacheImageData(tabItem, callback) { function whenDeniedToSaveImageData(tabItem, callback) {
tabItem.addSubscriber("deniedToCacheImageData", function onDenied() { tabItem.addSubscriber("deniedToSaveImageData", function onDenied() {
tabItem.removeSubscriber("deniedToCacheImageData", onDenied); tabItem.removeSubscriber("deniedToSaveImageData", onDenied);
callback(); 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();
}
}