зеркало из https://github.com/mozilla/pjs.git
Bug 604699 - Store thumbnails in the browser's image cache rather than in sessionstore [r=ian,sdwilsh, a=blocking2.0:final+]
(Based on patch from Sean Dunn <seanedunn@yahoo.com>.)
This commit is contained in:
Родитель
793df06f72
Коммит
e4dfcc281c
|
@ -47,6 +47,8 @@ let Storage = {
|
|||
GROUPS_DATA_IDENTIFIER: "tabview-groups",
|
||||
TAB_DATA_IDENTIFIER: "tabview-tab",
|
||||
UI_DATA_IDENTIFIER: "tabview-ui",
|
||||
CACHE_CLIENT_IDENTIFIER: "tabview-cache",
|
||||
CACHE_PREFIX: "moz-panorama:",
|
||||
|
||||
// ----------
|
||||
// Function: init
|
||||
|
@ -55,12 +57,28 @@ let Storage = {
|
|||
this._sessionStore =
|
||||
Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
|
||||
// Create stream-based cache session for tabview
|
||||
let cacheService =
|
||||
Cc["@mozilla.org/network/cache-service;1"].
|
||||
getService(Ci.nsICacheService);
|
||||
this._cacheSession = cacheService.createSession(
|
||||
this.CACHE_CLIENT_IDENTIFIER, Ci.nsICache.STORE_ON_DISK, true);
|
||||
this.StringInputStream = Components.Constructor(
|
||||
"@mozilla.org/io/string-input-stream;1", "nsIStringInputStream",
|
||||
"setData");
|
||||
this.StorageStream = Components.Constructor(
|
||||
"@mozilla.org/storagestream;1", "nsIStorageStream",
|
||||
"init");
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: uninit
|
||||
uninit: function Storage_uninit () {
|
||||
this._sessionStore = null;
|
||||
this._cacheSession = null;
|
||||
this.StringInputStream = null;
|
||||
this.StorageStream = null;
|
||||
},
|
||||
|
||||
// ----------
|
||||
|
@ -89,37 +107,196 @@ let Storage = {
|
|||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _openCacheEntry
|
||||
// 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 Storage__openCacheEntry(url, access, successCallback, errorCallback) {
|
||||
let onCacheEntryAvailable = function (entry, accessGranted, status) {
|
||||
if (entry && access == accessGranted && Components.isSuccessCode(status)) {
|
||||
successCallback(entry);
|
||||
} else {
|
||||
entry && entry.close();
|
||||
errorCallback();
|
||||
}
|
||||
}
|
||||
|
||||
let key = this.CACHE_PREFIX + url;
|
||||
|
||||
// switch to synchronous mode if parent window is about to close
|
||||
if (UI.isDOMWindowClosing) {
|
||||
let entry = this._cacheSession.openCacheEntry(key, access, true);
|
||||
let status = Components.results.NS_OK;
|
||||
onCacheEntryAvailable(entry, entry.accessGranted, status);
|
||||
} else {
|
||||
let listener = new CacheListener(onCacheEntryAvailable);
|
||||
this._cacheSession.asyncOpenCacheEntry(key, access, listener);
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: saveThumbnail
|
||||
// Saves the <imageData> to the cache using the given <url> as key.
|
||||
// Calls <callback>(status) when finished (passing true or false indicating
|
||||
// whether the operation succeeded).
|
||||
saveThumbnail: function Storage_saveThumbnail(url, imageData, callback) {
|
||||
Utils.assert(url, "url");
|
||||
Utils.assert(imageData, "imageData");
|
||||
Utils.assert(typeof callback == "function", "callback arg must be a function");
|
||||
|
||||
let self = this;
|
||||
let StringInputStream = this.StringInputStream;
|
||||
|
||||
let onCacheEntryAvailable = function (entry) {
|
||||
let outputStream = entry.openOutputStream(0);
|
||||
|
||||
let cleanup = function () {
|
||||
outputStream.close();
|
||||
entry.close();
|
||||
}
|
||||
|
||||
// switch to synchronous mode if parent window is about to close
|
||||
if (UI.isDOMWindowClosing) {
|
||||
outputStream.write(imageData, imageData.length);
|
||||
cleanup();
|
||||
callback(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// asynchronous mode
|
||||
let inputStream = new StringInputStream(imageData, imageData.length);
|
||||
gNetUtil.asyncCopy(inputStream, outputStream, function (result) {
|
||||
cleanup();
|
||||
inputStream.close();
|
||||
callback(Components.isSuccessCode(result));
|
||||
});
|
||||
}
|
||||
|
||||
let onCacheEntryUnavailable = function () {
|
||||
callback(false);
|
||||
}
|
||||
|
||||
this._openCacheEntry(url, Ci.nsICache.ACCESS_WRITE,
|
||||
onCacheEntryAvailable, onCacheEntryUnavailable);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// 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 Storage_loadThumbnail(url, callback) {
|
||||
Utils.assert(url, "url");
|
||||
Utils.assert(typeof callback == "function", "callback arg must be a function");
|
||||
|
||||
let self = this;
|
||||
|
||||
let onCacheEntryAvailable = function (entry) {
|
||||
let imageChunks = [];
|
||||
let nativeInputStream = entry.openInputStream(0);
|
||||
|
||||
const CHUNK_SIZE = 0x10000; // 65k
|
||||
const PR_UINT32_MAX = 0xFFFFFFFF;
|
||||
let storageStream = new self.StorageStream(CHUNK_SIZE, PR_UINT32_MAX, null);
|
||||
let storageOutStream = storageStream.getOutputStream(0);
|
||||
|
||||
let cleanup = function () {
|
||||
nativeInputStream.close();
|
||||
storageStream.close();
|
||||
storageOutStream.close();
|
||||
entry.close();
|
||||
}
|
||||
|
||||
gNetUtil.asyncCopy(nativeInputStream, storageOutStream, function (result) {
|
||||
// cancel if parent window has already been closed
|
||||
if (typeof UI == "undefined") {
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
let imageData = null;
|
||||
let isSuccess = Components.isSuccessCode(result);
|
||||
|
||||
if (isSuccess) {
|
||||
let storageInStream = storageStream.newInputStream(0);
|
||||
imageData = gNetUtil.readInputStreamToString(storageInStream,
|
||||
storageInStream.available());
|
||||
storageInStream.close();
|
||||
}
|
||||
|
||||
cleanup();
|
||||
callback(isSuccess, imageData);
|
||||
});
|
||||
}
|
||||
|
||||
let onCacheEntryUnavailable = function () {
|
||||
callback(false);
|
||||
}
|
||||
|
||||
this._openCacheEntry(url, Ci.nsICache.ACCESS_READ,
|
||||
onCacheEntryAvailable, onCacheEntryUnavailable);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: saveTab
|
||||
// Saves the data for a single tab.
|
||||
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) {
|
||||
this.saveThumbnail(data.url, imageData, function (status) {
|
||||
if (status) {
|
||||
// Notify subscribers
|
||||
tab._tabViewTabItem._sendToSubscribers("savedCachedImageData");
|
||||
} else {
|
||||
Utils.log("Error while saving thumbnail: " + e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this._sessionStore.setTabValue(tab, this.TAB_DATA_IDENTIFIER,
|
||||
JSON.stringify(data));
|
||||
|
||||
// Notify subscribers
|
||||
if (data && data.imageData && tab._tabViewTabItem)
|
||||
tab._tabViewTabItem._sendToSubscribers("savedImageData");
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: getTabData
|
||||
// Returns the data object associated with a single tab.
|
||||
getTabData: function Storage_getTabData(tab) {
|
||||
// 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) {
|
||||
Utils.assert(tab, "tab");
|
||||
Utils.assert(typeof callback == "function", "callback arg must be a function");
|
||||
|
||||
let existingData = null;
|
||||
|
||||
var existingData = null;
|
||||
try {
|
||||
var tabData = this._sessionStore.getTabValue(tab, this.TAB_DATA_IDENTIFIER);
|
||||
let tabData = this._sessionStore.getTabValue(tab, this.TAB_DATA_IDENTIFIER);
|
||||
if (tabData != "") {
|
||||
existingData = JSON.parse(tabData);
|
||||
}
|
||||
} catch (e) {
|
||||
// getWindowValue will fail if the property doesn't exist
|
||||
// getTabValue will fail if the property doesn't exist.
|
||||
Utils.log(e);
|
||||
}
|
||||
|
||||
if (existingData) {
|
||||
this.loadThumbnail(existingData.url, function (status, imageData) {
|
||||
if (status) {
|
||||
callback(imageData);
|
||||
|
||||
// Notify subscribers
|
||||
tab._tabViewTabItem._sendToSubscribers("loadedCachedImageData");
|
||||
} else {
|
||||
Utils.log("Error while loading thumbnail: " + e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return existingData;
|
||||
},
|
||||
|
||||
|
@ -224,3 +401,20 @@ let Storage = {
|
|||
return existingData;
|
||||
}
|
||||
};
|
||||
|
||||
// ##########
|
||||
// Class: CacheListener
|
||||
// Generic CacheListener for feeding to asynchronous cache calls.
|
||||
// Calls <callback>(entry, access, status) when the requested cache entry
|
||||
// is available.
|
||||
function CacheListener(callback) {
|
||||
Utils.assert(typeof callback == "function", "callback arg must be a function");
|
||||
this.callback = callback;
|
||||
};
|
||||
|
||||
CacheListener.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICacheListener]),
|
||||
onCacheEntryAvailable: function (entry, access, status) {
|
||||
this.callback(entry, access, status);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -328,46 +328,55 @@ 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 = Storage.getTabData(this.tab);
|
||||
if (tabData && TabItems.storageSanity(tabData)) {
|
||||
if (this.parent)
|
||||
this.parent.remove(this, {immediately: true});
|
||||
let tabData = null;
|
||||
let self = this;
|
||||
let imageDataCb = function(imageData) {
|
||||
Utils.assertThrow(tabData, "tabData");
|
||||
|
||||
tabData.imageData = imageData;
|
||||
|
||||
this.setBounds(tabData.bounds, true);
|
||||
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 (self.parent)
|
||||
self.parent.remove(self, {immediately: true});
|
||||
|
||||
self.setBounds(tabData.bounds, true);
|
||||
|
||||
if (Utils.isPoint(tabData.userSize))
|
||||
this.userSize = new Point(tabData.userSize);
|
||||
self.userSize = new Point(tabData.userSize);
|
||||
|
||||
if (tabData.groupID) {
|
||||
var groupItem = GroupItems.groupItem(tabData.groupID);
|
||||
if (groupItem) {
|
||||
groupItem.add(this, {immediately: true});
|
||||
groupItem.add(self, {immediately: true});
|
||||
|
||||
// if it matches the selected tab or no active tab and the browser
|
||||
// if it matches the selected tab or no active tab and the browser
|
||||
// tab is hidden, the active group item would be set.
|
||||
if (this.tab == gBrowser.selectedTab ||
|
||||
(!GroupItems.getActiveGroupItem() && !this.tab.hidden))
|
||||
GroupItems.setActiveGroupItem(this.parent);
|
||||
if (self.tab == gBrowser.selectedTab ||
|
||||
(!GroupItems.getActiveGroupItem() && !self.tab.hidden))
|
||||
GroupItems.setActiveGroupItem(self.parent);
|
||||
}
|
||||
}
|
||||
|
||||
let currentUrl = this.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'))
|
||||
this.showCachedData(tabData);
|
||||
} else {
|
||||
// create tab by double click is handled in UI_init().
|
||||
if (!TabItems.creatingNewOrphanTab)
|
||||
GroupItems.newTab(this, {immediately: true});
|
||||
GroupItems.newTab(self, {immediately: true});
|
||||
}
|
||||
|
||||
this._reconnected = true;
|
||||
this.save();
|
||||
this._sendToSubscribers("reconnected");
|
||||
self._reconnected = true;
|
||||
self.save();
|
||||
self._sendToSubscribers("reconnected");
|
||||
},
|
||||
|
||||
// ----------
|
||||
|
|
|
@ -23,6 +23,12 @@ XPCOMUtils.defineLazyGetter(this, "gPrivateBrowsing", function() {
|
|||
getService(Ci.nsIPrivateBrowsingService);
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gNetUtil", function() {
|
||||
var obj = {};
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm", obj);
|
||||
return obj.NetUtil;
|
||||
});
|
||||
|
||||
var gWindow = window.parent;
|
||||
var gBrowser = gWindow.gBrowser;
|
||||
var gTabView = gWindow.TabView;
|
||||
|
|
|
@ -127,6 +127,11 @@ let UI = {
|
|||
// Used to keep track of how many calls to storageBusy vs storageReady.
|
||||
_storageBusyCount: 0,
|
||||
|
||||
// Variable: isDOMWindowClosing
|
||||
// Tells wether we already received the "domwindowclosed" event and the parent
|
||||
// windows is about to close.
|
||||
isDOMWindowClosing: false,
|
||||
|
||||
// ----------
|
||||
// Function: init
|
||||
// Must be called after the object is created.
|
||||
|
@ -241,6 +246,7 @@ let UI = {
|
|||
// ___ setup observer to save canvas images
|
||||
function domWinClosedObserver(subject, topic, data) {
|
||||
if (topic == "domwindowclosed" && subject == gWindow) {
|
||||
self.isDOMWindowClosing = true;
|
||||
if (self.isTabViewVisible())
|
||||
GroupItems.removeHiddenGroups();
|
||||
TabItems.saveAll(true);
|
||||
|
|
|
@ -78,6 +78,7 @@ _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_bug608037.js \
|
||||
|
|
|
@ -1,42 +1,6 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is tabview bug 597248 test.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Raymond Lee <raymond@appcoast.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let newWin;
|
||||
let restoredWin;
|
||||
let newTabOne;
|
||||
let newTabTwo;
|
||||
let restoredNewTabOneLoaded = false;
|
||||
|
@ -45,45 +9,31 @@ let frameInitialized = false;
|
|||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// open a new window
|
||||
newWin = openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no");
|
||||
newWin.addEventListener("load", function(event) {
|
||||
newWin.removeEventListener("load", arguments.callee, false);
|
||||
setupOne();
|
||||
}, false);
|
||||
newWindowWithTabView(setupOne);
|
||||
}
|
||||
|
||||
function setupOne() {
|
||||
let loadedCount = 0;
|
||||
let allLoaded = function() {
|
||||
if (++loadedCount == 2) {
|
||||
newWin.addEventListener("tabviewshown", setupTwo, false);
|
||||
newWin.TabView.toggle();
|
||||
}
|
||||
}
|
||||
|
||||
newTabOne = newWin.gBrowser.tabs[0];
|
||||
newTabTwo = newWin.gBrowser.addTab();
|
||||
load(newTabOne, "http://mochi.test:8888/browser/browser/base/content/test/tabview/search1.html", allLoaded);
|
||||
load(newTabTwo, "http://mochi.test:8888/browser/browser/base/content/test/tabview/dummy_page.html", allLoaded);
|
||||
function setupOne(win) {
|
||||
win.TabView.firstUseExperienced = true;
|
||||
|
||||
win.gBrowser.addTab("http://mochi.test:8888/browser/browser/base/content/test/tabview/search1.html");
|
||||
win.gBrowser.addTab("http://mochi.test:8888/browser/browser/base/content/test/tabview/dummy_page.html");
|
||||
|
||||
afterAllTabsLoaded(function () setupTwo(win), win);
|
||||
}
|
||||
|
||||
function setupTwo() {
|
||||
newWin.removeEventListener("tabviewshown", setupTwo, false);
|
||||
|
||||
let contentWindow = newWin.document.getElementById("tab-view").contentWindow;
|
||||
function setupTwo(win) {
|
||||
let contentWindow = win.TabView.getContentWindow();
|
||||
|
||||
let tabItems = contentWindow.TabItems.getItems();
|
||||
is(tabItems.length, 2, "There should be 2 tab items before closing");
|
||||
is(tabItems.length, 3, "There should be 3 tab items before closing");
|
||||
|
||||
let numTabsToSave = tabItems.length;
|
||||
|
||||
// force all canvases to update, and hook in imageData save detection
|
||||
tabItems.forEach(function(tabItem) {
|
||||
contentWindow.TabItems._update(tabItem.tab);
|
||||
tabItem.addSubscriber(tabItem, "savedImageData", function(item) {
|
||||
item.removeSubscriber(item, "savedImageData");
|
||||
tabItem.addSubscriber(tabItem, "savedCachedImageData", function(item) {
|
||||
item.removeSubscriber(item, "savedCachedImageData");
|
||||
--numTabsToSave;
|
||||
});
|
||||
});
|
||||
|
@ -93,13 +43,12 @@ function setupTwo() {
|
|||
Services.obs.removeObserver(
|
||||
xulWindowDestory, "xul-window-destroyed", false);
|
||||
|
||||
newWin = null;
|
||||
// "xul-window-destroyed" is just fired just before a XUL window is
|
||||
// destroyed so restore window and test it after a delay
|
||||
executeSoon(function() {
|
||||
restoredWin = undoCloseWindow();
|
||||
restoredWin.addEventListener("load", function(event) {
|
||||
restoredWin.removeEventListener("load", arguments.callee, false);
|
||||
let restoredWin = undoCloseWindow();
|
||||
restoredWin.addEventListener("load", function onLoad(event) {
|
||||
restoredWin.removeEventListener("load", onLoad, false);
|
||||
|
||||
// ensure that closed tabs have been saved
|
||||
is(numTabsToSave, 0, "All tabs were saved when window was closed.");
|
||||
|
@ -122,7 +71,7 @@ function setupTwo() {
|
|||
restoredWin.addEventListener(
|
||||
"tabviewframeinitialized", onTabViewFrameInitialized, false);
|
||||
|
||||
is(restoredWin.gBrowser.tabs.length, 2, "The total number of tabs is 2");
|
||||
is(restoredWin.gBrowser.tabs.length, 3, "The total number of tabs is 3");
|
||||
|
||||
/*
|
||||
// bug 615954 happens too often so we disable this until we have a fix
|
||||
|
@ -140,10 +89,10 @@ function setupTwo() {
|
|||
Services.obs.addObserver(
|
||||
xulWindowDestory, "xul-window-destroyed", false);
|
||||
|
||||
newWin.close();
|
||||
win.close();
|
||||
}
|
||||
|
||||
let gTabsProgressListener = {
|
||||
/*let gTabsProgressListener = {
|
||||
onStateChange: function(browser, webProgress, request, stateFlags, status) {
|
||||
// ensure about:blank doesn't trigger the code
|
||||
if ((stateFlags & Ci.nsIWebProgressListener.STATE_STOP) &&
|
||||
|
@ -231,13 +180,4 @@ function updateAndCheck() {
|
|||
// clean up and finish
|
||||
restoredWin.close();
|
||||
finish();
|
||||
}
|
||||
|
||||
function load(tab, url, callback) {
|
||||
tab.linkedBrowser.addEventListener("load", function (event) {
|
||||
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
callback();
|
||||
}, true);
|
||||
tab.linkedBrowser.loadURI(url);
|
||||
}
|
||||
|
||||
}*/
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
let url = "http://non.existant/url";
|
||||
let cw;
|
||||
|
||||
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.Storage.loadThumbnail(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.Storage.saveThumbnail(url, data, function (status) {
|
||||
ok(status, "thumbnail entry was saved");
|
||||
ok(saved, "thumbnail was saved asynchronously");
|
||||
|
||||
cw.Storage.loadThumbnail(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.Storage.saveThumbnail(url, data, function (status) {
|
||||
ok(status, "thumbnail entry was saved");
|
||||
ok(!saved, "thumbnail was saved synchronously");
|
||||
|
||||
cw.Storage.loadThumbnail(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);
|
||||
}
|
||||
|
||||
showTabView(function () {
|
||||
registerCleanupFunction(function () TabView.hide());
|
||||
cw = TabView.getContentWindow();
|
||||
|
||||
next();
|
||||
});
|
||||
}
|
|
@ -102,7 +102,8 @@ function test() {
|
|||
gBrowser.loadOneTab('http://mochi.test:8888/', {inBackground: true});
|
||||
|
||||
afterAllTabsLoaded(function () {
|
||||
duplicateTabIn(gBrowser.selectedTab, 'current');
|
||||
// Valid choices for 'where' are window|tabshifted|tab
|
||||
duplicateTabIn(gBrowser.selectedTab, 'tab');
|
||||
|
||||
afterAllTabsLoaded(function () {
|
||||
assertNumberOfVisibleTabs(3);
|
||||
|
|
|
@ -19,10 +19,18 @@ function test() {
|
|||
|
||||
whenTabAttrModified(tab, function () {
|
||||
tabItem = tab._tabViewTabItem;
|
||||
cw.TabItems.resumeReconnecting();
|
||||
ok(tabItem.isShowingCachedData(), 'tabItem shows cached data');
|
||||
|
||||
testChangeUrlAfterReconnect();
|
||||
// Hook into loadedCachedImageData since loading cached thumbnails
|
||||
// is asynchronous.
|
||||
tabItem.addSubscriber(tabItem, "loadedCachedImageData", function(item) {
|
||||
item.removeSubscriber(item, "loadedCachedImageData");
|
||||
|
||||
ok(tabItem.isShowingCachedData(), 'tabItem shows cached data');
|
||||
|
||||
testChangeUrlAfterReconnect();
|
||||
});
|
||||
|
||||
cw.TabItems.resumeReconnecting();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче