From a13ddf90c3b8597898f548c5f502b5ab758612e0 Mon Sep 17 00:00:00 2001 From: Tim Taubert Date: Thu, 1 Sep 2011 15:17:27 +0200 Subject: [PATCH] Bug 677310 - Thumbnails are lost when switching to private browsing mode; r=dietrich --- browser/base/content/tabview/storage.js | 26 +-- browser/base/content/tabview/tabitems.js | 186 ++++++++++++------ .../base/content/tabview/thumbnailStorage.js | 128 ++++++------ browser/base/content/tabview/ui.js | 10 +- browser/base/content/test/tabview/Makefile.in | 3 +- .../test/tabview/browser_tabview_bug597248.js | 8 +- .../test/tabview/browser_tabview_bug604699.js | 85 -------- .../test/tabview/browser_tabview_bug627288.js | 5 +- .../test/tabview/browser_tabview_bug677310.js | 48 +++++ .../tabview/browser_tabview_storage_policy.js | 20 +- .../browser_tabview_thumbnail_storage.js | 161 +++++++++++++++ 11 files changed, 433 insertions(+), 247 deletions(-) delete mode 100644 browser/base/content/test/tabview/browser_tabview_bug604699.js create mode 100644 browser/base/content/test/tabview/browser_tabview_bug677310.js create mode 100644 browser/base/content/test/tabview/browser_tabview_thumbnail_storage.js diff --git a/browser/base/content/tabview/storage.js b/browser/base/content/tabview/storage.js index c9bf74ea9e29..6c5247a0b11c 100644 --- a/browser/base/content/tabview/storage.js +++ b/browser/base/content/tabview/storage.js @@ -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 (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; }, diff --git a/browser/base/content/tabview/tabitems.js b/browser/base/content/tabview/tabitems.js index 23a5107a99a5..262b9972b22b 100644 --- a/browser/base/content/tabview/tabitems.js +++ b/browser/base/content/tabview/tabitems.js @@ -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) { - try{ + 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 "); + + 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 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 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"); } -}; +}); diff --git a/browser/base/content/tabview/thumbnailStorage.js b/browser/base/content/tabview/thumbnailStorage.js index c6d4a7ca7090..0b9e63c9beea 100644 --- a/browser/base/content/tabview/thumbnailStorage.js +++ b/browser/base/content/tabview/thumbnailStorage.js @@ -84,20 +84,38 @@ let ThumbnailStorage = { // Opens a cache entry for the given and requests access . // Calls (entry) when the entry was successfully opened with // requested access rights. Otherwise calls (). - _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 "); + Utils.assert(access, "invalid or missing argument "); + Utils.assert(successCallback, "invalid or missing argument "); + Utils.assert(errorCallback, "invalid or missing argument "); + + 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 to the cache using the given as key. - // Calls (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 "); + Utils.assert(imageData, "invalid or missing argument "); + Utils.assert(callback, "invalid or missing argument "); + 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 as key. - // Calls (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 "); + Utils.assert(callback, "invalid or missing argument "); 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); } } diff --git a/browser/base/content/tabview/ui.js b/browser/base/content/tabview/ui.js index f2ea0332b654..723e5edd0d47 100644 --- a/browser/base/content/tabview/ui.js +++ b/browser/base/content/tabview/ui.js @@ -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. diff --git a/browser/base/content/test/tabview/Makefile.in b/browser/base/content/test/tabview/Makefile.in index cc62c3ad4cb0..10c1754beeb6 100644 --- a/browser/base/content/test/tabview/Makefile.in +++ b/browser/base/content/test/tabview/Makefile.in @@ -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 \ diff --git a/browser/base/content/test/tabview/browser_tabview_bug597248.js b/browser/base/content/test/tabview/browser_tabview_bug597248.js index e106441d82c6..eeb8dfe0f55e 100644 --- a/browser/base/content/test/tabview/browser_tabview_bug597248.js +++ b/browser/base/content/test/tabview/browser_tabview_bug597248.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); diff --git a/browser/base/content/test/tabview/browser_tabview_bug604699.js b/browser/base/content/test/tabview/browser_tabview_bug604699.js deleted file mode 100644 index aae6ddf866e3..000000000000 --- a/browser/base/content/test/tabview/browser_tabview_bug604699.js +++ /dev/null @@ -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(); - }); - }); -} diff --git a/browser/base/content/test/tabview/browser_tabview_bug627288.js b/browser/base/content/test/tabview/browser_tabview_bug627288.js index aa1ec5365525..39dac283a9c7 100644 --- a/browser/base/content/test/tabview/browser_tabview_bug627288.js +++ b/browser/base/content/test/tabview/browser_tabview_bug627288.js @@ -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(); }); }); } diff --git a/browser/base/content/test/tabview/browser_tabview_bug677310.js b/browser/base/content/test/tabview/browser_tabview_bug677310.js new file mode 100644 index 000000000000..48bd079bcff7 --- /dev/null +++ b/browser/base/content/test/tabview/browser_tabview_bug677310.js @@ -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); + }); + }); + }); +} diff --git a/browser/base/content/test/tabview/browser_tabview_storage_policy.js b/browser/base/content/test/tabview/browser_tabview_storage_policy.js index d36966a591f1..f19305b1645e 100644 --- a/browser/base/content/test/tabview/browser_tabview_storage_policy.js +++ b/browser/base/content/test/tabview/browser_tabview_storage_policy.js @@ -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(); }); } diff --git a/browser/base/content/test/tabview/browser_tabview_thumbnail_storage.js b/browser/base/content/test/tabview/browser_tabview_thumbnail_storage.js new file mode 100644 index 000000000000..23409f1a82b4 --- /dev/null +++ b/browser/base/content/test/tabview/browser_tabview_thumbnail_storage.js @@ -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(); + } +}