зеркало из https://github.com/mozilla/gecko-dev.git
merge m-c to fx-team; a=desktop-only
This commit is contained in:
Коммит
9b21207725
|
@ -1,38 +1,6 @@
|
||||||
/* ***** BEGIN LICENSE BLOCK *****
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
*
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
* 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 content.js.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Tim Taubert <ttaubert@mozilla.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 ***** */
|
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
@ -52,14 +20,6 @@ __defineGetter__("webProgress", function () {
|
||||||
//
|
//
|
||||||
// Handles events dispatched by the content window.
|
// Handles events dispatched by the content window.
|
||||||
let WindowEventHandler = {
|
let WindowEventHandler = {
|
||||||
// ----------
|
|
||||||
// Function: onDOMContentLoaded
|
|
||||||
// Sends an asynchronous message when the "onDOMContentLoaded" event for the
|
|
||||||
// current page is fired.
|
|
||||||
onDOMContentLoaded: function WEH_onDOMContentLoaded(event) {
|
|
||||||
sendAsyncMessage("Panorama:DOMContentLoaded");
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
// Function: onDOMWillOpenModalDialog
|
// Function: onDOMWillOpenModalDialog
|
||||||
// Sends a synchronous message when the "onDOMWillOpenModalDialog" event
|
// Sends a synchronous message when the "onDOMWillOpenModalDialog" event
|
||||||
|
@ -74,12 +34,20 @@ let WindowEventHandler = {
|
||||||
// as quick as possible, switch the selected tab and hide the tabview
|
// as quick as possible, switch the selected tab and hide the tabview
|
||||||
// before the modal dialog is shown
|
// before the modal dialog is shown
|
||||||
sendSyncMessage("Panorama:DOMWillOpenModalDialog");
|
sendSyncMessage("Panorama:DOMWillOpenModalDialog");
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
// Function: onMozAfterPaint
|
||||||
|
// Sends an asynchronous message when the "onMozAfterPaint" event
|
||||||
|
// is fired.
|
||||||
|
onMozAfterPaint: function WEH_onMozAfterPaint(event) {
|
||||||
|
sendAsyncMessage("Panorama:MozAfterPaint");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// add event listeners
|
// add event listeners
|
||||||
addEventListener("DOMContentLoaded", WindowEventHandler.onDOMContentLoaded, false);
|
|
||||||
addEventListener("DOMWillOpenModalDialog", WindowEventHandler.onDOMWillOpenModalDialog, false);
|
addEventListener("DOMWillOpenModalDialog", WindowEventHandler.onDOMWillOpenModalDialog, false);
|
||||||
|
addEventListener("MozAfterPaint", WindowEventHandler.onMozAfterPaint, false);
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
// WindowMessageHandler
|
// WindowMessageHandler
|
||||||
|
@ -90,7 +58,7 @@ let WindowMessageHandler = {
|
||||||
// Function: isDocumentLoaded
|
// Function: isDocumentLoaded
|
||||||
// Checks if the currently active document is loaded.
|
// Checks if the currently active document is loaded.
|
||||||
isDocumentLoaded: function WMH_isDocumentLoaded(cx) {
|
isDocumentLoaded: function WMH_isDocumentLoaded(cx) {
|
||||||
let isLoaded = (content.document.readyState == "complete" &&
|
let isLoaded = (content.document.readyState != "uninitialized" &&
|
||||||
!webProgress.isLoadingDocument);
|
!webProgress.isLoadingDocument);
|
||||||
|
|
||||||
sendAsyncMessage(cx.name, {isLoaded: isLoaded});
|
sendAsyncMessage(cx.name, {isLoaded: isLoaded});
|
||||||
|
@ -110,90 +78,3 @@ let WindowMessageHandler = {
|
||||||
addMessageListener("Panorama:isDocumentLoaded", WindowMessageHandler.isDocumentLoaded);
|
addMessageListener("Panorama:isDocumentLoaded", WindowMessageHandler.isDocumentLoaded);
|
||||||
addMessageListener("Panorama:isImageDocument", WindowMessageHandler.isImageDocument);
|
addMessageListener("Panorama:isImageDocument", WindowMessageHandler.isImageDocument);
|
||||||
|
|
||||||
// ----------
|
|
||||||
// WebProgressListener
|
|
||||||
//
|
|
||||||
// Observe the web progress of content pages loaded into this browser. When the
|
|
||||||
// state of a page changes we check if we're still allowed to store page
|
|
||||||
// information permanently.
|
|
||||||
let WebProgressListener = {
|
|
||||||
// ----------
|
|
||||||
// Function: onStateChange
|
|
||||||
// Called by the webProgress when its state changes.
|
|
||||||
onStateChange: function WPL_onStateChange(webProgress, request, flag, status) {
|
|
||||||
// The browser just started loading (again). Explicitly grant storage
|
|
||||||
// because the browser might have been blocked before (e.g. when navigating
|
|
||||||
// from a https-page to a http-page).
|
|
||||||
if (flag & Ci.nsIWebProgressListener.STATE_START) {
|
|
||||||
// ensure the dom window is the top one
|
|
||||||
if (this._isTopWindow(webProgress))
|
|
||||||
sendAsyncMessage("Panorama:StoragePolicy:granted");
|
|
||||||
}
|
|
||||||
|
|
||||||
// The browser finished loading - check the cache control headers. Send
|
|
||||||
// a message if we're not allowed to store information about this page.
|
|
||||||
if (flag & Ci.nsIWebProgressListener.STATE_STOP) {
|
|
||||||
// ensure the dom window is the top one
|
|
||||||
if (this._isTopWindow(webProgress) &&
|
|
||||||
request && request instanceof Ci.nsIHttpChannel) {
|
|
||||||
request.QueryInterface(Ci.nsIHttpChannel);
|
|
||||||
|
|
||||||
let exclude = false;
|
|
||||||
let reason = "";
|
|
||||||
|
|
||||||
// Check if the "Cache-Control" header is "no-store". In this case we're
|
|
||||||
// not allowed to store information about the current page.
|
|
||||||
if (this._isNoStoreResponse(request)) {
|
|
||||||
exclude = true;
|
|
||||||
reason = "no-store";
|
|
||||||
}
|
|
||||||
// Otherwise we'll deny storage if we're currently viewing a https
|
|
||||||
// page without a "Cache-Control: public" header.
|
|
||||||
else if (request.URI.schemeIs("https")) {
|
|
||||||
let cacheControlHeader = this._getCacheControlHeader(request);
|
|
||||||
if (cacheControlHeader && !(/public/i).test(cacheControlHeader)) {
|
|
||||||
exclude = true;
|
|
||||||
reason = "https";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exclude)
|
|
||||||
sendAsyncMessage("Panorama:StoragePolicy:denied", {reason: reason});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: _isTopWindow
|
|
||||||
// Returns whether the DOMWindow associated with the webProgress is the
|
|
||||||
// top content window (and not an iframe or similar).
|
|
||||||
_isTopWindow: function WPL__isTopWindow(webProgress) {
|
|
||||||
// can throw if there's no associated DOMWindow
|
|
||||||
return !!Utils.attempt(function () webProgress.DOMWindow == content);
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: _isNoStoreResponse
|
|
||||||
// Checks if the "Cache-Control" header is "no-store".
|
|
||||||
_isNoStoreResponse: function WPL__isNoStoreResponse(req) {
|
|
||||||
// can throw if called before the response has been received
|
|
||||||
return !!Utils.attempt(function () req.isNoStoreResponse());
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: _getCacheControlHeader
|
|
||||||
// Returns the value of the "Cache-Control" header.
|
|
||||||
_getCacheControlHeader: function WPL__getCacheControlHeader(req) {
|
|
||||||
// can throw when the "Cache-Control" header doesn't exist
|
|
||||||
return Utils.attempt(function () req.getResponseHeader("Cache-Control"));
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Implements progress listener interface.
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
|
|
||||||
Ci.nsISupportsWeakReference,
|
|
||||||
Ci.nsISupports])
|
|
||||||
};
|
|
||||||
|
|
||||||
// add web progress listener
|
|
||||||
webProgress.addProgressListener(WebProgressListener, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
|
|
||||||
|
|
|
@ -1,208 +0,0 @@
|
||||||
/* ***** 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 storagePolicy.js.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is
|
|
||||||
* the Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Tim Taubert <ttaubert@mozilla.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 ***** */
|
|
||||||
|
|
||||||
// **********
|
|
||||||
// Title: storagePolicy.js
|
|
||||||
|
|
||||||
// ##########
|
|
||||||
// Class: StoragePolicy
|
|
||||||
// Singleton for implementing a storage policy for sensitive data.
|
|
||||||
let StoragePolicy = {
|
|
||||||
// Pref that controls whether we can store SSL content on disk
|
|
||||||
PREF_DISK_CACHE_SSL: "browser.cache.disk_cache_ssl",
|
|
||||||
|
|
||||||
// Used to keep track of disk_cache_ssl preference
|
|
||||||
_enablePersistentHttpsCaching: null,
|
|
||||||
|
|
||||||
// Used to keep track of browsers whose data we shouldn't store permanently
|
|
||||||
_deniedBrowsers: [],
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: toString
|
|
||||||
// Prints [StoragePolicy] for debug use.
|
|
||||||
toString: function StoragePolicy_toString() {
|
|
||||||
return "[StoragePolicy]";
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: init
|
|
||||||
// Initializes the StoragePolicy object.
|
|
||||||
init: function StoragePolicy_init() {
|
|
||||||
// store the preference value
|
|
||||||
this._enablePersistentHttpsCaching =
|
|
||||||
Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
|
|
||||||
|
|
||||||
Services.prefs.addObserver(this.PREF_DISK_CACHE_SSL, this, false);
|
|
||||||
|
|
||||||
// tabs are already loaded before UI is initialized so cache-control
|
|
||||||
// values are unknown. We add browsers with https to the list for now.
|
|
||||||
if (!this._enablePersistentHttpsCaching)
|
|
||||||
Array.forEach(gBrowser.browsers, this._initializeBrowser.bind(this));
|
|
||||||
|
|
||||||
// make sure to remove tab browsers when tabs get closed
|
|
||||||
this._onTabClose = this._onTabClose.bind(this);
|
|
||||||
gBrowser.tabContainer.addEventListener("TabClose", this._onTabClose, false);
|
|
||||||
|
|
||||||
let mm = gWindow.messageManager;
|
|
||||||
|
|
||||||
// add message listeners for storage granted
|
|
||||||
this._onGranted = this._onGranted.bind(this);
|
|
||||||
mm.addMessageListener("Panorama:StoragePolicy:granted", this._onGranted);
|
|
||||||
|
|
||||||
// add message listeners for storage denied
|
|
||||||
this._onDenied = this._onDenied.bind(this);
|
|
||||||
mm.addMessageListener("Panorama:StoragePolicy:denied", this._onDenied);
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: _initializeBrowser
|
|
||||||
// Initializes the given browser and checks if we need to add it to our
|
|
||||||
// internal exclusion list.
|
|
||||||
_initializeBrowser: function StoragePolicy__initializeBrowser(browser) {
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
function checkExclusion() {
|
|
||||||
if (browser.currentURI.schemeIs("https"))
|
|
||||||
self._deniedBrowsers.push(browser);
|
|
||||||
}
|
|
||||||
|
|
||||||
function waitForDocumentLoad() {
|
|
||||||
let mm = browser.messageManager;
|
|
||||||
|
|
||||||
mm.addMessageListener("Panorama:DOMContentLoaded", function onLoad(cx) {
|
|
||||||
mm.removeMessageListener(cx.name, onLoad);
|
|
||||||
checkExclusion(browser);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this._isDocumentLoaded(browser, function (isLoaded) {
|
|
||||||
if (isLoaded)
|
|
||||||
checkExclusion();
|
|
||||||
else
|
|
||||||
waitForDocumentLoad();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: _isDocumentLoaded
|
|
||||||
// Check if the given browser's document is loaded.
|
|
||||||
_isDocumentLoaded: function StoragePolicy__isDocumentLoaded(browser, callback) {
|
|
||||||
let mm = browser.messageManager;
|
|
||||||
let message = "Panorama:isDocumentLoaded";
|
|
||||||
|
|
||||||
mm.addMessageListener(message, function onMessage(cx) {
|
|
||||||
mm.removeMessageListener(cx.name, onMessage);
|
|
||||||
callback(cx.json.isLoaded);
|
|
||||||
});
|
|
||||||
|
|
||||||
mm.sendAsyncMessage(message);
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: uninit
|
|
||||||
// Is called by UI.init() when the browser windows is closed.
|
|
||||||
uninit: function StoragePolicy_uninit() {
|
|
||||||
Services.prefs.removeObserver(this.PREF_DISK_CACHE_SSL, this);
|
|
||||||
gBrowser.removeTabsProgressListener(this);
|
|
||||||
gBrowser.tabContainer.removeEventListener("TabClose", this._onTabClose, false);
|
|
||||||
|
|
||||||
let mm = gWindow.messageManager;
|
|
||||||
|
|
||||||
// remove message listeners
|
|
||||||
mm.removeMessageListener("Panorama:StoragePolicy:granted", this._onGranted);
|
|
||||||
mm.removeMessageListener("Panorama:StoragePolicy:denied", this._onDenied);
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: _onGranted
|
|
||||||
// Handle the 'granted' message and remove the given browser from the list
|
|
||||||
// of denied browsers.
|
|
||||||
_onGranted: function StoragePolicy__onGranted(cx) {
|
|
||||||
let index = this._deniedBrowsers.indexOf(cx.target);
|
|
||||||
|
|
||||||
if (index > -1)
|
|
||||||
this._deniedBrowsers.splice(index, 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: _onDenied
|
|
||||||
// Handle the 'denied' message and add the given browser to the list of denied
|
|
||||||
// browsers.
|
|
||||||
_onDenied: function StoragePolicy__onDenied(cx) {
|
|
||||||
// exclusion is optional because cache-control is not no-store or public and
|
|
||||||
// the protocol is https. don't exclude when persistent https caching is
|
|
||||||
// enabled.
|
|
||||||
if ("https" == cx.json.reason && this._enablePersistentHttpsCaching)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let browser = cx.target;
|
|
||||||
|
|
||||||
if (this._deniedBrowsers.indexOf(browser) == -1)
|
|
||||||
this._deniedBrowsers.push(browser);
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: _onTabClose
|
|
||||||
// Remove the browser from our internal exclusion list when a tab gets closed.
|
|
||||||
_onTabClose: function StoragePolicy__onTabClose(event) {
|
|
||||||
let browser = event.target.linkedBrowser;
|
|
||||||
let index = this._deniedBrowsers.indexOf(browser);
|
|
||||||
|
|
||||||
if (index > -1)
|
|
||||||
this._deniedBrowsers.splice(index, 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: canStoreThumbnailForTab
|
|
||||||
// Returns whether we're allowed to store the thumbnail of the given tab.
|
|
||||||
canStoreThumbnailForTab: function StoragePolicy_canStoreThumbnailForTab(tab) {
|
|
||||||
// deny saving thumbnails in private browsing mode
|
|
||||||
if (gPrivateBrowsing.privateBrowsingEnabled &&
|
|
||||||
UI._privateBrowsing.transitionMode != "enter")
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return (this._deniedBrowsers.indexOf(tab.linkedBrowser) == -1);
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: observe
|
|
||||||
// Observe pref changes.
|
|
||||||
observe: function StoragePolicy_observe(subject, topic, data) {
|
|
||||||
this._enablePersistentHttpsCaching =
|
|
||||||
Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -68,8 +68,7 @@ function TabItem(tab, options) {
|
||||||
let div = document.body.lastChild;
|
let div = document.body.lastChild;
|
||||||
let $div = iQ(div);
|
let $div = iQ(div);
|
||||||
|
|
||||||
this._cachedImageData = null;
|
this._showsCachedData = false;
|
||||||
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);
|
||||||
|
@ -81,13 +80,6 @@ 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;
|
||||||
|
@ -123,6 +115,8 @@ function TabItem(tab, options) {
|
||||||
|
|
||||||
this.draggable();
|
this.draggable();
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
|
||||||
// ___ more div setup
|
// ___ more div setup
|
||||||
$div.mousedown(function(e) {
|
$div.mousedown(function(e) {
|
||||||
if (!Utils.isRightClick(e))
|
if (!Utils.isRightClick(e))
|
||||||
|
@ -190,35 +184,32 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||||
// Returns a boolean indicates whether the cached data is being displayed or
|
// Returns a boolean indicates whether the cached data is being displayed or
|
||||||
// not.
|
// not.
|
||||||
isShowingCachedData: function TabItem_isShowingCachedData() {
|
isShowingCachedData: function TabItem_isShowingCachedData() {
|
||||||
return (this._cachedImageData != null);
|
return this._showsCachedData;
|
||||||
},
|
},
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
// Function: showCachedData
|
// Function: showCachedData
|
||||||
// Shows the cached data i.e. image and title. Note: this method should only
|
// Shows the cached data i.e. image and title. Note: this method should only
|
||||||
// be called at browser startup with the cached data avaliable.
|
// be called at browser startup with the cached data avaliable.
|
||||||
//
|
showCachedData: function TabItem_showCachedData() {
|
||||||
// Parameters:
|
let {title, url} = this.getTabState();
|
||||||
// imageData - the image data
|
let thumbnailURL = gPageThumbnails.getThumbnailURL(url);
|
||||||
showCachedData: function TabItem_showCachedData(imageData) {
|
|
||||||
this._cachedImageData = imageData;
|
this.$cachedThumb.attr("src", thumbnailURL).show();
|
||||||
this.$cachedThumb.attr("src", this._cachedImageData).show();
|
|
||||||
this.$canvas.css({opacity: 0});
|
this.$canvas.css({opacity: 0});
|
||||||
|
|
||||||
let {title, url} = this.getTabState();
|
let tooltip = (title && title != url ? title + "\n" + url : url);
|
||||||
this.$tabTitle.text(title).attr("title", title ? title + "\n" + url : url);
|
this.$tabTitle.text(title).attr("title", tooltip);
|
||||||
|
this._showsCachedData = true;
|
||||||
this._sendToSubscribers("showingCachedData");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
// Function: hideCachedData
|
// Function: hideCachedData
|
||||||
// Hides the cached data i.e. image and title and show the canvas.
|
// Hides the cached data i.e. image and title and show the canvas.
|
||||||
hideCachedData: function TabItem_hideCachedData() {
|
hideCachedData: function TabItem_hideCachedData() {
|
||||||
this.$cachedThumb.hide();
|
this.$cachedThumb.attr("src", "").hide();
|
||||||
this.$canvas.css({opacity: 1.0});
|
this.$canvas.css({opacity: 1.0});
|
||||||
if (this._cachedImageData)
|
this._showsCachedData = false;
|
||||||
this._cachedImageData = null;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
|
@ -286,95 +277,6 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||||
return {title: title, url: url};
|
return {title: title, url: url};
|
||||||
},
|
},
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: loadThumbnail
|
|
||||||
// Loads the tabItems thumbnail.
|
|
||||||
loadThumbnail: function TabItem_loadThumbnail() {
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
function TabItem_loadThumbnail_callback(error, imageData) {
|
|
||||||
// we could have been unlinked while waiting for the thumbnail to load
|
|
||||||
if (!self.tab)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (error || !imageData) {
|
|
||||||
// paint the canvas to avoid leaving traces when dragging tab over it
|
|
||||||
self.tabCanvas.paint();
|
|
||||||
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 (self.getTabState().url == currentUrl || currentUrl == "about:blank")
|
|
||||||
self.showCachedData(imageData);
|
|
||||||
}
|
|
||||||
|
|
||||||
ThumbnailStorage.loadThumbnail(this.getTabState().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,
|
||||||
|
@ -394,7 +296,9 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||||
let groupItem;
|
let groupItem;
|
||||||
|
|
||||||
if (tabData && TabItems.storageSanity(tabData)) {
|
if (tabData && TabItems.storageSanity(tabData)) {
|
||||||
this.loadThumbnail();
|
// Show the cached data while we're waiting for the tabItem to be updated.
|
||||||
|
// If the tab isn't restored yet this acts as a placeholder until it is.
|
||||||
|
this.showCachedData();
|
||||||
|
|
||||||
if (this.parent)
|
if (this.parent)
|
||||||
this.parent.remove(this, {immediately: true});
|
this.parent.remove(this, {immediately: true});
|
||||||
|
@ -801,6 +705,32 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||||
transformOrigin: xOrigin + "% " + yOrigin + "%",
|
transformOrigin: xOrigin + "% " + yOrigin + "%",
|
||||||
transform: "scale(" + zoomScaleFactor + ")"
|
transform: "scale(" + zoomScaleFactor + ")"
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
// Function: updateCanvas
|
||||||
|
// Updates the tabitem's canvas.
|
||||||
|
updateCanvas: function TabItem_updateCanvas() {
|
||||||
|
// ___ thumbnail
|
||||||
|
let $canvas = this.$canvas;
|
||||||
|
if (!this.canvasSizeForced) {
|
||||||
|
let w = $canvas.width();
|
||||||
|
let h = $canvas.height();
|
||||||
|
if (w != $canvas[0].width || h != $canvas[0].height) {
|
||||||
|
$canvas[0].width = w;
|
||||||
|
$canvas[0].height = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TabItems._lastUpdateTime = Date.now();
|
||||||
|
this._lastTabUpdateTime = TabItems._lastUpdateTime;
|
||||||
|
|
||||||
|
if (this.tabCanvas)
|
||||||
|
this.tabCanvas.paint();
|
||||||
|
|
||||||
|
// ___ cache
|
||||||
|
if (this.isShowingCachedData())
|
||||||
|
this.hideCachedData();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -828,6 +758,7 @@ let TabItems = {
|
||||||
tempCanvas: null,
|
tempCanvas: null,
|
||||||
_reconnectingPaused: false,
|
_reconnectingPaused: false,
|
||||||
tabItemPadding: {},
|
tabItemPadding: {},
|
||||||
|
_mozAfterPaintHandler: null,
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
// Function: toString
|
// Function: toString
|
||||||
|
@ -859,6 +790,10 @@ let TabItems = {
|
||||||
this.tempCanvas.width = 150;
|
this.tempCanvas.width = 150;
|
||||||
this.tempCanvas.height = 112;
|
this.tempCanvas.height = 112;
|
||||||
|
|
||||||
|
let mm = gWindow.messageManager;
|
||||||
|
this._mozAfterPaintHandler = this.onMozAfterPaint.bind(this);
|
||||||
|
mm.addMessageListener("Panorama:MozAfterPaint", this._mozAfterPaintHandler);
|
||||||
|
|
||||||
// When a tab is opened, create the TabItem
|
// When a tab is opened, create the TabItem
|
||||||
this._eventListeners.open = function (event) {
|
this._eventListeners.open = function (event) {
|
||||||
let tab = event.target;
|
let tab = event.target;
|
||||||
|
@ -908,6 +843,9 @@ let TabItems = {
|
||||||
// ----------
|
// ----------
|
||||||
// Function: uninit
|
// Function: uninit
|
||||||
uninit: function TabItems_uninit() {
|
uninit: function TabItems_uninit() {
|
||||||
|
let mm = gWindow.messageManager;
|
||||||
|
mm.removeMessageListener("Panorama:MozAfterPaint", this._mozAfterPaintHandler);
|
||||||
|
|
||||||
for (let name in this._eventListeners) {
|
for (let name in this._eventListeners) {
|
||||||
AllTabs.unregister(name, this._eventListeners[name]);
|
AllTabs.unregister(name, this._eventListeners[name]);
|
||||||
}
|
}
|
||||||
|
@ -946,20 +884,33 @@ let TabItems = {
|
||||||
return this._fragment;
|
return this._fragment;
|
||||||
},
|
},
|
||||||
|
|
||||||
// ----------
|
// Function: _isComplete
|
||||||
// Function: isComplete
|
// Checks whether the xul:tab has fully loaded and calls a callback with a
|
||||||
// Return whether the xul:tab has fully loaded.
|
// boolean indicates whether the tab is loaded or not.
|
||||||
isComplete: function TabItems_isComplete(tab) {
|
_isComplete: function TabItems__isComplete(tab, callback) {
|
||||||
// If our readyState is complete, but we're showing about:blank,
|
|
||||||
// and we're not loading about:blank, it means we haven't really
|
|
||||||
// started loading. This can happen to the first few tabs in a
|
|
||||||
// page.
|
|
||||||
Utils.assertThrow(tab, "tab");
|
Utils.assertThrow(tab, "tab");
|
||||||
return (
|
|
||||||
tab.linkedBrowser.contentDocument.readyState == 'complete' &&
|
let mm = tab.linkedBrowser.messageManager;
|
||||||
!(tab.linkedBrowser.contentDocument.URL == 'about:blank' &&
|
let message = "Panorama:isDocumentLoaded";
|
||||||
tab._tabViewTabItem.getTabState().url != 'about:blank')
|
|
||||||
);
|
mm.addMessageListener(message, function onMessage(cx) {
|
||||||
|
mm.removeMessageListener(cx.name, onMessage);
|
||||||
|
callback(cx.json.isLoaded);
|
||||||
|
});
|
||||||
|
mm.sendAsyncMessage(message);
|
||||||
|
},
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
// Function: onMozAfterPaint
|
||||||
|
// Called when a web page is painted.
|
||||||
|
onMozAfterPaint: function TabItems_onMozAfterPaint(cx) {
|
||||||
|
let index = gBrowser.browsers.indexOf(cx.target);
|
||||||
|
if (index == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let tab = gBrowser.tabs[index];
|
||||||
|
if (!tab.pinned)
|
||||||
|
this.update(tab);
|
||||||
},
|
},
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
|
@ -1037,38 +988,26 @@ let TabItems = {
|
||||||
|
|
||||||
// ___ URL
|
// ___ URL
|
||||||
let tabUrl = tab.linkedBrowser.currentURI.spec;
|
let tabUrl = tab.linkedBrowser.currentURI.spec;
|
||||||
tabItem.$container.attr("title", label + "\n" + tabUrl);
|
let tooltip = (label == tabUrl ? label : label + "\n" + tabUrl);
|
||||||
|
tabItem.$container.attr("title", tooltip);
|
||||||
|
|
||||||
// ___ Make sure the tab is complete and ready for updating.
|
// ___ Make sure the tab is complete and ready for updating.
|
||||||
if (!this.isComplete(tab) && (!options || !options.force)) {
|
if (options && options.force) {
|
||||||
// If it's incomplete, stick it on the end of the queue
|
tabItem.updateCanvas();
|
||||||
this._tabsWaitingForUpdate.push(tab);
|
tabItem._sendToSubscribers("updated");
|
||||||
return;
|
} else {
|
||||||
|
this._isComplete(tab, function TabItems__update_isComplete(isComplete) {
|
||||||
|
if (!Utils.isValidXULTab(tab) || tab.pinned)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (isComplete) {
|
||||||
|
tabItem.updateCanvas();
|
||||||
|
tabItem._sendToSubscribers("updated");
|
||||||
|
} else {
|
||||||
|
this._tabsWaitingForUpdate.push(tab);
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ___ thumbnail
|
|
||||||
let $canvas = tabItem.$canvas;
|
|
||||||
if (!tabItem.canvasSizeForced) {
|
|
||||||
let w = $canvas.width();
|
|
||||||
let h = $canvas.height();
|
|
||||||
if (w != tabItem.$canvas[0].width || h != tabItem.$canvas[0].height) {
|
|
||||||
tabItem.$canvas[0].width = w;
|
|
||||||
tabItem.$canvas[0].height = h;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._lastUpdateTime = Date.now();
|
|
||||||
tabItem._lastTabUpdateTime = this._lastUpdateTime;
|
|
||||||
|
|
||||||
tabItem.tabCanvas.paint();
|
|
||||||
tabItem.saveThumbnail();
|
|
||||||
|
|
||||||
// ___ cache
|
|
||||||
if (tabItem.isShowingCachedData())
|
|
||||||
tabItem.hideCachedData();
|
|
||||||
|
|
||||||
// ___ notify subscribers that a full update has completed.
|
|
||||||
tabItem._sendToSubscribers("updated");
|
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
Utils.log(e);
|
Utils.log(e);
|
||||||
}
|
}
|
||||||
|
@ -1280,17 +1219,6 @@ let TabItems = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// ----------
|
|
||||||
// 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);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
// Function: storageSanity
|
// Function: storageSanity
|
||||||
// Checks the specified data (as returned by TabItem.getStorageData or loaded from storage)
|
// Checks the specified data (as returned by TabItem.getStorageData or loaded from storage)
|
||||||
|
|
|
@ -25,16 +25,11 @@ XPCOMUtils.defineLazyGetter(this, "gPrefBranch", function() {
|
||||||
return Services.prefs.getBranch("browser.panorama.");
|
return Services.prefs.getBranch("browser.panorama.");
|
||||||
});
|
});
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "gPrivateBrowsing", function() {
|
XPCOMUtils.defineLazyServiceGetter(this, "gPrivateBrowsing",
|
||||||
return Cc["@mozilla.org/privatebrowsing;1"].
|
"@mozilla.org/privatebrowsing;1", "nsIPrivateBrowsingService");
|
||||||
getService(Ci.nsIPrivateBrowsingService);
|
|
||||||
});
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "gNetUtil", function() {
|
XPCOMUtils.defineLazyModuleGetter(this, "gPageThumbnails",
|
||||||
var obj = {};
|
"resource:///modules/PageThumbs.jsm", "PageThumbs");
|
||||||
Cu.import("resource://gre/modules/NetUtil.jsm", obj);
|
|
||||||
return obj.NetUtil;
|
|
||||||
});
|
|
||||||
|
|
||||||
var gWindow = window.parent;
|
var gWindow = window.parent;
|
||||||
var gBrowser = gWindow.gBrowser;
|
var gBrowser = gWindow.gBrowser;
|
||||||
|
@ -71,13 +66,11 @@ let AllTabs = {
|
||||||
|
|
||||||
#include iq.js
|
#include iq.js
|
||||||
#include storage.js
|
#include storage.js
|
||||||
#include storagePolicy.js
|
|
||||||
#include items.js
|
#include items.js
|
||||||
#include groupitems.js
|
#include groupitems.js
|
||||||
#include tabitems.js
|
#include tabitems.js
|
||||||
#include favicons.js
|
#include favicons.js
|
||||||
#include drag.js
|
#include drag.js
|
||||||
#include trench.js
|
#include trench.js
|
||||||
#include thumbnailStorage.js
|
|
||||||
#include search.js
|
#include search.js
|
||||||
#include ui.js
|
#include ui.js
|
||||||
|
|
|
@ -71,7 +71,6 @@ _BROWSER_FILES = \
|
||||||
browser_tabview_bug595943.js \
|
browser_tabview_bug595943.js \
|
||||||
browser_tabview_bug595965.js \
|
browser_tabview_bug595965.js \
|
||||||
browser_tabview_bug596781.js \
|
browser_tabview_bug596781.js \
|
||||||
browser_tabview_bug597248.js \
|
|
||||||
browser_tabview_bug597360.js \
|
browser_tabview_bug597360.js \
|
||||||
browser_tabview_bug597399.js \
|
browser_tabview_bug597399.js \
|
||||||
browser_tabview_bug598375.js \
|
browser_tabview_bug598375.js \
|
||||||
|
@ -115,7 +114,6 @@ _BROWSER_FILES = \
|
||||||
browser_tabview_bug626455.js \
|
browser_tabview_bug626455.js \
|
||||||
browser_tabview_bug626525.js \
|
browser_tabview_bug626525.js \
|
||||||
browser_tabview_bug626791.js \
|
browser_tabview_bug626791.js \
|
||||||
browser_tabview_bug627288.js \
|
|
||||||
browser_tabview_bug627736.js \
|
browser_tabview_bug627736.js \
|
||||||
browser_tabview_bug628061.js \
|
browser_tabview_bug628061.js \
|
||||||
browser_tabview_bug628165.js \
|
browser_tabview_bug628165.js \
|
||||||
|
@ -151,13 +149,13 @@ _BROWSER_FILES = \
|
||||||
browser_tabview_bug655269.js \
|
browser_tabview_bug655269.js \
|
||||||
browser_tabview_bug656778.js \
|
browser_tabview_bug656778.js \
|
||||||
browser_tabview_bug656913.js \
|
browser_tabview_bug656913.js \
|
||||||
|
browser_tabview_bug659594.js \
|
||||||
browser_tabview_bug662266.js \
|
browser_tabview_bug662266.js \
|
||||||
browser_tabview_bug663421.js \
|
browser_tabview_bug663421.js \
|
||||||
browser_tabview_bug665502.js \
|
browser_tabview_bug665502.js \
|
||||||
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_bug678374.js \
|
browser_tabview_bug678374.js \
|
||||||
browser_tabview_bug679853.js \
|
browser_tabview_bug679853.js \
|
||||||
browser_tabview_bug681599.js \
|
browser_tabview_bug681599.js \
|
||||||
|
@ -188,8 +186,6 @@ _BROWSER_FILES = \
|
||||||
browser_tabview_search.js \
|
browser_tabview_search.js \
|
||||||
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_thumbnail_storage.js \
|
|
||||||
browser_tabview_undo_group.js \
|
browser_tabview_undo_group.js \
|
||||||
dummy_page.html \
|
dummy_page.html \
|
||||||
head.js \
|
head.js \
|
||||||
|
|
|
@ -54,12 +54,17 @@ function test() {
|
||||||
let test = tests.shift();
|
let test = tests.shift();
|
||||||
let tab = win.gBrowser.tabs[0];
|
let tab = win.gBrowser.tabs[0];
|
||||||
|
|
||||||
tab.linkedBrowser.addEventListener('load', function onLoad() {
|
let browser = tab.linkedBrowser;
|
||||||
tab.linkedBrowser.removeEventListener('load', onLoad, true);
|
browser.addEventListener("load", function onLoad(event) {
|
||||||
checkUrl(test);
|
browser.removeEventListener("load", onLoad, true);
|
||||||
}, true);
|
|
||||||
|
|
||||||
tab.linkedBrowser.loadURI(test.url);
|
let tabItem = tab._tabViewTabItem;
|
||||||
|
tabItem.addSubscriber("updated", function onUpdated() {
|
||||||
|
tabItem.removeSubscriber("updated", onUpdated);
|
||||||
|
checkUrl(test);
|
||||||
|
});
|
||||||
|
}, true);
|
||||||
|
browser.loadURI(test.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
let checkUrl = function (test) {
|
let checkUrl = function (test) {
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/* Any copyright is dedicated to the Public Domain.
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
|
||||||
|
|
||||||
let stateStartup = {windows:[
|
let stateStartup = {windows:[
|
||||||
{tabs:[{entries:[{url:"about:home"}]}], extData:{"tabview-last-session-group-name":"title"}}
|
{tabs:[{entries:[{url:"about:home"}]}], extData:{"tabview-last-session-group-name":"title"}}
|
||||||
]};
|
]};
|
||||||
|
|
|
@ -1,146 +0,0 @@
|
||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
||||||
|
|
||||||
let newTabOne;
|
|
||||||
let newTabTwo;
|
|
||||||
let newTabThree;
|
|
||||||
let restoredNewTabTwoLoaded = false;
|
|
||||||
let restoredNewTabThreeLoaded = false;
|
|
||||||
let frameInitialized = false;
|
|
||||||
|
|
||||||
function test() {
|
|
||||||
waitForExplicitFinish();
|
|
||||||
newWindowWithTabView(setupOne);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupOne(win) {
|
|
||||||
win.TabView.firstUseExperienced = true;
|
|
||||||
|
|
||||||
win.gBrowser.addTab("http://mochi.test:8888/browser/browser/components/tabview/test/search1.html");
|
|
||||||
win.gBrowser.addTab("http://mochi.test:8888/browser/browser/components/tabview/test/dummy_page.html");
|
|
||||||
|
|
||||||
afterAllTabsLoaded(function () setupTwo(win), win);
|
|
||||||
}
|
|
||||||
|
|
||||||
let restoredWin;
|
|
||||||
|
|
||||||
function setupTwo(win) {
|
|
||||||
let contentWindow = win.TabView.getContentWindow();
|
|
||||||
|
|
||||||
let tabItems = contentWindow.TabItems.getItems();
|
|
||||||
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) {
|
|
||||||
// mark thumbnail as dirty
|
|
||||||
tabItem.tabCanvas.paint();
|
|
||||||
|
|
||||||
tabItem.addSubscriber("savedCachedImageData", function onSaved(item) {
|
|
||||||
item.removeSubscriber("savedCachedImageData", onSaved);
|
|
||||||
|
|
||||||
if (!--numTabsToSave)
|
|
||||||
restoreWindow();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// after the window is closed, restore it.
|
|
||||||
let restoreWindow = function() {
|
|
||||||
executeSoon(function() {
|
|
||||||
restoredWin = undoCloseWindow();
|
|
||||||
restoredWin.addEventListener("load", function onLoad(event) {
|
|
||||||
restoredWin.removeEventListener("load", onLoad, false);
|
|
||||||
|
|
||||||
registerCleanupFunction(function() restoredWin.close());
|
|
||||||
is(restoredWin.gBrowser.tabs.length, 3, "The total number of tabs is 3");
|
|
||||||
|
|
||||||
// setup tab variables and listen to the tabs load progress
|
|
||||||
newTabOne = restoredWin.gBrowser.tabs[0];
|
|
||||||
newTabTwo = restoredWin.gBrowser.tabs[1];
|
|
||||||
newTabThree = restoredWin.gBrowser.tabs[2];
|
|
||||||
restoredWin.gBrowser.addTabsProgressListener(gTabsProgressListener);
|
|
||||||
|
|
||||||
// execute code when the frame is initialized
|
|
||||||
let onTabViewFrameInitialized = function() {
|
|
||||||
restoredWin.removeEventListener(
|
|
||||||
"tabviewframeinitialized", onTabViewFrameInitialized, false);
|
|
||||||
|
|
||||||
let restoredContentWindow = restoredWin.TabView.getContentWindow();
|
|
||||||
// prevent TabItems._update being called before checking cached images
|
|
||||||
restoredContentWindow.TabItems._pauseUpdateForTest = true;
|
|
||||||
|
|
||||||
let nextStep = function() {
|
|
||||||
// since we are not sure whether the frame is initialized first or two tabs
|
|
||||||
// compete loading first so we need this.
|
|
||||||
if (restoredNewTabTwoLoaded && restoredNewTabThreeLoaded)
|
|
||||||
updateAndCheck();
|
|
||||||
else
|
|
||||||
frameInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let tabItems = restoredContentWindow.TabItems.getItems();
|
|
||||||
let count = tabItems.length;
|
|
||||||
|
|
||||||
tabItems.forEach(function(tabItem) {
|
|
||||||
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);
|
|
||||||
if (--count == 0)
|
|
||||||
nextStep();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
restoredWin.addEventListener(
|
|
||||||
"tabviewframeinitialized", onTabViewFrameInitialized, false);
|
|
||||||
}, false);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
win.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
let gTabsProgressListener = {
|
|
||||||
onStateChange: function(browser, webProgress, request, stateFlags, status) {
|
|
||||||
// ensure about:blank doesn't trigger the code
|
|
||||||
if ((stateFlags & Ci.nsIWebProgressListener.STATE_STOP) &&
|
|
||||||
(stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) &&
|
|
||||||
browser.currentURI.spec != "about:blank") {
|
|
||||||
if (newTabTwo.linkedBrowser == browser)
|
|
||||||
restoredNewTabTwoLoaded = true;
|
|
||||||
else if (newTabThree.linkedBrowser == browser)
|
|
||||||
restoredNewTabThreeLoaded = true;
|
|
||||||
|
|
||||||
// since we are not sure whether the frame is initialized first or two tabs
|
|
||||||
// compete loading first so we need this.
|
|
||||||
if (restoredNewTabTwoLoaded && restoredNewTabThreeLoaded) {
|
|
||||||
restoredWin.gBrowser.removeTabsProgressListener(gTabsProgressListener);
|
|
||||||
|
|
||||||
if (frameInitialized)
|
|
||||||
updateAndCheck();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function updateAndCheck() {
|
|
||||||
// force all canvas to update
|
|
||||||
let contentWindow = restoredWin.TabView.getContentWindow();
|
|
||||||
|
|
||||||
contentWindow.TabItems._pauseUpdateForTest = false;
|
|
||||||
|
|
||||||
let tabItems = contentWindow.TabItems.getItems();
|
|
||||||
tabItems.forEach(function(tabItem) {
|
|
||||||
contentWindow.TabItems._update(tabItem.tab);
|
|
||||||
ok(!tabItem.isShowingCachedData(),
|
|
||||||
"Tab item is not showing cached data anymore. " +
|
|
||||||
tabItem.tab.linkedBrowser.currentURI.spec);
|
|
||||||
});
|
|
||||||
|
|
||||||
// clean up and finish
|
|
||||||
restoredWin.close();
|
|
||||||
finish();
|
|
||||||
}
|
|
|
@ -67,34 +67,30 @@ function onTabViewWindowLoaded(win) {
|
||||||
}
|
}
|
||||||
|
|
||||||
afterAllTabsLoaded(function() {
|
afterAllTabsLoaded(function() {
|
||||||
afterAllTabItemsUpdated(function() {
|
let children = group.getChildren();
|
||||||
let children = group.getChildren();
|
let len = children.length;
|
||||||
let len = children.length;
|
let iconUpdateCounter = 0;
|
||||||
let iconUpdateCounter = 0;
|
|
||||||
|
|
||||||
children.forEach(function(tabItem) {
|
children.forEach(function(tabItem) {
|
||||||
tabItem.addSubscriber("iconUpdated", function onIconUpdated() {
|
tabItem.addSubscriber("iconUpdated", function onIconUpdated() {
|
||||||
// the tab is not loaded completely so ignore it.
|
tabItem.removeSubscriber("iconUpdated", onIconUpdated);
|
||||||
if (tabItem.tab.linkedBrowser.currentURI.spec == "about:blank")
|
|
||||||
return;
|
|
||||||
|
|
||||||
tabItem.removeSubscriber("iconUpdated", onIconUpdated);
|
if (++iconUpdateCounter == len) {
|
||||||
|
check(datatext, "datatext", false);
|
||||||
|
check(datahtml, "datahtml", false);
|
||||||
|
check(mozilla, "about:mozilla", false);
|
||||||
|
check(robots, "about:robots", true);
|
||||||
|
check(html, "html", true);
|
||||||
|
check(png, "png", false);
|
||||||
|
check(svg, "svg", true);
|
||||||
|
|
||||||
if (++iconUpdateCounter == len) {
|
// Get rid of the group and its children
|
||||||
check(datatext, "datatext", false);
|
// The group close will trigger a finish().
|
||||||
check(datahtml, "datahtml", false);
|
closeGroupItem(group);
|
||||||
check(mozilla, "about:mozilla", false);
|
}
|
||||||
check(robots, "about:robots", true);
|
|
||||||
check(html, "html", true);
|
|
||||||
check(png, "png", false);
|
|
||||||
check(svg, "svg", true);
|
|
||||||
|
|
||||||
// Get rid of the group and its children
|
|
||||||
// The group close will trigger a finish().
|
|
||||||
closeGroupItem(group);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}, win);
|
});
|
||||||
|
|
||||||
|
afterAllTabItemsUpdated(function () {}, win);
|
||||||
}, win);
|
}, win);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,93 +0,0 @@
|
||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
||||||
|
|
||||||
function test() {
|
|
||||||
let cw;
|
|
||||||
let tab;
|
|
||||||
|
|
||||||
let testReconnectWithSameUrl = function () {
|
|
||||||
tab = gBrowser.loadOneTab('http://mochi.test:8888/', {inBackground: true});
|
|
||||||
|
|
||||||
afterAllTabsLoaded(function () {
|
|
||||||
let tabItem = tab._tabViewTabItem;
|
|
||||||
let data = tabItem.getStorageData(true);
|
|
||||||
gBrowser.removeTab(tab);
|
|
||||||
|
|
||||||
cw.TabItems.pauseReconnecting();
|
|
||||||
tab = gBrowser.loadOneTab('http://mochi.test:8888/', {inBackground: true});
|
|
||||||
|
|
||||||
afterAllTabsLoaded(function () {
|
|
||||||
tabItem = tab._tabViewTabItem;
|
|
||||||
|
|
||||||
tabItem.addSubscriber("savedCachedImageData", function onSaved() {
|
|
||||||
tabItem.removeSubscriber("savedCachedImageData", onSaved);
|
|
||||||
|
|
||||||
tabItem.addSubscriber("showingCachedData", function onLoaded() {
|
|
||||||
tabItem.removeSubscriber("showingCachedData", onLoaded);
|
|
||||||
|
|
||||||
ok(tabItem.isShowingCachedData(), 'tabItem shows cached data');
|
|
||||||
testChangeUrlAfterReconnect();
|
|
||||||
});
|
|
||||||
|
|
||||||
cw.TabItems.resumeReconnecting();
|
|
||||||
});
|
|
||||||
|
|
||||||
cw.Storage.saveTab(tab, data);
|
|
||||||
tabItem.saveThumbnail();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let testChangeUrlAfterReconnect = function () {
|
|
||||||
tab.linkedBrowser.loadURI('http://mochi.test:8888/browser/');
|
|
||||||
|
|
||||||
whenTabAttrModified(tab, function () {
|
|
||||||
cw.TabItems._update(tab);
|
|
||||||
|
|
||||||
let tabItem = tab._tabViewTabItem;
|
|
||||||
let currentLabel = tabItem.$tabTitle.text();
|
|
||||||
|
|
||||||
is(currentLabel, 'mochitest index /browser/', 'tab label is up-to-date');
|
|
||||||
testReconnectWithNewUrl();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let testReconnectWithNewUrl = function () {
|
|
||||||
let tabItem = tab._tabViewTabItem;
|
|
||||||
let data = tabItem.getStorageData(true);
|
|
||||||
gBrowser.removeTab(tab);
|
|
||||||
|
|
||||||
cw.TabItems.pauseReconnecting();
|
|
||||||
tab = gBrowser.loadOneTab('http://mochi.test:8888/', {inBackground: true});
|
|
||||||
cw.Storage.saveTab(tab, data);
|
|
||||||
|
|
||||||
whenTabAttrModified(tab, function () {
|
|
||||||
tabItem = tab._tabViewTabItem;
|
|
||||||
cw.TabItems.resumeReconnecting();
|
|
||||||
ok(!tabItem.isShowingCachedData(), 'tabItem does not show cached data');
|
|
||||||
|
|
||||||
gBrowser.removeTab(tab);
|
|
||||||
hideTabView(finish);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
waitForExplicitFinish();
|
|
||||||
|
|
||||||
showTabView(function () {
|
|
||||||
cw = TabView.getContentWindow();
|
|
||||||
testReconnectWithSameUrl();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
function whenTabAttrModified(tab, callback) {
|
|
||||||
let onModified = function (event) {
|
|
||||||
tab.removeEventListener(event.type, onModified, false);
|
|
||||||
// we need executeSoon here because the tabItem also listens for the
|
|
||||||
// TabAttrModified event. so this is to make sure the tabItem logic
|
|
||||||
// is executed before the test logic.
|
|
||||||
executeSoon(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
tab.addEventListener("TabAttrModified", onModified, false);
|
|
||||||
}
|
|
|
@ -6,13 +6,14 @@ function test() {
|
||||||
|
|
||||||
newWindowWithTabView(function (win) {
|
newWindowWithTabView(function (win) {
|
||||||
let cw = win.TabView.getContentWindow();
|
let cw = win.TabView.getContentWindow();
|
||||||
let tabItem = win.gBrowser.tabs[0]._tabViewTabItem;
|
|
||||||
|
|
||||||
tabItem.addSubscriber("savedCachedImageData", function onSaved() {
|
win.addEventListener("SSWindowClosing", function onClose() {
|
||||||
tabItem.removeSubscriber("savedCachedImageData", onSaved);
|
win.removeEventListener("SSWindowClosing", onClose);
|
||||||
|
|
||||||
ok(cw.UI.isDOMWindowClosing, "dom window is closing");
|
executeSoon(function () {
|
||||||
waitForFocus(finish);
|
ok(cw.UI.isDOMWindowClosing, "dom window is closing");
|
||||||
|
waitForFocus(finish);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
win.close();
|
win.close();
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
|
||||||
|
newWindowWithTabView(function(win) {
|
||||||
|
let numTabsToUpdate = 2;
|
||||||
|
|
||||||
|
showTabView(function() {
|
||||||
|
let contentWindow = win.TabView.getContentWindow();
|
||||||
|
let groupItem = contentWindow.GroupItems.groupItems[0];
|
||||||
|
|
||||||
|
groupItem.getChildren().forEach(function(tabItem) {
|
||||||
|
tabItem.addSubscriber("updated", function onUpdated() {
|
||||||
|
tabItem.removeSubscriber("updated", onUpdated);
|
||||||
|
|
||||||
|
if (--numTabsToUpdate == 0)
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
contentWindow.TabItems.update(tabItem.tab);
|
||||||
|
});
|
||||||
|
}, win);
|
||||||
|
}, function(win) {
|
||||||
|
BrowserOffline.toggleOfflineStatus();
|
||||||
|
ok(Services.io.offline, "It is now offline");
|
||||||
|
|
||||||
|
let originalTab = win.gBrowser.tabs[0];
|
||||||
|
originalTab.linkedBrowser.loadURI("http://www.example.com/foo");
|
||||||
|
win.gBrowser.addTab("http://www.example.com/bar");
|
||||||
|
|
||||||
|
registerCleanupFunction(function () {
|
||||||
|
if (Services.io.offline)
|
||||||
|
BrowserOffline.toggleOfflineStatus();
|
||||||
|
win.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
/* 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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -26,14 +26,13 @@ function onTabViewWindowLoaded(win) {
|
||||||
// procreate!
|
// procreate!
|
||||||
contentWindow.UI.setActive(group);
|
contentWindow.UI.setActive(group);
|
||||||
for (var i=0; i<7; i++) {
|
for (var i=0; i<7; i++) {
|
||||||
win.gBrowser.loadOneTab('about:blank#' + i, {inBackground: true});
|
win.gBrowser.loadOneTab('http://example.com#' + i, {inBackground: true});
|
||||||
}
|
}
|
||||||
let children = group.getChildren();
|
let children = group.getChildren();
|
||||||
|
|
||||||
// Wait until they all update because, once updated, they will notice that they
|
// Wait until they all update because, once updated, they will notice that they
|
||||||
// don't have favicons and this will change their styling at some unknown time.
|
// don't have favicons and this will change their styling at some unknown time.
|
||||||
afterAllTabItemsUpdated(function() {
|
afterAllTabItemsUpdated(function() {
|
||||||
|
|
||||||
ok(!group.shouldStack(group._children.length), "The group should not stack.");
|
ok(!group.shouldStack(group._children.length), "The group should not stack.");
|
||||||
is(expander[0].style.display, "none", "The expander is hidden.");
|
is(expander[0].style.display, "none", "The expander is hidden.");
|
||||||
|
|
||||||
|
|
|
@ -1,161 +0,0 @@
|
||||||
/* 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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -78,13 +78,23 @@ function closeGroupItem(groupItem, callback) {
|
||||||
function afterAllTabItemsUpdated(callback, win) {
|
function afterAllTabItemsUpdated(callback, win) {
|
||||||
win = win || window;
|
win = win || window;
|
||||||
let tabItems = win.document.getElementById("tab-view").contentWindow.TabItems;
|
let tabItems = win.document.getElementById("tab-view").contentWindow.TabItems;
|
||||||
|
let counter = 0;
|
||||||
|
|
||||||
for (let a = 0; a < win.gBrowser.tabs.length; a++) {
|
for (let a = 0; a < win.gBrowser.tabs.length; a++) {
|
||||||
let tabItem = win.gBrowser.tabs[a]._tabViewTabItem;
|
let tabItem = win.gBrowser.tabs[a]._tabViewTabItem;
|
||||||
if (tabItem)
|
if (tabItem) {
|
||||||
tabItems._update(win.gBrowser.tabs[a]);
|
let tab = win.gBrowser.tabs[a];
|
||||||
|
counter++;
|
||||||
|
tabItem.addSubscriber("updated", function onUpdated() {
|
||||||
|
tabItem.removeSubscriber("updated", onUpdated);
|
||||||
|
if (--counter == 0)
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
tabItems.update(tab);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
callback();
|
if (counter == 0)
|
||||||
|
callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------
|
// ---------
|
||||||
|
|
|
@ -1,266 +0,0 @@
|
||||||
/* ***** 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 thumbnailStorage.js.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is
|
|
||||||
* the Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
|
||||||
* 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 ***** */
|
|
||||||
|
|
||||||
// **********
|
|
||||||
// Title: thumbnailStorage.js
|
|
||||||
|
|
||||||
// ##########
|
|
||||||
// Class: ThumbnailStorage
|
|
||||||
// Singleton for persistent storage of thumbnail data.
|
|
||||||
let ThumbnailStorage = {
|
|
||||||
CACHE_CLIENT_IDENTIFIER: "tabview-cache",
|
|
||||||
CACHE_PREFIX: "moz-panorama:",
|
|
||||||
|
|
||||||
// Holds the cache session reference
|
|
||||||
_cacheSession: null,
|
|
||||||
|
|
||||||
// Holds the string input stream reference
|
|
||||||
_stringInputStream: null,
|
|
||||||
|
|
||||||
// Holds the storage stream reference
|
|
||||||
_storageStream: null,
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: toString
|
|
||||||
// Prints [ThumbnailStorage] for debug use.
|
|
||||||
toString: function ThumbnailStorage_toString() {
|
|
||||||
return "[ThumbnailStorage]";
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: init
|
|
||||||
// Should be called when UI is initialized.
|
|
||||||
init: function ThumbnailStorage_init() {
|
|
||||||
// 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: _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>().
|
|
||||||
//
|
|
||||||
// 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 {
|
|
||||||
if (entry)
|
|
||||||
entry.close();
|
|
||||||
|
|
||||||
errorCallback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let key = this.CACHE_PREFIX + url;
|
|
||||||
|
|
||||||
if (options && options.synchronously) {
|
|
||||||
let entry = this._cacheSession.openCacheEntry(key, access, true);
|
|
||||||
let status = Cr.NS_OK;
|
|
||||||
onCacheEntryAvailable(entry, entry.accessGranted, status);
|
|
||||||
} else {
|
|
||||||
let listener = new CacheListener(onCacheEntryAvailable);
|
|
||||||
this._cacheSession.asyncOpenCacheEntry(key, access, listener);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: saveThumbnail
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
function onCacheEntryAvailable(entry) {
|
|
||||||
let outputStream = entry.openOutputStream(0);
|
|
||||||
|
|
||||||
function cleanup() {
|
|
||||||
outputStream.close();
|
|
||||||
entry.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// synchronous mode
|
|
||||||
if (synchronously) {
|
|
||||||
outputStream.write(imageData, imageData.length);
|
|
||||||
cleanup();
|
|
||||||
callback();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// asynchronous mode
|
|
||||||
let inputStream = new self._stringInputStream(imageData, imageData.length);
|
|
||||||
gNetUtil.asyncCopy(inputStream, outputStream, function (result) {
|
|
||||||
cleanup();
|
|
||||||
inputStream.close();
|
|
||||||
callback(Components.isSuccessCode(result) ? "" : "failure");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onCacheEntryUnavailable() {
|
|
||||||
callback("unavailable");
|
|
||||||
}
|
|
||||||
|
|
||||||
this._openCacheEntry(url, Ci.nsICache.ACCESS_WRITE, onCacheEntryAvailable,
|
|
||||||
onCacheEntryUnavailable, options);
|
|
||||||
},
|
|
||||||
|
|
||||||
// ----------
|
|
||||||
// Function: loadThumbnail
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
function onCacheEntryAvailable(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 ? "" : "failure", imageData);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onCacheEntryUnavailable() {
|
|
||||||
callback("unavailable");
|
|
||||||
}
|
|
||||||
|
|
||||||
this._openCacheEntry(url, Ci.nsICache.ACCESS_READ, onCacheEntryAvailable,
|
|
||||||
onCacheEntryUnavailable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ##########
|
|
||||||
// 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 = {
|
|
||||||
// ----------
|
|
||||||
// Function: toString
|
|
||||||
// Prints [CacheListener] for debug use
|
|
||||||
toString: function CacheListener_toString() {
|
|
||||||
return "[CacheListener]";
|
|
||||||
},
|
|
||||||
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsICacheListener]),
|
|
||||||
onCacheEntryAvailable: function CacheListener_onCacheEntryAvailable(
|
|
||||||
entry, access, status) {
|
|
||||||
this.callback(entry, access, status);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
@ -158,15 +158,9 @@ let UI = {
|
||||||
// initialize the direction of the page
|
// initialize the direction of the page
|
||||||
this._initPageDirection();
|
this._initPageDirection();
|
||||||
|
|
||||||
// ___ thumbnail storage
|
|
||||||
ThumbnailStorage.init();
|
|
||||||
|
|
||||||
// ___ storage
|
// ___ storage
|
||||||
Storage.init();
|
Storage.init();
|
||||||
|
|
||||||
// ___ storage policy
|
|
||||||
StoragePolicy.init();
|
|
||||||
|
|
||||||
if (Storage.readWindowBusyState(gWindow))
|
if (Storage.readWindowBusyState(gWindow))
|
||||||
this.storageBusy();
|
this.storageBusy();
|
||||||
|
|
||||||
|
@ -277,7 +271,6 @@ let UI = {
|
||||||
GroupItems.removeHiddenGroups();
|
GroupItems.removeHiddenGroups();
|
||||||
|
|
||||||
TabItems.saveAll();
|
TabItems.saveAll();
|
||||||
TabItems.saveAllThumbnails({synchronously: true});
|
|
||||||
|
|
||||||
self._save();
|
self._save();
|
||||||
}, false);
|
}, false);
|
||||||
|
@ -319,7 +312,6 @@ let UI = {
|
||||||
GroupItems.uninit();
|
GroupItems.uninit();
|
||||||
FavIcons.uninit();
|
FavIcons.uninit();
|
||||||
Storage.uninit();
|
Storage.uninit();
|
||||||
StoragePolicy.uninit();
|
|
||||||
|
|
||||||
this._removeTabActionHandlers();
|
this._removeTabActionHandlers();
|
||||||
this._currentTab = null;
|
this._currentTab = null;
|
||||||
|
@ -717,11 +709,6 @@ let UI = {
|
||||||
if (data == "enter" || data == "exit") {
|
if (data == "enter" || data == "exit") {
|
||||||
Search.hide();
|
Search.hide();
|
||||||
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.
|
||||||
|
|
Загрузка…
Ссылка в новой задаче