2010-06-16 17:19:11 -07:00
|
|
|
/* ***** 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 tabitems.js.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
2010-08-11 15:28:45 -07:00
|
|
|
* the Mozilla Foundation.
|
2010-06-16 17:19:11 -07:00
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
2010-08-11 15:28:45 -07:00
|
|
|
* Ian Gilman <ian@iangilman.com>
|
2010-06-16 17:19:11 -07:00
|
|
|
* Aza Raskin <aza@mozilla.com>
|
|
|
|
* Michael Yoshitaka Erlewine <mitcho@mitcho.com>
|
|
|
|
* Ehsan Akhgari <ehsan@mozilla.com>
|
2010-06-23 02:25:34 -07:00
|
|
|
* Raymond Lee <raymond@appcoast.com>
|
2011-01-23 16:58:12 -08:00
|
|
|
* Tim Taubert <tim.taubert@gmx.de>
|
2010-09-26 23:25:00 -07:00
|
|
|
* Sean Dunn <seanedunn@yahoo.com>
|
2010-06-16 17:19:11 -07:00
|
|
|
*
|
|
|
|
* 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: tabitems.js
|
2010-05-11 17:22:06 -07:00
|
|
|
|
2010-03-29 11:55:13 -07:00
|
|
|
// ##########
|
2010-05-13 17:24:37 -07:00
|
|
|
// Class: TabItem
|
2010-07-27 20:02:51 -07:00
|
|
|
// An <Item> that represents a tab. Also implements the <Subscribable> interface.
|
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// tab - a xul:tab
|
2010-10-09 12:46:18 -07:00
|
|
|
function TabItem(tab, options) {
|
2010-08-10 11:13:10 -07:00
|
|
|
Utils.assert(tab, "tab");
|
2010-07-27 20:02:51 -07:00
|
|
|
|
|
|
|
this.tab = tab;
|
|
|
|
// register this as the tab's tabItem
|
2011-01-11 00:20:08 -08:00
|
|
|
this.tab._tabViewTabItem = this;
|
2010-07-27 20:02:51 -07:00
|
|
|
|
2010-10-09 12:46:18 -07:00
|
|
|
if (!options)
|
|
|
|
options = {};
|
|
|
|
|
2010-07-27 20:02:51 -07:00
|
|
|
// ___ set up div
|
2011-02-11 11:24:51 -08:00
|
|
|
document.body.appendChild(TabItems.fragment().cloneNode(true));
|
|
|
|
|
|
|
|
// The document fragment contains just one Node
|
|
|
|
// As per DOM3 appendChild: it will then be the last child
|
|
|
|
let div = document.body.lastChild;
|
|
|
|
let $div = iQ(div);
|
2010-07-27 20:02:51 -07:00
|
|
|
|
2010-11-30 00:15:51 -08:00
|
|
|
this._cachedImageData = null;
|
2011-09-01 06:17:27 -07:00
|
|
|
this._thumbnailNeedsSaving = false;
|
2010-07-27 20:02:51 -07:00
|
|
|
this.canvasSizeForced = false;
|
2010-11-05 06:32:00 -07:00
|
|
|
this.$thumb = iQ('.thumb', $div);
|
|
|
|
this.$fav = iQ('.favicon', $div);
|
|
|
|
this.$tabTitle = iQ('.tab-title', $div);
|
|
|
|
this.$canvas = iQ('.thumb canvas', $div);
|
|
|
|
this.$cachedThumb = iQ('img.cached-thumb', $div);
|
|
|
|
this.$favImage = iQ('.favicon>img', $div);
|
|
|
|
this.$close = iQ('.close', $div);
|
|
|
|
|
|
|
|
this.tabCanvas = new TabCanvas(this.tab, this.$canvas[0]);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2011-09-01 06:17:27 -07:00
|
|
|
let self = this;
|
|
|
|
|
|
|
|
// when we paint onto the canvas make sure our thumbnail gets saved
|
|
|
|
this.tabCanvas.addSubscriber("painted", function () {
|
|
|
|
self._thumbnailNeedsSaving = true;
|
|
|
|
});
|
|
|
|
|
2010-04-30 17:24:03 -07:00
|
|
|
this.defaultSize = new Point(TabItems.tabWidth, TabItems.tabHeight);
|
2010-11-05 06:32:00 -07:00
|
|
|
this._hidden = false;
|
2010-06-14 15:43:02 -07:00
|
|
|
this.isATabItem = true;
|
2010-06-19 17:45:23 -07:00
|
|
|
this.keepProportional = true;
|
2011-01-05 12:54:34 -08:00
|
|
|
this._hasBeenDrawn = false;
|
|
|
|
this._reconnected = false;
|
2011-09-01 06:17:27 -07:00
|
|
|
this.isDragging = false;
|
2010-09-26 23:25:00 -07:00
|
|
|
this.isStacked = false;
|
2011-02-16 14:05:59 -08:00
|
|
|
this.url = "";
|
2010-05-11 12:03:31 -07:00
|
|
|
|
2011-02-11 11:24:51 -08:00
|
|
|
// Read off the total vertical and horizontal padding on the tab container
|
|
|
|
// and cache this value, as it must be the same for every TabItem.
|
|
|
|
if (Utils.isEmptyObject(TabItems.tabItemPadding)) {
|
|
|
|
TabItems.tabItemPadding.x = parseInt($div.css('padding-left'))
|
|
|
|
+ parseInt($div.css('padding-right'));
|
|
|
|
|
|
|
|
TabItems.tabItemPadding.y = parseInt($div.css('padding-top'))
|
|
|
|
+ parseInt($div.css('padding-bottom'));
|
|
|
|
}
|
|
|
|
|
|
|
|
this.bounds = new Rect(0,0,1,1);
|
2010-08-10 11:39:28 -07:00
|
|
|
|
2010-09-09 22:29:00 -07:00
|
|
|
this._lastTabUpdateTime = Date.now();
|
|
|
|
|
2010-06-15 14:38:55 -07:00
|
|
|
// ___ superclass setup
|
2011-02-11 11:24:51 -08:00
|
|
|
this._init(div);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-15 16:33:58 -07:00
|
|
|
// ___ drag/drop
|
2010-06-15 14:38:55 -07:00
|
|
|
// override dropOptions with custom tabitem methods
|
2010-07-30 02:54:30 -07:00
|
|
|
this.dropOptions.drop = function(e) {
|
2011-07-04 15:23:35 -07:00
|
|
|
let groupItem = drag.info.item.parent;
|
|
|
|
groupItem.add(drag.info.$el);
|
2010-06-22 16:42:06 -07:00
|
|
|
};
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-22 16:42:06 -07:00
|
|
|
this.draggable();
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-15 16:33:58 -07:00
|
|
|
// ___ more div setup
|
2010-06-14 15:43:02 -07:00
|
|
|
$div.mousedown(function(e) {
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!Utils.isRightClick(e))
|
2010-06-14 15:43:02 -07:00
|
|
|
self.lastMouseDownTarget = e.target;
|
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-14 15:43:02 -07:00
|
|
|
$div.mouseup(function(e) {
|
|
|
|
var same = (e.target == self.lastMouseDownTarget);
|
|
|
|
self.lastMouseDownTarget = null;
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!same)
|
2010-06-14 15:43:02 -07:00
|
|
|
return;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-11-23 09:36:28 -08:00
|
|
|
// press close button or middle mouse click
|
2011-01-25 13:13:21 -08:00
|
|
|
if (iQ(e.target).hasClass("close") || Utils.isMiddleClick(e)) {
|
2011-01-27 10:15:18 -08:00
|
|
|
self.closedManually = true;
|
2010-08-10 11:39:28 -07:00
|
|
|
self.close();
|
2010-11-23 09:36:28 -08:00
|
|
|
} else {
|
2010-07-18 08:58:10 -07:00
|
|
|
if (!Items.item(this).isDragging)
|
2010-06-14 15:43:02 -07:00
|
|
|
self.zoomIn();
|
|
|
|
}
|
|
|
|
});
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2011-01-05 12:54:34 -08:00
|
|
|
this.droppable(true);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-18 17:08:45 -07:00
|
|
|
TabItems.register(this);
|
2010-10-13 10:07:55 -07:00
|
|
|
|
2011-01-05 12:54:34 -08:00
|
|
|
// ___ reconnect to data from Storage
|
|
|
|
if (!TabItems.reconnectingPaused())
|
2011-10-27 00:58:52 -07:00
|
|
|
this._reconnect(options);
|
2010-03-29 11:55:13 -07:00
|
|
|
};
|
|
|
|
|
2010-09-08 10:02:08 -07:00
|
|
|
TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
2011-02-26 06:28:00 -08:00
|
|
|
// ----------
|
|
|
|
// Function: toString
|
|
|
|
// Prints [TabItem (tab)] for debug use
|
|
|
|
toString: function TabItem_toString() {
|
|
|
|
return "[TabItem (" + this.tab + ")]";
|
|
|
|
},
|
|
|
|
|
2010-07-27 20:02:51 -07:00
|
|
|
// ----------
|
|
|
|
// Function: forceCanvasSize
|
|
|
|
// Repaints the thumbnail with the given resolution, and forces it
|
|
|
|
// to stay that resolution until unforceCanvasSize is called.
|
2010-09-08 10:02:08 -07:00
|
|
|
forceCanvasSize: function TabItem_forceCanvasSize(w, h) {
|
2010-07-27 20:02:51 -07:00
|
|
|
this.canvasSizeForced = true;
|
2010-11-05 06:32:00 -07:00
|
|
|
this.$canvas[0].width = w;
|
|
|
|
this.$canvas[0].height = h;
|
2010-07-27 20:02:51 -07:00
|
|
|
this.tabCanvas.paint();
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: unforceCanvasSize
|
|
|
|
// Stops holding the thumbnail resolution; allows it to shift to the
|
|
|
|
// size of thumbnail on screen. Note that this call does not nest, unlike
|
|
|
|
// <TabItems.resumePainting>; if you call forceCanvasSize multiple
|
|
|
|
// times, you just need a single unforce to clear them all.
|
2010-09-08 10:02:08 -07:00
|
|
|
unforceCanvasSize: function TabItem_unforceCanvasSize() {
|
2010-07-27 20:02:51 -07:00
|
|
|
this.canvasSizeForced = false;
|
|
|
|
},
|
|
|
|
|
2010-11-30 00:15:51 -08:00
|
|
|
// ----------
|
|
|
|
// Function: isShowingCachedData
|
|
|
|
// Returns a boolean indicates whether the cached data is being displayed or
|
|
|
|
// not.
|
2011-10-10 00:04:27 -07:00
|
|
|
isShowingCachedData: function TabItem_isShowingCachedData() {
|
2010-11-30 00:15:51 -08:00
|
|
|
return (this._cachedImageData != null);
|
|
|
|
},
|
|
|
|
|
2010-07-27 20:02:51 -07:00
|
|
|
// ----------
|
|
|
|
// Function: showCachedData
|
|
|
|
// Shows the cached data i.e. image and title. Note: this method should only
|
|
|
|
// be called at browser startup with the cached data avaliable.
|
2010-11-30 00:15:51 -08:00
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// tabData - the tab data
|
2011-09-01 06:17:27 -07:00
|
|
|
// imageData - the image data
|
|
|
|
showCachedData: function TabItem_showCachedData(tabData, imageData) {
|
|
|
|
this._cachedImageData = imageData;
|
2010-11-05 06:32:00 -07:00
|
|
|
this.$cachedThumb.attr("src", this._cachedImageData).show();
|
2011-09-01 06:17:27 -07:00
|
|
|
this.$canvas.css({opacity: 0});
|
2010-11-05 06:32:00 -07:00
|
|
|
this.$tabTitle.text(tabData.title ? tabData.title : "");
|
2011-09-01 06:17:27 -07:00
|
|
|
|
|
|
|
this._sendToSubscribers("showingCachedData");
|
2010-07-27 20:02:51 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: hideCachedData
|
|
|
|
// Hides the cached data i.e. image and title and show the canvas.
|
2010-09-08 10:02:08 -07:00
|
|
|
hideCachedData: function TabItem_hideCachedData() {
|
2010-11-05 06:32:00 -07:00
|
|
|
this.$cachedThumb.hide();
|
|
|
|
this.$canvas.css({opacity: 1.0});
|
2011-02-16 14:05:59 -08:00
|
|
|
if (this._cachedImageData)
|
2010-11-30 00:15:51 -08:00
|
|
|
this._cachedImageData = null;
|
2010-07-27 20:02:51 -07:00
|
|
|
},
|
|
|
|
|
2010-07-18 08:58:10 -07:00
|
|
|
// ----------
|
2010-07-16 17:27:01 -07:00
|
|
|
// Function: getStorageData
|
2010-07-18 08:58:10 -07:00
|
|
|
// Get data to be used for persistent storage of this object.
|
2011-09-01 06:17:27 -07:00
|
|
|
getStorageData: function TabItem_getStorageData() {
|
2011-09-20 05:21:50 -07:00
|
|
|
let data = {
|
2010-07-22 15:09:36 -07:00
|
|
|
url: this.tab.linkedBrowser.currentURI.spec,
|
2010-07-14 19:40:46 -07:00
|
|
|
groupID: (this.parent ? this.parent.id : 0),
|
2011-09-01 06:17:27 -07:00
|
|
|
title: this.tab.label
|
2010-04-23 17:11:06 -07:00
|
|
|
};
|
2011-09-25 19:46:25 -07:00
|
|
|
if (this.parent && this.parent.getActiveTab() == this)
|
2011-09-20 05:21:50 -07:00
|
|
|
data.active = true;
|
|
|
|
|
|
|
|
return data;
|
2010-04-23 17:11:06 -07:00
|
|
|
},
|
2010-05-21 15:44:15 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-07-16 17:27:01 -07:00
|
|
|
// Function: save
|
2010-07-18 08:58:10 -07:00
|
|
|
// Store persistent for this object.
|
2011-09-01 06:17:27 -07:00
|
|
|
save: function TabItem_save() {
|
|
|
|
try {
|
2011-01-05 12:54:34 -08:00
|
|
|
if (!this.tab || this.tab.parentNode == null || !this._reconnected) // too soon/late to save
|
2010-05-21 15:44:15 -07:00
|
|
|
return;
|
2010-05-24 14:49:03 -07:00
|
|
|
|
2011-09-01 06:17:27 -07:00
|
|
|
let data = this.getStorageData();
|
2010-07-11 17:54:42 -07:00
|
|
|
if (TabItems.storageSanity(data))
|
2010-07-22 15:09:36 -07:00
|
|
|
Storage.saveTab(this.tab, data);
|
2010-07-30 02:54:30 -07:00
|
|
|
} catch(e) {
|
2010-05-21 15:44:15 -07:00
|
|
|
Utils.log("Error in saving tab value: "+e);
|
|
|
|
}
|
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2011-01-05 12:54:34 -08:00
|
|
|
// ----------
|
2011-09-01 06:17:27 -07:00
|
|
|
// Function: loadThumbnail
|
|
|
|
// Loads the tabItems thumbnail.
|
|
|
|
loadThumbnail: function TabItem_loadThumbnail(tabData) {
|
|
|
|
Utils.assert(tabData, "invalid or missing argument <tabData>");
|
2011-02-16 14:05:59 -08:00
|
|
|
|
2011-02-19 10:22:49 -08:00
|
|
|
let self = this;
|
2011-09-01 06:17:27 -07:00
|
|
|
|
|
|
|
function TabItem_loadThumbnail_callback(error, imageData) {
|
2011-07-08 05:50:33 -07:00
|
|
|
// we could have been unlinked while waiting for the thumbnail to load
|
2011-09-01 06:17:27 -07:00
|
|
|
if (error || !imageData || !self.tab)
|
2011-07-08 05:50:33 -07:00
|
|
|
return;
|
|
|
|
|
2011-09-01 06:17:27 -07:00
|
|
|
self._sendToSubscribers("loadedCachedImageData");
|
2011-02-19 10:22:49 -08:00
|
|
|
|
|
|
|
// 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.
|
2011-09-01 06:17:27 -07:00
|
|
|
let currentUrl = self.tab.linkedBrowser.currentURI.spec;
|
|
|
|
if (tabData.url == currentUrl || currentUrl == "about:blank")
|
|
|
|
self.showCachedData(tabData, imageData);
|
|
|
|
}
|
|
|
|
|
|
|
|
ThumbnailStorage.loadThumbnail(tabData.url, TabItem_loadThumbnail_callback);
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: saveThumbnail
|
|
|
|
// Saves the tabItems thumbnail.
|
|
|
|
saveThumbnail: function TabItem_saveThumbnail(options) {
|
|
|
|
if (!this.tabCanvas)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// nothing to do if the thumbnail hasn't changed
|
|
|
|
if (!this._thumbnailNeedsSaving)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// check the storage policy to see if we're allowed to store the thumbnail
|
|
|
|
if (!StoragePolicy.canStoreThumbnailForTab(this.tab)) {
|
|
|
|
this._sendToSubscribers("deniedToSaveImageData");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let url = this.tab.linkedBrowser.currentURI.spec;
|
|
|
|
let delayed = this._saveThumbnailDelayed;
|
|
|
|
let synchronously = (options && options.synchronously);
|
|
|
|
|
|
|
|
// is there a delayed save waiting?
|
|
|
|
if (delayed) {
|
|
|
|
// check if url has changed since last call to saveThumbnail
|
|
|
|
if (!synchronously && url == delayed.url)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// url has changed in the meantime, clear the timeout
|
|
|
|
clearTimeout(delayed.timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
let self = this;
|
|
|
|
|
|
|
|
function callback(error) {
|
|
|
|
if (!error) {
|
|
|
|
self._thumbnailNeedsSaving = false;
|
|
|
|
self._sendToSubscribers("savedCachedImageData");
|
2011-02-19 10:22:49 -08:00
|
|
|
}
|
2011-09-01 06:17:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
function doSaveThumbnail() {
|
|
|
|
self._saveThumbnailDelayed = null;
|
|
|
|
|
|
|
|
// we could have been unlinked in the meantime
|
|
|
|
if (!self.tabCanvas)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let imageData = self.tabCanvas.toImageData();
|
|
|
|
ThumbnailStorage.saveThumbnail(url, imageData, callback, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (synchronously) {
|
|
|
|
doSaveThumbnail();
|
|
|
|
} else {
|
|
|
|
let timeout = setTimeout(doSaveThumbnail, 2000);
|
|
|
|
this._saveThumbnailDelayed = {url: url, timeout: timeout};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: _reconnect
|
|
|
|
// Load the reciever's persistent data from storage. If there is none,
|
|
|
|
// treats it as a new tab.
|
2011-10-27 00:58:52 -07:00
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// options - an object with additional parameters, see below
|
|
|
|
//
|
|
|
|
// Possible options:
|
|
|
|
// groupItemId - if the tab doesn't have any data associated with it and
|
|
|
|
// groupItemId is available, add the tab to that group.
|
|
|
|
_reconnect: function TabItem__reconnect(options) {
|
2011-09-01 06:17:27 -07:00
|
|
|
Utils.assertThrow(!this._reconnected, "shouldn't already be reconnected");
|
|
|
|
Utils.assertThrow(this.tab, "should have a xul:tab");
|
|
|
|
|
|
|
|
let tabData = Storage.getTabData(this.tab);
|
2011-10-27 00:58:52 -07:00
|
|
|
let groupItem;
|
2011-09-01 06:17:27 -07:00
|
|
|
|
2011-01-05 12:54:34 -08:00
|
|
|
if (tabData && TabItems.storageSanity(tabData)) {
|
2011-09-01 06:17:27 -07:00
|
|
|
this.loadThumbnail(tabData);
|
|
|
|
|
2011-09-20 05:21:50 -07:00
|
|
|
if (this.parent)
|
|
|
|
this.parent.remove(this, {immediately: true});
|
2011-01-05 12:54:34 -08:00
|
|
|
|
2011-10-27 00:58:52 -07:00
|
|
|
if (tabData.groupID)
|
2011-07-04 15:23:35 -07:00
|
|
|
groupItem = GroupItems.groupItem(tabData.groupID);
|
2011-10-27 00:58:52 -07:00
|
|
|
else
|
2011-07-04 15:23:35 -07:00
|
|
|
groupItem = new GroupItem([], {immediately: true, bounds: tabData.bounds});
|
|
|
|
|
|
|
|
if (groupItem) {
|
2011-09-20 05:21:50 -07:00
|
|
|
groupItem.add(this, {immediately: true});
|
|
|
|
|
|
|
|
// restore the active tab for each group between browser sessions
|
|
|
|
if (tabData.active)
|
|
|
|
groupItem.setActiveTab(this);
|
2011-07-04 15:23:35 -07:00
|
|
|
|
|
|
|
// if it matches the selected tab or no active tab and the browser
|
|
|
|
// tab is hidden, the active group item would be set.
|
2011-09-20 05:21:50 -07:00
|
|
|
if (this.tab == gBrowser.selectedTab ||
|
|
|
|
(!GroupItems.getActiveGroupItem() && !this.tab.hidden))
|
|
|
|
UI.setActive(this.parent);
|
2011-01-05 12:54:34 -08:00
|
|
|
}
|
|
|
|
} else {
|
2011-10-27 00:58:52 -07:00
|
|
|
if (options && options.groupItemId)
|
|
|
|
groupItem = GroupItems.groupItem(options.groupItemId);
|
|
|
|
|
|
|
|
if (groupItem) {
|
|
|
|
groupItem.add(this, {immediately: true});
|
|
|
|
} else {
|
|
|
|
// create tab group by double click is handled in UI_init().
|
|
|
|
GroupItems.newTab(this, {immediately: true});
|
|
|
|
}
|
2011-01-05 12:54:34 -08:00
|
|
|
}
|
|
|
|
|
2011-09-20 05:21:50 -07:00
|
|
|
this._reconnected = true;
|
|
|
|
this.save();
|
|
|
|
this._sendToSubscribers("reconnected");
|
2011-01-05 12:54:34 -08:00
|
|
|
},
|
2011-07-04 15:23:35 -07:00
|
|
|
|
2010-11-05 06:32:00 -07:00
|
|
|
// ----------
|
|
|
|
// Function: setHidden
|
|
|
|
// Hide/unhide this item
|
|
|
|
setHidden: function TabItem_setHidden(val) {
|
|
|
|
if (val)
|
|
|
|
this.addClass("tabHidden");
|
|
|
|
else
|
|
|
|
this.removeClass("tabHidden");
|
|
|
|
this._hidden = val;
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: getHidden
|
|
|
|
// Return hide state of item
|
|
|
|
getHidden: function TabItem_getHidden() {
|
|
|
|
return this._hidden;
|
|
|
|
},
|
|
|
|
|
2010-07-18 08:58:10 -07:00
|
|
|
// ----------
|
2010-07-16 17:27:01 -07:00
|
|
|
// Function: setBounds
|
2010-07-18 08:58:10 -07:00
|
|
|
// Moves this item to the specified location and size.
|
|
|
|
//
|
|
|
|
// Parameters:
|
2010-07-16 17:27:01 -07:00
|
|
|
// rect - a <Rect> giving the new bounds
|
|
|
|
// immediately - true if it should not animate; default false
|
|
|
|
// options - an object with additional parameters, see below
|
2010-07-18 08:58:10 -07:00
|
|
|
//
|
|
|
|
// Possible options:
|
2010-07-16 17:27:01 -07:00
|
|
|
// force - true to always update the DOM even if the bounds haven't changed; default false
|
2010-09-26 23:25:00 -07:00
|
|
|
setBounds: function TabItem_setBounds(inRect, immediately, options) {
|
2011-08-09 19:34:46 -07:00
|
|
|
Utils.assert(Utils.isRect(inRect), 'TabItem.setBounds: rect is not a real rectangle!');
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-11 17:54:42 -07:00
|
|
|
if (!options)
|
2010-06-14 15:43:02 -07:00
|
|
|
options = {};
|
2010-06-15 16:08:21 -07:00
|
|
|
|
2010-09-26 23:25:00 -07:00
|
|
|
// force the input size to be valid
|
|
|
|
let validSize = TabItems.calcValidSize(
|
|
|
|
new Point(inRect.width, inRect.height),
|
|
|
|
{hideTitle: (this.isStacked || options.hideTitle === true)});
|
|
|
|
let rect = new Rect(inRect.left, inRect.top,
|
|
|
|
validSize.x, validSize.y);
|
2011-01-12 07:20:28 -08:00
|
|
|
|
2011-02-07 18:00:07 -08:00
|
|
|
var css = {};
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2011-02-07 18:00:07 -08:00
|
|
|
if (rect.left != this.bounds.left || options.force)
|
|
|
|
css.left = rect.left;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2011-02-07 18:00:07 -08:00
|
|
|
if (rect.top != this.bounds.top || options.force)
|
|
|
|
css.top = rect.top;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2011-02-07 18:00:07 -08:00
|
|
|
if (rect.width != this.bounds.width || options.force) {
|
2011-02-11 11:24:51 -08:00
|
|
|
css.width = rect.width - TabItems.tabItemPadding.x;
|
2011-04-07 13:00:06 -07:00
|
|
|
css.fontSize = TabItems.getFontSizeFromWidth(rect.width);
|
2011-02-07 18:00:07 -08:00
|
|
|
css.fontSize += 'px';
|
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2011-02-07 18:00:07 -08:00
|
|
|
if (rect.height != this.bounds.height || options.force) {
|
2011-04-07 13:00:06 -07:00
|
|
|
css.height = rect.height - TabItems.tabItemPadding.y;
|
2011-02-07 18:00:07 -08:00
|
|
|
if (!this.isStacked)
|
2011-04-07 13:00:06 -07:00
|
|
|
css.height -= TabItems.fontSizeRange.max;
|
2011-02-07 18:00:07 -08:00
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2011-02-07 18:00:07 -08:00
|
|
|
if (Utils.isEmptyObject(css))
|
|
|
|
return;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2011-02-07 18:00:07 -08:00
|
|
|
this.bounds.copy(rect);
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2011-02-07 18:00:07 -08:00
|
|
|
// If this is a brand new tab don't animate it in from
|
|
|
|
// a random location (i.e., from [0,0]). Instead, just
|
|
|
|
// have it appear where it should be.
|
|
|
|
if (immediately || (!this._hasBeenDrawn)) {
|
|
|
|
this.$container.css(css);
|
|
|
|
} else {
|
|
|
|
TabItems.pausePainting();
|
|
|
|
this.$container.animate(css, {
|
|
|
|
duration: 200,
|
|
|
|
easing: "tabviewBounce",
|
|
|
|
complete: function() {
|
|
|
|
TabItems.resumePainting();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2011-02-14 09:21:37 -08:00
|
|
|
if (css.fontSize && !(this.parent && this.parent.isStacked())) {
|
2011-02-07 18:00:07 -08:00
|
|
|
if (css.fontSize < TabItems.fontSizeRange.min)
|
|
|
|
immediately ? this.$tabTitle.hide() : this.$tabTitle.fadeOut();
|
|
|
|
else
|
|
|
|
immediately ? this.$tabTitle.show() : this.$tabTitle.fadeIn();
|
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2011-02-07 18:00:07 -08:00
|
|
|
if (css.width) {
|
|
|
|
TabItems.update(this.tab);
|
2010-07-22 21:35:07 -07:00
|
|
|
|
2011-02-07 18:00:07 -08:00
|
|
|
let widthRange, proportion;
|
2010-07-22 21:35:07 -07:00
|
|
|
|
2011-02-14 09:21:37 -08:00
|
|
|
if (this.parent && this.parent.isStacked()) {
|
2011-02-07 18:00:07 -08:00
|
|
|
if (UI.rtl) {
|
|
|
|
this.$fav.css({top:0, right:0});
|
2010-07-22 21:35:07 -07:00
|
|
|
} else {
|
2011-02-07 18:00:07 -08:00
|
|
|
this.$fav.css({top:0, left:0});
|
2010-06-25 15:15:51 -07:00
|
|
|
}
|
2011-02-07 18:00:07 -08:00
|
|
|
widthRange = new Range(70, 90);
|
|
|
|
proportion = widthRange.proportion(css.width); // between 0 and 1
|
|
|
|
} else {
|
|
|
|
if (UI.rtl) {
|
|
|
|
this.$fav.css({top:4, right:2});
|
|
|
|
} else {
|
|
|
|
this.$fav.css({top:4, left:4});
|
|
|
|
}
|
|
|
|
widthRange = new Range(40, 45);
|
|
|
|
proportion = widthRange.proportion(css.width); // between 0 and 1
|
2010-07-18 08:58:10 -07:00
|
|
|
}
|
2010-03-29 16:08:48 -07:00
|
|
|
|
2011-02-07 18:00:07 -08:00
|
|
|
if (proportion <= .1)
|
|
|
|
this.$close.hide();
|
|
|
|
else
|
|
|
|
this.$close.show().css({opacity:proportion});
|
|
|
|
|
|
|
|
var pad = 1 + 5 * proportion;
|
|
|
|
var alphaRange = new Range(0.1,0.2);
|
|
|
|
this.$fav.css({
|
|
|
|
"-moz-padding-start": pad + "px",
|
|
|
|
"-moz-padding-end": pad + 2 + "px",
|
|
|
|
"padding-top": pad + "px",
|
|
|
|
"padding-bottom": pad + "px",
|
|
|
|
"border-color": "rgba(0,0,0,"+ alphaRange.scale(proportion) +")",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
this._hasBeenDrawn = true;
|
2010-04-12 17:20:35 -07:00
|
|
|
|
2011-01-21 07:21:30 -08:00
|
|
|
UI.clearShouldResizeItems();
|
|
|
|
|
2010-06-22 20:45:51 -07:00
|
|
|
rect = this.getBounds(); // ensure that it's a <Rect>
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2011-08-09 19:34:46 -07:00
|
|
|
Utils.assert(Utils.isRect(this.bounds), 'TabItem.setBounds: this.bounds is not a real rectangle!');
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-07-22 15:09:36 -07:00
|
|
|
if (!this.parent && this.tab.parentNode != null)
|
2010-06-19 12:05:36 -07:00
|
|
|
this.setTrenches(rect);
|
2010-05-21 15:44:15 -07:00
|
|
|
|
|
|
|
this.save();
|
2010-04-01 17:20:59 -07:00
|
|
|
},
|
2010-04-02 17:33:06 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-07-16 17:27:01 -07:00
|
|
|
// Function: setZ
|
2010-07-18 08:58:10 -07:00
|
|
|
// Sets the z-index for this item.
|
2010-09-08 10:02:08 -07:00
|
|
|
setZ: function TabItem_setZ(value) {
|
2010-07-07 14:12:04 -07:00
|
|
|
this.zIndex = value;
|
2010-11-05 06:32:00 -07:00
|
|
|
this.$container.css({zIndex: value});
|
2010-04-02 17:33:06 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-03-29 16:08:48 -07:00
|
|
|
// ----------
|
2010-07-16 17:27:01 -07:00
|
|
|
// Function: close
|
|
|
|
// Closes this item (actually closes the tab associated with it, which automatically
|
|
|
|
// closes the item.
|
2011-02-22 13:02:29 -08:00
|
|
|
// Parameters:
|
|
|
|
// groupClose - true if this method is called by group close action.
|
2010-11-14 19:12:10 -08:00
|
|
|
// Returns true if this tab is removed.
|
2011-02-22 13:02:29 -08:00
|
|
|
close: function TabItem_close(groupClose) {
|
|
|
|
// When the last tab is closed, put a new tab into closing tab's group. If
|
|
|
|
// closing tab doesn't belong to a group and no empty group, create a new
|
|
|
|
// one for the new tab.
|
|
|
|
if (!groupClose && gBrowser.tabs.length == 1) {
|
2011-08-15 07:03:39 -07:00
|
|
|
let group = this.tab._tabViewTabItem.parent;
|
2011-06-16 22:05:30 -07:00
|
|
|
group.newTab(null, { closedLastTab: true });
|
2011-02-22 13:02:29 -08:00
|
|
|
}
|
2011-08-15 07:03:39 -07:00
|
|
|
|
2010-11-14 19:12:10 -08:00
|
|
|
// when "TabClose" event is fired, the browser tab is about to close and our
|
|
|
|
// item "close" is fired before the browser tab actually get closed.
|
|
|
|
// Therefore, we need "tabRemoved" event below.
|
2010-07-23 14:26:17 -07:00
|
|
|
gBrowser.removeTab(this.tab);
|
2011-08-15 07:03:39 -07:00
|
|
|
let tabClosed = !this.tab;
|
|
|
|
|
|
|
|
if (tabClosed)
|
2010-11-14 19:12:10 -08:00
|
|
|
this._sendToSubscribers("tabRemoved");
|
2010-05-21 15:44:15 -07:00
|
|
|
|
|
|
|
// No need to explicitly delete the tab data, becasue sessionstore data
|
|
|
|
// associated with the tab will automatically go away
|
2011-08-15 07:03:39 -07:00
|
|
|
return tabClosed;
|
2010-03-29 16:08:48 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-04-29 00:42:37 -07:00
|
|
|
// ----------
|
2010-07-16 17:27:01 -07:00
|
|
|
// Function: addClass
|
2010-07-18 08:58:10 -07:00
|
|
|
// Adds the specified CSS class to this item's container DOM element.
|
2010-09-08 10:02:08 -07:00
|
|
|
addClass: function TabItem_addClass(className) {
|
2010-11-05 06:32:00 -07:00
|
|
|
this.$container.addClass(className);
|
2010-04-29 00:42:37 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-04-29 00:42:37 -07:00
|
|
|
// ----------
|
2010-07-16 17:27:01 -07:00
|
|
|
// Function: removeClass
|
2010-07-18 08:58:10 -07:00
|
|
|
// Removes the specified CSS class from this item's container DOM element.
|
2010-09-08 10:02:08 -07:00
|
|
|
removeClass: function TabItem_removeClass(className) {
|
2010-11-05 06:32:00 -07:00
|
|
|
this.$container.removeClass(className);
|
2010-04-29 00:42:37 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-07-16 17:27:01 -07:00
|
|
|
// Function: makeActive
|
|
|
|
// Updates this item to visually indicate that it's active.
|
2010-09-08 10:02:08 -07:00
|
|
|
makeActive: function TabItem_makeActive() {
|
2010-11-05 06:32:00 -07:00
|
|
|
this.$container.addClass("focus");
|
2010-09-17 08:24:39 -07:00
|
|
|
|
|
|
|
if (this.parent)
|
|
|
|
this.parent.setActiveTab(this);
|
2010-05-12 19:56:35 -07:00
|
|
|
},
|
|
|
|
|
2010-07-18 08:58:10 -07:00
|
|
|
// ----------
|
2010-07-16 17:27:01 -07:00
|
|
|
// Function: makeDeactive
|
|
|
|
// Updates this item to visually indicate that it's not active.
|
2010-09-08 10:02:08 -07:00
|
|
|
makeDeactive: function TabItem_makeDeactive() {
|
2010-11-05 06:32:00 -07:00
|
|
|
this.$container.removeClass("focus");
|
2010-05-12 19:56:35 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-12 19:56:35 -07:00
|
|
|
// ----------
|
2010-06-14 15:43:02 -07:00
|
|
|
// Function: zoomIn
|
|
|
|
// Allows you to select the tab and zoom in on it, thereby bringing you
|
|
|
|
// to the tab in Firefox to interact with.
|
2010-08-09 00:01:30 -07:00
|
|
|
// Parameters:
|
|
|
|
// isNewBlankTab - boolean indicates whether it is a newly opened blank tab.
|
2010-09-08 10:02:08 -07:00
|
|
|
zoomIn: function TabItem_zoomIn(isNewBlankTab) {
|
2010-09-10 07:40:27 -07:00
|
|
|
// don't allow zoom in if its group is hidden
|
|
|
|
if (this.parent && this.parent.hidden)
|
|
|
|
return;
|
|
|
|
|
2011-02-13 18:38:16 -08:00
|
|
|
let self = this;
|
|
|
|
let $tabEl = this.$container;
|
|
|
|
let $canvas = this.$canvas;
|
|
|
|
|
2011-09-29 21:17:00 -07:00
|
|
|
Search.hide();
|
2011-08-25 03:53:40 -07:00
|
|
|
|
2011-04-22 13:05:11 -07:00
|
|
|
UI.setActive(this);
|
2011-02-22 12:34:49 -08:00
|
|
|
TabItems._update(this.tab, {force: true});
|
2011-01-31 16:41:05 -08:00
|
|
|
|
2011-02-13 18:38:16 -08:00
|
|
|
// Zoom in!
|
|
|
|
let tab = this.tab;
|
2010-06-23 02:25:34 -07:00
|
|
|
|
2011-02-13 18:38:16 -08:00
|
|
|
function onZoomDone() {
|
|
|
|
$canvas.css({ '-moz-transform': null });
|
|
|
|
$tabEl.removeClass("front");
|
2011-01-31 16:41:05 -08:00
|
|
|
|
2011-02-13 18:38:16 -08:00
|
|
|
UI.goToTab(tab);
|
2010-06-23 02:25:34 -07:00
|
|
|
|
2011-02-13 18:38:16 -08:00
|
|
|
// tab might not be selected because hideTabView() is invoked after
|
|
|
|
// UI.goToTab() so we need to setup everything for the gBrowser.selectedTab
|
|
|
|
if (tab != gBrowser.selectedTab) {
|
|
|
|
UI.onTabSelect(gBrowser.selectedTab);
|
|
|
|
} else {
|
|
|
|
if (isNewBlankTab)
|
|
|
|
gWindow.gURLBar.focus();
|
2010-06-14 15:43:02 -07:00
|
|
|
}
|
2011-02-13 18:38:16 -08:00
|
|
|
if (self.parent && self.parent.expanded)
|
|
|
|
self.parent.collapse();
|
2011-06-16 22:05:30 -07:00
|
|
|
|
|
|
|
self._sendToSubscribers("zoomedIn");
|
2011-02-13 18:38:16 -08:00
|
|
|
}
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2011-02-13 18:38:16 -08:00
|
|
|
let animateZoom = gPrefBranch.getBoolPref("animate_zoom");
|
|
|
|
if (animateZoom) {
|
|
|
|
let transform = this.getZoomTransform();
|
|
|
|
TabItems.pausePainting();
|
2011-01-31 16:41:05 -08:00
|
|
|
|
2011-02-28 21:28:00 -08:00
|
|
|
if (this.parent && this.parent.expanded)
|
|
|
|
$tabEl.removeClass("stack-trayed");
|
2011-02-13 18:38:16 -08:00
|
|
|
$tabEl.addClass("front");
|
|
|
|
$canvas
|
2011-01-31 16:41:05 -08:00
|
|
|
.css({ '-moz-transform-origin': transform.transformOrigin })
|
|
|
|
.animate({ '-moz-transform': transform.transform }, {
|
2010-06-14 15:43:02 -07:00
|
|
|
duration: 230,
|
|
|
|
easing: 'fast',
|
2010-10-09 12:10:25 -07:00
|
|
|
complete: function() {
|
|
|
|
onZoomDone();
|
2011-01-15 12:11:50 -08:00
|
|
|
|
|
|
|
setTimeout(function() {
|
|
|
|
TabItems.resumePainting();
|
|
|
|
}, 0);
|
2010-10-09 12:10:25 -07:00
|
|
|
}
|
2010-06-14 15:43:02 -07:00
|
|
|
});
|
2011-02-13 18:38:16 -08:00
|
|
|
} else {
|
|
|
|
setTimeout(onZoomDone, 0);
|
2010-07-18 08:58:10 -07:00
|
|
|
}
|
2010-06-14 15:43:02 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-06-14 15:43:02 -07:00
|
|
|
// ----------
|
|
|
|
// Function: zoomOut
|
2010-07-29 12:37:25 -07:00
|
|
|
// Handles the zoom down animation after returning to TabView.
|
2010-06-14 15:43:02 -07:00
|
|
|
// It is expected that this routine will be called from the chrome thread
|
2010-07-18 08:58:10 -07:00
|
|
|
//
|
|
|
|
// Parameters:
|
2010-06-14 15:43:02 -07:00
|
|
|
// complete - a function to call after the zoom down animation
|
2010-09-08 10:02:08 -07:00
|
|
|
zoomOut: function TabItem_zoomOut(complete) {
|
2011-01-31 16:41:05 -08:00
|
|
|
let $tab = this.$container, $canvas = this.$canvas;
|
2010-06-14 15:43:02 -07:00
|
|
|
var self = this;
|
2010-10-09 12:10:25 -07:00
|
|
|
|
|
|
|
let onZoomDone = function onZoomDone() {
|
2011-01-31 16:41:05 -08:00
|
|
|
$tab.removeClass("front");
|
|
|
|
$canvas.css("-moz-transform", null);
|
2010-10-09 12:10:25 -07:00
|
|
|
|
|
|
|
if (typeof complete == "function")
|
|
|
|
complete();
|
|
|
|
};
|
2011-01-31 16:41:05 -08:00
|
|
|
|
2011-04-22 13:05:11 -07:00
|
|
|
UI.setActive(this);
|
2011-02-22 12:34:49 -08:00
|
|
|
TabItems._update(this.tab, {force: true});
|
2011-01-31 16:41:05 -08:00
|
|
|
|
|
|
|
$tab.addClass("front");
|
|
|
|
|
2010-10-09 12:10:25 -07:00
|
|
|
let animateZoom = gPrefBranch.getBoolPref("animate_zoom");
|
|
|
|
if (animateZoom) {
|
2011-01-31 16:41:05 -08:00
|
|
|
// The scaleCheat of 2 here is a clever way to speed up the zoom-out
|
|
|
|
// code. See getZoomTransform() below.
|
|
|
|
let transform = this.getZoomTransform(2);
|
2011-02-05 01:06:41 -08:00
|
|
|
TabItems.pausePainting();
|
|
|
|
|
2011-01-31 16:41:05 -08:00
|
|
|
$canvas.css({
|
|
|
|
'-moz-transform': transform.transform,
|
|
|
|
'-moz-transform-origin': transform.transformOrigin
|
|
|
|
});
|
|
|
|
|
|
|
|
$canvas.animate({ "-moz-transform": "scale(1.0)" }, {
|
2010-10-09 12:10:25 -07:00
|
|
|
duration: 300,
|
|
|
|
easing: 'cubic-bezier', // note that this is legal easing, even without parameters
|
|
|
|
complete: function() {
|
|
|
|
TabItems.resumePainting();
|
|
|
|
onZoomDone();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
onZoomDone();
|
|
|
|
}
|
2010-06-14 15:43:02 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-11-27 14:28:18 -08:00
|
|
|
// ----------
|
2011-01-31 16:41:05 -08:00
|
|
|
// Function: getZoomTransform
|
|
|
|
// Returns the transform function which represents the maximum bounds of the
|
|
|
|
// tab thumbnail in the zoom animation.
|
|
|
|
getZoomTransform: function TabItem_getZoomTransform(scaleCheat) {
|
|
|
|
// Taking the bounds of the container (as opposed to the canvas) makes us
|
|
|
|
// immune to any transformations applied to the canvas.
|
|
|
|
let { left, top, width, height, right, bottom } = this.$container.bounds();
|
|
|
|
|
|
|
|
let { innerWidth: windowWidth, innerHeight: windowHeight } = window;
|
|
|
|
|
2010-11-27 14:28:18 -08:00
|
|
|
// The scaleCheat is a clever way to speed up the zoom-in code.
|
|
|
|
// Because image scaling is slowest on big images, we cheat and stop
|
|
|
|
// the image at scaled-down size and placed accordingly. Because the
|
|
|
|
// animation is fast, you can't see the difference but it feels a lot
|
|
|
|
// zippier. The only trick is choosing the right animation function so
|
2011-01-31 16:41:05 -08:00
|
|
|
// that you don't see a change in percieved animation speed from frame #1
|
|
|
|
// (the tab) to frame #2 (the half-size image) to frame #3 (the first frame
|
|
|
|
// of real animation). Choosing an animation that starts fast is key.
|
|
|
|
|
2010-11-27 14:28:18 -08:00
|
|
|
if (!scaleCheat)
|
|
|
|
scaleCheat = 1.7;
|
|
|
|
|
2011-01-31 16:41:05 -08:00
|
|
|
let zoomWidth = width + (window.innerWidth - width) / scaleCheat;
|
|
|
|
let zoomScaleFactor = zoomWidth / width;
|
2010-06-14 15:43:02 -07:00
|
|
|
|
2011-01-31 16:41:05 -08:00
|
|
|
let zoomHeight = height * zoomScaleFactor;
|
|
|
|
let zoomTop = top * (1 - 1/scaleCheat);
|
|
|
|
let zoomLeft = left * (1 - 1/scaleCheat);
|
2010-09-26 23:25:00 -07:00
|
|
|
|
2011-01-31 16:41:05 -08:00
|
|
|
let xOrigin = (left - zoomLeft) / ((left - zoomLeft) + (zoomLeft + zoomWidth - right)) * 100;
|
|
|
|
let yOrigin = (top - zoomTop) / ((top - zoomTop) + (zoomTop + zoomHeight - bottom)) * 100;
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2011-01-31 16:41:05 -08:00
|
|
|
return {
|
|
|
|
transformOrigin: xOrigin + "% " + yOrigin + "%",
|
|
|
|
transform: "scale(" + zoomScaleFactor + ")"
|
|
|
|
};
|
2010-03-29 16:08:48 -07:00
|
|
|
}
|
|
|
|
});
|
2010-03-29 11:55:13 -07:00
|
|
|
|
|
|
|
// ##########
|
2010-05-13 17:24:37 -07:00
|
|
|
// Class: TabItems
|
|
|
|
// Singleton for managing <TabItem>s
|
2010-09-08 10:02:08 -07:00
|
|
|
let TabItems = {
|
2010-07-18 08:58:10 -07:00
|
|
|
minTabWidth: 40,
|
2010-03-31 17:24:16 -07:00
|
|
|
tabWidth: 160,
|
2010-07-18 08:58:10 -07:00
|
|
|
tabHeight: 120,
|
2010-09-26 23:25:00 -07:00
|
|
|
tabAspect: 0, // set in init
|
|
|
|
invTabAspect: 0, // set in init
|
2010-03-31 17:24:16 -07:00
|
|
|
fontSize: 9,
|
2010-09-26 23:25:00 -07:00
|
|
|
fontSizeRange: new Range(8,15),
|
2011-02-11 11:24:51 -08:00
|
|
|
_fragment: null,
|
2010-07-20 09:46:01 -07:00
|
|
|
items: [],
|
2010-07-27 20:02:51 -07:00
|
|
|
paintingPaused: 0,
|
2011-02-14 09:21:37 -08:00
|
|
|
_tabsWaitingForUpdate: null,
|
2011-01-12 16:54:49 -08:00
|
|
|
_heartbeat: null, // see explanation at startHeartbeat() below
|
2011-02-16 14:05:59 -08:00
|
|
|
_heartbeatTiming: 200, // milliseconds between calls
|
|
|
|
_maxTimeForUpdating: 200, // milliseconds that consecutive updates can take
|
2010-08-04 16:42:36 -07:00
|
|
|
_lastUpdateTime: Date.now(),
|
2010-08-11 21:36:58 -07:00
|
|
|
_eventListeners: [],
|
2010-12-17 12:22:17 -08:00
|
|
|
_pauseUpdateForTest: false,
|
2010-09-09 22:29:00 -07:00
|
|
|
tempCanvas: null,
|
2011-01-05 12:54:34 -08:00
|
|
|
_reconnectingPaused: false,
|
2011-02-11 11:24:51 -08:00
|
|
|
tabItemPadding: {},
|
2010-07-27 20:02:51 -07:00
|
|
|
|
2011-02-26 06:28:00 -08:00
|
|
|
// ----------
|
|
|
|
// Function: toString
|
|
|
|
// Prints [TabItems count=count] for debug use
|
|
|
|
toString: function TabItems_toString() {
|
|
|
|
return "[TabItems count=" + this.items.length + "]";
|
|
|
|
},
|
|
|
|
|
2010-07-27 20:02:51 -07:00
|
|
|
// ----------
|
|
|
|
// Function: init
|
|
|
|
// Set up the necessary tracking to maintain the <TabItems>s.
|
2010-09-08 10:02:08 -07:00
|
|
|
init: function TabItems_init() {
|
2010-08-10 11:13:10 -07:00
|
|
|
Utils.assert(window.AllTabs, "AllTabs must be initialized first");
|
2010-11-30 00:15:51 -08:00
|
|
|
let self = this;
|
2011-01-12 07:20:28 -08:00
|
|
|
|
2011-02-14 09:21:37 -08:00
|
|
|
// Set up tab priority queue
|
|
|
|
this._tabsWaitingForUpdate = new TabPriorityQueue();
|
2011-01-12 07:20:28 -08:00
|
|
|
this.minTabHeight = this.minTabWidth * this.tabHeight / this.tabWidth;
|
2010-09-26 23:25:00 -07:00
|
|
|
this.tabAspect = this.tabHeight / this.tabWidth;
|
|
|
|
this.invTabAspect = 1 / this.tabAspect;
|
2010-07-27 20:02:51 -07:00
|
|
|
|
2011-01-16 19:19:24 -08:00
|
|
|
let $canvas = iQ("<canvas>")
|
2011-01-23 16:58:12 -08:00
|
|
|
.attr('moz-opaque', '');
|
2010-09-09 22:29:00 -07:00
|
|
|
$canvas.appendTo(iQ("body"));
|
|
|
|
$canvas.hide();
|
|
|
|
this.tempCanvas = $canvas[0];
|
|
|
|
// 150 pixels is an empirical size, below which FF's drawWindow()
|
|
|
|
// algorithm breaks down
|
|
|
|
this.tempCanvas.width = 150;
|
2011-02-02 17:53:13 -08:00
|
|
|
this.tempCanvas.height = 112;
|
2010-09-09 22:29:00 -07:00
|
|
|
|
2010-07-27 20:02:51 -07:00
|
|
|
// When a tab is opened, create the TabItem
|
2011-08-21 01:41:17 -07:00
|
|
|
this._eventListeners.open = function (event) {
|
|
|
|
let tab = event.target;
|
2010-07-28 14:20:41 -07:00
|
|
|
|
2011-08-21 01:41:17 -07:00
|
|
|
if (!tab.pinned)
|
|
|
|
self.link(tab);
|
2010-08-11 21:36:58 -07:00
|
|
|
}
|
2010-07-27 20:02:51 -07:00
|
|
|
// When a tab's content is loaded, show the canvas and hide the cached data
|
|
|
|
// if necessary.
|
2011-08-21 01:41:17 -07:00
|
|
|
this._eventListeners.attrModified = function (event) {
|
|
|
|
let tab = event.target;
|
2010-07-28 14:20:41 -07:00
|
|
|
|
2011-08-21 01:41:17 -07:00
|
|
|
if (!tab.pinned)
|
|
|
|
self.update(tab);
|
2010-08-11 21:36:58 -07:00
|
|
|
}
|
2010-07-27 20:02:51 -07:00
|
|
|
// When a tab is closed, unlink.
|
2011-08-21 01:41:17 -07:00
|
|
|
this._eventListeners.close = function (event) {
|
|
|
|
let tab = event.target;
|
2010-07-28 14:20:41 -07:00
|
|
|
|
2011-03-25 15:40:00 -07:00
|
|
|
// XXX bug #635975 - don't unlink the tab if the dom window is closing.
|
2011-08-21 01:41:17 -07:00
|
|
|
if (!tab.pinned && !UI.isDOMWindowClosing)
|
2011-03-25 15:40:00 -07:00
|
|
|
self.unlink(tab);
|
2010-08-11 21:36:58 -07:00
|
|
|
}
|
|
|
|
for (let name in this._eventListeners) {
|
|
|
|
AllTabs.register(name, this._eventListeners[name]);
|
|
|
|
}
|
2010-07-27 20:02:51 -07:00
|
|
|
|
2011-10-27 00:58:52 -07:00
|
|
|
let activeGroupItem = GroupItems.getActiveGroupItem();
|
|
|
|
let activeGroupItemId = activeGroupItem ? activeGroupItem.id : null;
|
2010-07-27 20:02:51 -07:00
|
|
|
// For each tab, create the link.
|
2011-08-21 01:41:17 -07:00
|
|
|
AllTabs.tabs.forEach(function (tab) {
|
|
|
|
if (tab.pinned)
|
2010-07-28 14:20:41 -07:00
|
|
|
return;
|
|
|
|
|
2011-10-27 00:58:52 -07:00
|
|
|
let options = {immediately: true};
|
|
|
|
// if tab is visible in the tabstrip and doesn't have any data stored in
|
|
|
|
// the session store (see TabItem__reconnect), it implies that it is a
|
|
|
|
// new tab which is created before Panorama is initialized. Therefore,
|
|
|
|
// passing the active group id to the link() method for setting it up.
|
|
|
|
if (!tab.hidden && activeGroupItemId)
|
|
|
|
options.groupItemId = activeGroupItemId;
|
|
|
|
self.link(tab, options);
|
2010-07-29 17:23:49 -07:00
|
|
|
self.update(tab);
|
2010-07-27 20:02:51 -07:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2010-08-11 21:36:58 -07:00
|
|
|
// ----------
|
|
|
|
// Function: uninit
|
2010-09-08 10:02:08 -07:00
|
|
|
uninit: function TabItems_uninit() {
|
2010-08-11 21:36:58 -07:00
|
|
|
for (let name in this._eventListeners) {
|
|
|
|
AllTabs.unregister(name, this._eventListeners[name]);
|
|
|
|
}
|
2010-08-11 23:39:12 -07:00
|
|
|
this.items.forEach(function(tabItem) {
|
2010-08-11 23:46:16 -07:00
|
|
|
for (let x in tabItem) {
|
2010-08-11 23:39:12 -07:00
|
|
|
if (typeof tabItem[x] == "object")
|
|
|
|
tabItem[x] = null;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2010-08-11 21:36:58 -07:00
|
|
|
this.items = null;
|
|
|
|
this._eventListeners = null;
|
|
|
|
this._lastUpdateTime = null;
|
2011-02-14 09:21:37 -08:00
|
|
|
this._tabsWaitingForUpdate.clear();
|
2010-08-11 21:36:58 -07:00
|
|
|
},
|
|
|
|
|
2011-02-11 11:24:51 -08:00
|
|
|
// ----------
|
|
|
|
// Function: fragment
|
|
|
|
// Return a DocumentFragment which has a single <div> child. This child node
|
|
|
|
// will act as a template for all TabItem containers.
|
|
|
|
// The first call of this function caches the DocumentFragment in _fragment.
|
|
|
|
fragment: function TabItems_fragment() {
|
|
|
|
if (this._fragment)
|
|
|
|
return this._fragment;
|
|
|
|
|
|
|
|
let div = document.createElement("div");
|
|
|
|
div.classList.add("tab");
|
|
|
|
div.innerHTML = "<div class='thumb'>" +
|
|
|
|
"<img class='cached-thumb' style='display:none'/><canvas moz-opaque/></div>" +
|
|
|
|
"<div class='favicon'><img/></div>" +
|
|
|
|
"<span class='tab-title'> </span>" +
|
2011-07-04 15:23:35 -07:00
|
|
|
"<div class='close'></div>";
|
2011-02-11 11:24:51 -08:00
|
|
|
this._fragment = document.createDocumentFragment();
|
|
|
|
this._fragment.appendChild(div);
|
|
|
|
|
|
|
|
return this._fragment;
|
|
|
|
},
|
|
|
|
|
2011-02-16 14:05:59 -08:00
|
|
|
// ----------
|
|
|
|
// Function: isComplete
|
|
|
|
// Return whether the xul:tab has fully loaded.
|
2011-10-10 00:04:27 -07:00
|
|
|
isComplete: function TabItems_isComplete(tab) {
|
2011-02-16 14:05:59 -08:00
|
|
|
// 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");
|
|
|
|
return (
|
|
|
|
tab.linkedBrowser.contentDocument.readyState == 'complete' &&
|
|
|
|
!(tab.linkedBrowser.contentDocument.URL == 'about:blank' &&
|
|
|
|
tab._tabViewTabItem.url != 'about:blank')
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2010-07-27 20:02:51 -07:00
|
|
|
// ----------
|
2010-07-29 14:45:08 -07:00
|
|
|
// Function: update
|
|
|
|
// Takes in a xul:tab.
|
2010-09-08 10:02:08 -07:00
|
|
|
update: function TabItems_update(tab) {
|
2010-07-27 20:02:51 -07:00
|
|
|
try {
|
2010-08-10 11:13:10 -07:00
|
|
|
Utils.assertThrow(tab, "tab");
|
2010-09-10 02:50:14 -07:00
|
|
|
Utils.assertThrow(!tab.pinned, "shouldn't be an app tab");
|
2011-01-11 00:20:08 -08:00
|
|
|
Utils.assertThrow(tab._tabViewTabItem, "should already be linked");
|
2010-08-08 20:52:26 -07:00
|
|
|
|
2010-08-05 10:39:33 -07:00
|
|
|
let shouldDefer = (
|
2010-08-08 20:52:26 -07:00
|
|
|
this.isPaintingPaused() ||
|
2011-02-14 09:21:37 -08:00
|
|
|
this._tabsWaitingForUpdate.hasItems() ||
|
2010-08-05 10:39:33 -07:00
|
|
|
Date.now() - this._lastUpdateTime < this._heartbeatTiming
|
|
|
|
);
|
|
|
|
|
2010-11-16 14:35:00 -08:00
|
|
|
if (shouldDefer) {
|
2011-02-14 09:21:37 -08:00
|
|
|
this._tabsWaitingForUpdate.push(tab);
|
2010-09-09 22:29:00 -07:00
|
|
|
this.startHeartbeat();
|
2010-08-08 20:52:26 -07:00
|
|
|
} else
|
2010-08-04 16:42:36 -07:00
|
|
|
this._update(tab);
|
|
|
|
} catch(e) {
|
|
|
|
Utils.log(e);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: _update
|
|
|
|
// Takes in a xul:tab.
|
2011-02-22 12:34:49 -08:00
|
|
|
//
|
|
|
|
// Parameters:
|
|
|
|
// tab - a xul tab to update
|
|
|
|
// options - an object with additional parameters, see below
|
|
|
|
//
|
|
|
|
// Possible options:
|
|
|
|
// force - true to always update the tab item even if it's incomplete
|
|
|
|
_update: function TabItems__update(tab, options) {
|
2010-08-04 16:42:36 -07:00
|
|
|
try {
|
2010-12-17 12:22:17 -08:00
|
|
|
if (this._pauseUpdateForTest)
|
|
|
|
return;
|
|
|
|
|
2010-08-10 11:13:10 -07:00
|
|
|
Utils.assertThrow(tab, "tab");
|
2010-07-30 02:54:30 -07:00
|
|
|
|
2010-08-05 10:39:33 -07:00
|
|
|
// ___ get the TabItem
|
2011-01-11 00:20:08 -08:00
|
|
|
Utils.assertThrow(tab._tabViewTabItem, "must already be linked");
|
|
|
|
let tabItem = tab._tabViewTabItem;
|
2010-07-30 02:54:30 -07:00
|
|
|
|
2011-02-16 14:05:59 -08:00
|
|
|
// Even if the page hasn't loaded, display the favicon and title
|
|
|
|
|
2010-08-05 10:39:33 -07:00
|
|
|
// ___ icon
|
2011-05-03 03:23:44 -07:00
|
|
|
if (UI.shouldLoadFavIcon(tab.linkedBrowser)) {
|
|
|
|
let iconUrl = UI.getFavIconUrlForTab(tab);
|
|
|
|
|
2011-04-19 11:59:18 -07:00
|
|
|
if (tabItem.$favImage[0].src != iconUrl)
|
2010-11-05 06:32:00 -07:00
|
|
|
tabItem.$favImage[0].src = iconUrl;
|
2011-01-13 10:22:47 -08:00
|
|
|
|
2010-11-05 06:32:00 -07:00
|
|
|
iQ(tabItem.$fav[0]).show();
|
2011-01-13 10:22:47 -08:00
|
|
|
} else {
|
2010-11-05 06:32:00 -07:00
|
|
|
if (tabItem.$favImage[0].hasAttribute("src"))
|
|
|
|
tabItem.$favImage[0].removeAttribute("src");
|
|
|
|
iQ(tabItem.$fav[0]).hide();
|
2011-01-13 10:22:47 -08:00
|
|
|
}
|
2010-07-29 14:45:08 -07:00
|
|
|
|
2011-02-16 14:05:59 -08:00
|
|
|
// ___ label
|
|
|
|
let label = tab.label;
|
|
|
|
let $name = tabItem.$tabTitle;
|
|
|
|
if ($name.text() != label)
|
|
|
|
$name.text(label);
|
|
|
|
|
|
|
|
// ___ remove from waiting list now that we have no other
|
|
|
|
// early returns
|
|
|
|
this._tabsWaitingForUpdate.remove(tab);
|
|
|
|
|
2010-08-05 10:39:33 -07:00
|
|
|
// ___ URL
|
2010-07-29 14:45:08 -07:00
|
|
|
let tabUrl = tab.linkedBrowser.currentURI.spec;
|
|
|
|
if (tabUrl != tabItem.url) {
|
2010-08-05 10:39:33 -07:00
|
|
|
let oldURL = tabItem.url;
|
2010-07-29 14:45:08 -07:00
|
|
|
tabItem.url = tabUrl;
|
2010-08-10 15:36:56 -07:00
|
|
|
tabItem.save();
|
2010-07-29 14:45:08 -07:00
|
|
|
}
|
2010-07-27 20:02:51 -07:00
|
|
|
|
2011-02-16 14:05:59 -08:00
|
|
|
// ___ Make sure the tab is complete and ready for updating.
|
2011-02-22 12:34:49 -08:00
|
|
|
if (!this.isComplete(tab) && (!options || !options.force)) {
|
2011-02-16 14:05:59 -08:00
|
|
|
// If it's incomplete, stick it on the end of the queue
|
|
|
|
this._tabsWaitingForUpdate.push(tab);
|
|
|
|
return;
|
|
|
|
}
|
2010-07-27 20:02:51 -07:00
|
|
|
|
2010-08-05 10:39:33 -07:00
|
|
|
// ___ thumbnail
|
2010-11-05 06:32:00 -07:00
|
|
|
let $canvas = tabItem.$canvas;
|
2010-07-29 14:45:08 -07:00
|
|
|
if (!tabItem.canvasSizeForced) {
|
2010-08-05 10:39:33 -07:00
|
|
|
let w = $canvas.width();
|
|
|
|
let h = $canvas.height();
|
2010-11-05 06:32:00 -07:00
|
|
|
if (w != tabItem.$canvas[0].width || h != tabItem.$canvas[0].height) {
|
|
|
|
tabItem.$canvas[0].width = w;
|
|
|
|
tabItem.$canvas[0].height = h;
|
2010-07-27 20:02:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-09 22:29:00 -07:00
|
|
|
this._lastUpdateTime = Date.now();
|
|
|
|
tabItem._lastTabUpdateTime = this._lastUpdateTime;
|
|
|
|
|
2010-07-29 14:45:08 -07:00
|
|
|
tabItem.tabCanvas.paint();
|
2011-09-01 06:17:27 -07:00
|
|
|
tabItem.saveThumbnail();
|
2010-07-27 20:02:51 -07:00
|
|
|
|
2010-08-05 10:39:33 -07:00
|
|
|
// ___ cache
|
2011-02-16 14:05:59 -08:00
|
|
|
if (tabItem.isShowingCachedData())
|
2010-07-29 14:45:08 -07:00
|
|
|
tabItem.hideCachedData();
|
2011-02-14 09:21:37 -08:00
|
|
|
|
|
|
|
// ___ notify subscribers that a full update has completed.
|
|
|
|
tabItem._sendToSubscribers("updated");
|
2010-07-29 14:45:08 -07:00
|
|
|
} catch(e) {
|
|
|
|
Utils.log(e);
|
|
|
|
}
|
2010-07-27 20:02:51 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: link
|
2010-09-10 02:50:14 -07:00
|
|
|
// Takes in a xul:tab, creates a TabItem for it and adds it to the scene.
|
2010-10-09 12:46:18 -07:00
|
|
|
link: function TabItems_link(tab, options) {
|
2010-07-29 14:45:08 -07:00
|
|
|
try {
|
2010-08-10 11:13:10 -07:00
|
|
|
Utils.assertThrow(tab, "tab");
|
2010-09-10 02:50:14 -07:00
|
|
|
Utils.assertThrow(!tab.pinned, "shouldn't be an app tab");
|
2011-01-11 00:20:08 -08:00
|
|
|
Utils.assertThrow(!tab._tabViewTabItem, "shouldn't already be linked");
|
|
|
|
new TabItem(tab, options); // sets tab._tabViewTabItem to itself
|
2010-07-29 14:45:08 -07:00
|
|
|
} catch(e) {
|
|
|
|
Utils.log(e);
|
|
|
|
}
|
2010-07-27 20:02:51 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: unlink
|
2010-09-10 02:50:14 -07:00
|
|
|
// Takes in a xul:tab and destroys the TabItem associated with it.
|
2010-09-08 10:02:08 -07:00
|
|
|
unlink: function TabItems_unlink(tab) {
|
2010-07-29 14:45:08 -07:00
|
|
|
try {
|
2010-08-10 11:13:10 -07:00
|
|
|
Utils.assertThrow(tab, "tab");
|
2011-01-11 00:20:08 -08:00
|
|
|
Utils.assertThrow(tab._tabViewTabItem, "should already be linked");
|
2010-09-12 20:45:02 -07:00
|
|
|
// note that it's ok to unlink an app tab; see .handleTabUnpin
|
2010-07-27 20:02:51 -07:00
|
|
|
|
2011-01-11 00:20:08 -08:00
|
|
|
this.unregister(tab._tabViewTabItem);
|
|
|
|
tab._tabViewTabItem._sendToSubscribers("close");
|
2010-11-05 06:32:00 -07:00
|
|
|
tab._tabViewTabItem.$container.remove();
|
2011-01-11 00:20:08 -08:00
|
|
|
tab._tabViewTabItem.removeTrenches();
|
|
|
|
Items.unsquish(null, tab._tabViewTabItem);
|
2010-07-27 20:02:51 -07:00
|
|
|
|
2011-06-15 21:41:14 -07:00
|
|
|
tab._tabViewTabItem.tab = null;
|
|
|
|
tab._tabViewTabItem.tabCanvas.tab = null;
|
|
|
|
tab._tabViewTabItem.tabCanvas = null;
|
2011-01-11 00:20:08 -08:00
|
|
|
tab._tabViewTabItem = null;
|
2010-08-16 16:56:28 -07:00
|
|
|
Storage.saveTab(tab, null);
|
2010-08-04 16:42:36 -07:00
|
|
|
|
2011-02-14 09:21:37 -08:00
|
|
|
this._tabsWaitingForUpdate.remove(tab);
|
2010-07-29 14:45:08 -07:00
|
|
|
} catch(e) {
|
|
|
|
Utils.log(e);
|
2010-07-27 20:02:51 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2010-09-12 20:45:02 -07:00
|
|
|
// ----------
|
|
|
|
// when a tab becomes pinned, destroy its TabItem
|
|
|
|
handleTabPin: function TabItems_handleTabPin(xulTab) {
|
|
|
|
this.unlink(xulTab);
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// when a tab becomes unpinned, create a TabItem for it
|
|
|
|
handleTabUnpin: function TabItems_handleTabUnpin(xulTab) {
|
|
|
|
this.link(xulTab);
|
|
|
|
this.update(xulTab);
|
|
|
|
},
|
|
|
|
|
2010-08-04 16:42:36 -07:00
|
|
|
// ----------
|
2010-09-09 22:29:00 -07:00
|
|
|
// Function: startHeartbeat
|
|
|
|
// Start a new heartbeat if there isn't one already started.
|
|
|
|
// The heartbeat is a chain of setTimeout calls that allows us to spread
|
|
|
|
// out update calls over a period of time.
|
2011-01-12 16:54:49 -08:00
|
|
|
// _heartbeat is used to make sure that we don't add multiple
|
2010-09-09 22:29:00 -07:00
|
|
|
// setTimeout chains.
|
|
|
|
startHeartbeat: function TabItems_startHeartbeat() {
|
2011-01-12 16:54:49 -08:00
|
|
|
if (!this._heartbeat) {
|
2010-09-09 22:29:00 -07:00
|
|
|
let self = this;
|
2011-01-12 16:54:49 -08:00
|
|
|
this._heartbeat = setTimeout(function() {
|
2010-09-09 22:29:00 -07:00
|
|
|
self._checkHeartbeat();
|
|
|
|
}, this._heartbeatTiming);
|
|
|
|
}
|
|
|
|
},
|
2011-01-12 16:54:49 -08:00
|
|
|
|
2010-09-09 22:29:00 -07:00
|
|
|
// ----------
|
|
|
|
// Function: _checkHeartbeat
|
|
|
|
// This periodically checks for tabs waiting to be updated, and calls
|
|
|
|
// _update on them.
|
|
|
|
// Should only be called by startHeartbeat and resumePainting.
|
|
|
|
_checkHeartbeat: function TabItems__checkHeartbeat() {
|
2011-01-12 16:54:49 -08:00
|
|
|
this._heartbeat = null;
|
|
|
|
|
2011-04-19 09:54:02 -07:00
|
|
|
if (this.isPaintingPaused())
|
2010-08-04 16:42:36 -07:00
|
|
|
return;
|
2010-08-08 20:52:26 -07:00
|
|
|
|
2011-04-19 09:54:02 -07:00
|
|
|
// restart the heartbeat to update all waiting tabs once the UI becomes idle
|
|
|
|
if (!UI.isIdle()) {
|
|
|
|
this.startHeartbeat();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-02-16 14:05:59 -08:00
|
|
|
let accumTime = 0;
|
|
|
|
let items = this._tabsWaitingForUpdate.getItems();
|
|
|
|
// Do as many updates as we can fit into a "perceived" amount
|
|
|
|
// of time, which is tunable.
|
|
|
|
while (accumTime < this._maxTimeForUpdating && items.length) {
|
|
|
|
let updateBegin = Date.now();
|
|
|
|
this._update(items.pop());
|
|
|
|
let updateEnd = Date.now();
|
|
|
|
|
|
|
|
// Maintain a simple average of time for each tabitem update
|
|
|
|
// We can use this as a base by which to delay things like
|
|
|
|
// tab zooming, so there aren't any hitches.
|
|
|
|
let deltaTime = updateEnd - updateBegin;
|
|
|
|
accumTime += deltaTime;
|
|
|
|
}
|
2010-08-08 20:52:26 -07:00
|
|
|
|
2011-02-14 09:21:37 -08:00
|
|
|
if (this._tabsWaitingForUpdate.hasItems())
|
2010-09-09 22:29:00 -07:00
|
|
|
this.startHeartbeat();
|
2010-07-27 20:02:51 -07:00
|
|
|
},
|
|
|
|
|
2011-02-16 14:05:59 -08:00
|
|
|
// ----------
|
|
|
|
// Function: pausePainting
|
|
|
|
// Tells TabItems to stop updating thumbnails (so you can do
|
|
|
|
// animations without thumbnail paints causing stutters).
|
|
|
|
// pausePainting can be called multiple times, but every call to
|
|
|
|
// pausePainting needs to be mirrored with a call to <resumePainting>.
|
|
|
|
pausePainting: function TabItems_pausePainting() {
|
|
|
|
this.paintingPaused++;
|
|
|
|
if (this._heartbeat) {
|
|
|
|
clearTimeout(this._heartbeat);
|
|
|
|
this._heartbeat = null;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: resumePainting
|
|
|
|
// Undoes a call to <pausePainting>. For instance, if you called
|
|
|
|
// pausePainting three times in a row, you'll need to call resumePainting
|
|
|
|
// three times before TabItems will start updating thumbnails again.
|
|
|
|
resumePainting: function TabItems_resumePainting() {
|
|
|
|
this.paintingPaused--;
|
|
|
|
Utils.assert(this.paintingPaused > -1, "paintingPaused should not go below zero");
|
|
|
|
if (!this.isPaintingPaused())
|
|
|
|
this.startHeartbeat();
|
|
|
|
},
|
2010-09-09 22:29:00 -07:00
|
|
|
|
2010-08-04 16:42:36 -07:00
|
|
|
// ----------
|
2010-07-27 20:02:51 -07:00
|
|
|
// Function: isPaintingPaused
|
|
|
|
// Returns a boolean indicating whether painting
|
|
|
|
// is paused or not.
|
2010-09-08 10:02:08 -07:00
|
|
|
isPaintingPaused: function TabItems_isPaintingPaused() {
|
2010-08-04 16:42:36 -07:00
|
|
|
return this.paintingPaused > 0;
|
2010-07-27 20:02:51 -07:00
|
|
|
},
|
|
|
|
|
2011-01-05 12:54:34 -08:00
|
|
|
// ----------
|
|
|
|
// Function: pauseReconnecting
|
|
|
|
// Don't reconnect any new tabs until resume is called.
|
|
|
|
pauseReconnecting: function TabItems_pauseReconnecting() {
|
|
|
|
Utils.assertThrow(!this._reconnectingPaused, "shouldn't already be paused");
|
|
|
|
|
|
|
|
this._reconnectingPaused = true;
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: resumeReconnecting
|
|
|
|
// Reconnect all of the tabs that were created since we paused.
|
|
|
|
resumeReconnecting: function TabItems_resumeReconnecting() {
|
|
|
|
Utils.assertThrow(this._reconnectingPaused, "should already be paused");
|
|
|
|
|
|
|
|
this._reconnectingPaused = false;
|
|
|
|
this.items.forEach(function(item) {
|
|
|
|
if (!item._reconnected)
|
|
|
|
item._reconnect();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: reconnectingPaused
|
|
|
|
// Returns true if reconnecting is paused.
|
|
|
|
reconnectingPaused: function TabItems_reconnectingPaused() {
|
|
|
|
return this._reconnectingPaused;
|
|
|
|
},
|
|
|
|
|
2010-07-18 08:58:10 -07:00
|
|
|
// ----------
|
2010-07-16 17:27:01 -07:00
|
|
|
// Function: register
|
2010-07-18 08:58:10 -07:00
|
|
|
// Adds the given <TabItem> to the master list.
|
2010-09-08 10:02:08 -07:00
|
|
|
register: function TabItems_register(item) {
|
2010-08-10 11:13:10 -07:00
|
|
|
Utils.assert(item && item.isAnItem, 'item must be a TabItem');
|
|
|
|
Utils.assert(this.items.indexOf(item) == -1, 'only register once per item');
|
2010-05-18 17:08:45 -07:00
|
|
|
this.items.push(item);
|
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
|
|
|
// ----------
|
2010-07-16 17:27:01 -07:00
|
|
|
// Function: unregister
|
2010-07-18 08:58:10 -07:00
|
|
|
// Removes the given <TabItem> from the master list.
|
2010-09-08 10:02:08 -07:00
|
|
|
unregister: function TabItems_unregister(item) {
|
2010-07-13 17:10:53 -07:00
|
|
|
var index = this.items.indexOf(item);
|
2010-07-11 17:54:42 -07:00
|
|
|
if (index != -1)
|
2010-07-18 08:58:10 -07:00
|
|
|
this.items.splice(index, 1);
|
2010-05-18 17:08:45 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-04-23 17:11:06 -07:00
|
|
|
// ----------
|
2010-07-16 17:27:01 -07:00
|
|
|
// Function: getItems
|
|
|
|
// Returns a copy of the master array of <TabItem>s.
|
2010-09-08 10:02:08 -07:00
|
|
|
getItems: function TabItems_getItems() {
|
2010-05-18 17:08:45 -07:00
|
|
|
return Utils.copy(this.items);
|
2010-04-23 17:11:06 -07:00
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-24 14:49:03 -07:00
|
|
|
// ----------
|
2010-07-16 17:27:01 -07:00
|
|
|
// Function: saveAll
|
2010-07-18 08:58:10 -07:00
|
|
|
// Saves all open <TabItem>s.
|
2011-09-01 06:17:27 -07:00
|
|
|
saveAll: function TabItems_saveAll() {
|
|
|
|
let tabItems = this.getItems();
|
|
|
|
|
|
|
|
tabItems.forEach(function TabItems_saveAll_forEach(tabItem) {
|
|
|
|
tabItem.save();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: saveAllThumbnails
|
|
|
|
// Saves thumbnails of all open <TabItem>s.
|
|
|
|
saveAllThumbnails: function TabItems_saveAllThumbnails(options) {
|
|
|
|
let tabItems = this.getItems();
|
|
|
|
|
|
|
|
tabItems.forEach(function TabItems_saveAllThumbnails_forEach(tabItem) {
|
|
|
|
tabItem.saveThumbnail(options);
|
2010-05-24 14:49:03 -07:00
|
|
|
});
|
|
|
|
},
|
2010-07-18 08:58:10 -07:00
|
|
|
|
2010-05-11 12:03:31 -07:00
|
|
|
// ----------
|
2010-07-16 17:27:01 -07:00
|
|
|
// Function: storageSanity
|
|
|
|
// Checks the specified data (as returned by TabItem.getStorageData or loaded from storage)
|
|
|
|
// and returns true if it looks valid.
|
2011-07-04 15:23:35 -07:00
|
|
|
// TODO: this is a stub, please implement
|
2010-09-08 10:02:08 -07:00
|
|
|
storageSanity: function TabItems_storageSanity(data) {
|
2011-07-04 15:23:35 -07:00
|
|
|
return true;
|
2011-01-12 07:20:28 -08:00
|
|
|
},
|
2011-04-07 13:00:06 -07:00
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: getFontSizeFromWidth
|
|
|
|
// Private method that returns the fontsize to use given the tab's width
|
|
|
|
getFontSizeFromWidth: function TabItem_getFontSizeFromWidth(width) {
|
|
|
|
let widthRange = new Range(0, TabItems.tabWidth);
|
|
|
|
let proportion = widthRange.proportion(width - TabItems.tabItemPadding.x, true);
|
|
|
|
// proportion is in [0,1]
|
|
|
|
return TabItems.fontSizeRange.scale(proportion);
|
|
|
|
},
|
|
|
|
|
2010-09-26 23:25:00 -07:00
|
|
|
// ----------
|
|
|
|
// Function: _getWidthForHeight
|
|
|
|
// Private method that returns the tabitem width given a height.
|
2011-04-07 13:00:06 -07:00
|
|
|
_getWidthForHeight: function TabItems__getWidthForHeight(height) {
|
|
|
|
return height * TabItems.invTabAspect;
|
2010-09-26 23:25:00 -07:00
|
|
|
},
|
2011-01-12 07:20:28 -08:00
|
|
|
|
|
|
|
// ----------
|
2010-09-26 23:25:00 -07:00
|
|
|
// Function: _getHeightForWidth
|
|
|
|
// Private method that returns the tabitem height given a width.
|
2011-04-07 13:00:06 -07:00
|
|
|
_getHeightForWidth: function TabItems__getHeightForWidth(width) {
|
|
|
|
return width * TabItems.tabAspect;
|
2010-09-26 23:25:00 -07:00
|
|
|
},
|
2011-04-07 13:00:06 -07:00
|
|
|
|
2010-09-26 23:25:00 -07:00
|
|
|
// ----------
|
|
|
|
// Function: calcValidSize
|
|
|
|
// Pass in a desired size, and receive a size based on proper title
|
|
|
|
// size and aspect ratio.
|
|
|
|
calcValidSize: function TabItems_calcValidSize(size, options) {
|
|
|
|
Utils.assert(Utils.isPoint(size), 'input is a Point');
|
|
|
|
|
2011-04-07 13:00:06 -07:00
|
|
|
let width = Math.max(TabItems.minTabWidth, size.x);
|
|
|
|
let showTitle = !options || !options.hideTitle;
|
|
|
|
let titleSize = showTitle ? TabItems.fontSizeRange.max : 0;
|
|
|
|
let height = Math.max(TabItems.minTabHeight, size.y - titleSize);
|
|
|
|
let retSize = new Point(width, height);
|
|
|
|
|
|
|
|
if (size.x > -1)
|
|
|
|
retSize.y = this._getHeightForWidth(width);
|
|
|
|
if (size.y > -1)
|
|
|
|
retSize.x = this._getWidthForHeight(height);
|
|
|
|
|
|
|
|
if (size.x > -1 && size.y > -1) {
|
|
|
|
if (retSize.x < size.x)
|
|
|
|
retSize.y = this._getHeightForWidth(retSize.x);
|
|
|
|
else
|
|
|
|
retSize.x = this._getWidthForHeight(retSize.y);
|
2010-09-26 23:25:00 -07:00
|
|
|
}
|
2011-04-07 13:00:06 -07:00
|
|
|
|
|
|
|
if (showTitle)
|
|
|
|
retSize.y += titleSize;
|
|
|
|
|
2010-09-26 23:25:00 -07:00
|
|
|
return retSize;
|
2010-07-18 08:58:10 -07:00
|
|
|
}
|
2010-03-29 11:55:13 -07:00
|
|
|
};
|
2010-07-27 20:02:51 -07:00
|
|
|
|
2011-02-14 09:21:37 -08:00
|
|
|
// ##########
|
|
|
|
// Class: TabPriorityQueue
|
|
|
|
// Container that returns tab items in a priority order
|
|
|
|
// Current implementation assigns tab to either a high priority
|
|
|
|
// or low priority queue, and toggles which queue items are popped
|
|
|
|
// from. This guarantees that high priority items which are constantly
|
|
|
|
// being added will not eclipse changes for lower priority items.
|
|
|
|
function TabPriorityQueue() {
|
|
|
|
};
|
|
|
|
|
|
|
|
TabPriorityQueue.prototype = {
|
|
|
|
_low: [], // low priority queue
|
|
|
|
_high: [], // high priority queue
|
|
|
|
|
2011-02-26 06:28:00 -08:00
|
|
|
// ----------
|
|
|
|
// Function: toString
|
|
|
|
// Prints [TabPriorityQueue count=count] for debug use
|
|
|
|
toString: function TabPriorityQueue_toString() {
|
|
|
|
return "[TabPriorityQueue count=" + (this._low.length + this._high.length) + "]";
|
|
|
|
},
|
|
|
|
|
2011-02-14 09:21:37 -08:00
|
|
|
// ----------
|
|
|
|
// Function: clear
|
|
|
|
// Empty the update queue
|
|
|
|
clear: function TabPriorityQueue_clear() {
|
|
|
|
this._low = [];
|
|
|
|
this._high = [];
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: hasItems
|
|
|
|
// Return whether pending items exist
|
|
|
|
hasItems: function TabPriorityQueue_hasItems() {
|
|
|
|
return (this._low.length > 0) || (this._high.length > 0);
|
|
|
|
},
|
|
|
|
|
2011-02-16 14:05:59 -08:00
|
|
|
// ----------
|
|
|
|
// Function: getItems
|
|
|
|
// Returns all queued items, ordered from low to high priority
|
|
|
|
getItems: function TabPriorityQueue_getItems() {
|
|
|
|
return this._low.concat(this._high);
|
|
|
|
},
|
|
|
|
|
2011-02-14 09:21:37 -08:00
|
|
|
// ----------
|
|
|
|
// Function: push
|
|
|
|
// Add an item to be prioritized
|
|
|
|
push: function TabPriorityQueue_push(tab) {
|
|
|
|
// Push onto correct priority queue.
|
|
|
|
// It's only low priority if it's in a stack, and isn't the top,
|
|
|
|
// and the stack isn't expanded.
|
|
|
|
// If it already exists in the destination queue,
|
|
|
|
// leave it. If it exists in a different queue, remove it first and push
|
|
|
|
// onto new queue.
|
|
|
|
let item = tab._tabViewTabItem;
|
|
|
|
if (item.parent && (item.parent.isStacked() &&
|
|
|
|
!item.parent.isTopOfStack(item) &&
|
|
|
|
!item.parent.expanded)) {
|
|
|
|
let idx = this._high.indexOf(tab);
|
|
|
|
if (idx != -1) {
|
|
|
|
this._high.splice(idx, 1);
|
|
|
|
this._low.unshift(tab);
|
|
|
|
} else if (this._low.indexOf(tab) == -1)
|
|
|
|
this._low.unshift(tab);
|
|
|
|
} else {
|
|
|
|
let idx = this._low.indexOf(tab);
|
|
|
|
if (idx != -1) {
|
|
|
|
this._low.splice(idx, 1);
|
|
|
|
this._high.unshift(tab);
|
|
|
|
} else if (this._high.indexOf(tab) == -1)
|
|
|
|
this._high.unshift(tab);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: pop
|
|
|
|
// Remove and return the next item in priority order
|
|
|
|
pop: function TabPriorityQueue_pop() {
|
|
|
|
let ret = null;
|
|
|
|
if (this._high.length)
|
|
|
|
ret = this._high.pop();
|
|
|
|
else if (this._low.length)
|
|
|
|
ret = this._low.pop();
|
|
|
|
return ret;
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: peek
|
|
|
|
// Return the next item in priority order, without removing it
|
|
|
|
peek: function TabPriorityQueue_peek() {
|
|
|
|
let ret = null;
|
|
|
|
if (this._high.length)
|
|
|
|
ret = this._high[this._high.length-1];
|
|
|
|
else if (this._low.length)
|
|
|
|
ret = this._low[this._low.length-1];
|
|
|
|
return ret;
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: remove
|
|
|
|
// Remove the passed item
|
|
|
|
remove: function TabPriorityQueue_remove(tab) {
|
|
|
|
let index = this._high.indexOf(tab);
|
|
|
|
if (index != -1)
|
|
|
|
this._high.splice(index, 1);
|
|
|
|
else {
|
|
|
|
index = this._low.indexOf(tab);
|
|
|
|
if (index != -1)
|
|
|
|
this._low.splice(index, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-07-27 20:02:51 -07:00
|
|
|
// ##########
|
|
|
|
// Class: TabCanvas
|
|
|
|
// Takes care of the actual canvas for the tab thumbnail
|
|
|
|
// Does not need to be accessed from outside of tabitems.js
|
2010-09-08 10:02:08 -07:00
|
|
|
function TabCanvas(tab, canvas) {
|
2011-02-11 11:24:51 -08:00
|
|
|
this.tab = tab;
|
|
|
|
this.canvas = canvas;
|
2010-07-27 20:02:51 -07:00
|
|
|
};
|
|
|
|
|
2011-09-01 06:17:27 -07:00
|
|
|
TabCanvas.prototype = Utils.extend(new Subscribable(), {
|
2011-02-26 06:28:00 -08:00
|
|
|
// ----------
|
|
|
|
// Function: toString
|
|
|
|
// Prints [TabCanvas (tab)] for debug use
|
|
|
|
toString: function TabCanvas_toString() {
|
|
|
|
return "[TabCanvas (" + this.tab + ")]";
|
|
|
|
},
|
|
|
|
|
2010-07-27 20:02:51 -07:00
|
|
|
// ----------
|
|
|
|
// Function: paint
|
2010-09-08 10:02:08 -07:00
|
|
|
paint: function TabCanvas_paint(evt) {
|
2010-07-27 20:02:51 -07:00
|
|
|
var w = this.canvas.width;
|
|
|
|
var h = this.canvas.height;
|
|
|
|
if (!w || !h)
|
|
|
|
return;
|
|
|
|
|
2011-02-02 17:53:13 -08:00
|
|
|
if (!this.tab.linkedBrowser.contentWindow) {
|
|
|
|
Utils.log('no tab.linkedBrowser.contentWindow in TabCanvas.paint()');
|
2010-07-27 20:02:51 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-02-02 17:53:13 -08:00
|
|
|
let ctx = this.canvas.getContext("2d");
|
2010-09-09 22:29:00 -07:00
|
|
|
let tempCanvas = TabItems.tempCanvas;
|
2011-02-02 17:53:13 -08:00
|
|
|
let bgColor = '#fff';
|
|
|
|
|
2010-09-09 22:29:00 -07:00
|
|
|
if (w < tempCanvas.width) {
|
|
|
|
// Small draw case where nearest-neighbor algorithm breaks down in Windows
|
|
|
|
// First draw to a larger canvas (150px wide), and then draw that image
|
|
|
|
// to the destination canvas.
|
2011-02-02 17:53:13 -08:00
|
|
|
let tempCtx = tempCanvas.getContext("2d");
|
|
|
|
this._drawWindow(tempCtx, tempCanvas.width, tempCanvas.height, bgColor);
|
|
|
|
|
|
|
|
// Now copy to tabitem canvas.
|
|
|
|
try {
|
|
|
|
this._fillCanvasBackground(ctx, w, h, bgColor);
|
|
|
|
ctx.drawImage(tempCanvas, 0, 0, w, h);
|
|
|
|
} catch (e) {
|
2010-09-09 22:29:00 -07:00
|
|
|
Utils.error('paint', e);
|
2011-02-02 17:53:13 -08:00
|
|
|
}
|
2010-09-09 22:29:00 -07:00
|
|
|
} else {
|
|
|
|
// General case where nearest neighbor algorithm looks good
|
|
|
|
// Draw directly to the destination canvas
|
2011-02-02 17:53:13 -08:00
|
|
|
this._drawWindow(ctx, w, h, bgColor);
|
|
|
|
}
|
2011-09-01 06:17:27 -07:00
|
|
|
|
|
|
|
this._sendToSubscribers("painted");
|
2011-02-02 17:53:13 -08:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: _fillCanvasBackground
|
|
|
|
// Draws a rectangle of <width>x<height> with color <bgColor> to the given
|
|
|
|
// canvas context.
|
|
|
|
_fillCanvasBackground: function TabCanvas__fillCanvasBackground(ctx, width, height, bgColor) {
|
|
|
|
ctx.fillStyle = bgColor;
|
|
|
|
ctx.fillRect(0, 0, width, height);
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: _drawWindow
|
|
|
|
// Draws contents of the tabs' browser window to the given canvas context.
|
|
|
|
_drawWindow: function TabCanvas__drawWindow(ctx, width, height, bgColor) {
|
|
|
|
this._fillCanvasBackground(ctx, width, height, bgColor);
|
|
|
|
|
|
|
|
let rect = this._calculateClippingRect(width, height);
|
|
|
|
let scaler = width / rect.width;
|
|
|
|
|
|
|
|
ctx.save();
|
|
|
|
ctx.scale(scaler, scaler);
|
|
|
|
|
|
|
|
try {
|
|
|
|
let win = this.tab.linkedBrowser.contentWindow;
|
|
|
|
ctx.drawWindow(win, rect.left, rect.top, rect.width, rect.height,
|
|
|
|
bgColor, ctx.DRAWWINDOW_DO_NOT_FLUSH);
|
|
|
|
} catch (e) {
|
|
|
|
Utils.error('paint', e);
|
2010-07-27 20:02:51 -07:00
|
|
|
}
|
2011-02-02 17:53:13 -08:00
|
|
|
|
|
|
|
ctx.restore();
|
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: _calculateClippingRect
|
|
|
|
// Calculate the clipping rect that will be projected to the tab's
|
|
|
|
// thumbnail canvas.
|
|
|
|
_calculateClippingRect: function TabCanvas__calculateClippingRect(origWidth, origHeight) {
|
|
|
|
let win = this.tab.linkedBrowser.contentWindow;
|
|
|
|
|
2011-02-05 01:10:43 -08:00
|
|
|
// TODO BUG 631593: retrieve actual scrollbar width
|
|
|
|
// 25px is supposed to be width of the vertical scrollbar
|
2011-02-15 13:31:02 -08:00
|
|
|
let maxWidth = Math.max(1, win.innerWidth - 25);
|
2011-02-02 17:53:13 -08:00
|
|
|
let maxHeight = win.innerHeight;
|
|
|
|
|
|
|
|
let height = Math.min(maxHeight, Math.floor(origHeight * maxWidth / origWidth));
|
|
|
|
let width = Math.floor(origWidth * height / origHeight);
|
|
|
|
|
|
|
|
// very short pages in combination with a very wide browser window force us
|
|
|
|
// to extend the clipping rect and add some empty space around the thumb
|
|
|
|
let factor = 0.7;
|
|
|
|
if (width < maxWidth * factor) {
|
|
|
|
width = maxWidth * factor;
|
|
|
|
height = Math.floor(origHeight * width / origWidth);
|
|
|
|
}
|
|
|
|
|
|
|
|
let left = win.scrollX + Math.max(0, Math.round((maxWidth - width) / 2));
|
|
|
|
let top = win.scrollY;
|
|
|
|
|
|
|
|
return new Rect(left, top, width, height);
|
2010-07-27 20:02:51 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
// ----------
|
|
|
|
// Function: toImageData
|
2010-09-08 10:02:08 -07:00
|
|
|
toImageData: function TabCanvas_toImageData() {
|
2011-05-26 00:28:26 -07:00
|
|
|
return this.canvas.toDataURL("image/png");
|
2010-07-27 20:02:51 -07:00
|
|
|
}
|
2011-09-01 06:17:27 -07:00
|
|
|
});
|