Bug 895359 - [New Tab Page] Switch to Promise.jsm and remove remaining callbacks; r=jaws

This commit is contained in:
Tim Taubert 2013-07-24 09:58:50 +02:00
parent 0b00114d06
commit 5a834565bf
4 changed files with 90 additions and 151 deletions

View File

@ -11,7 +11,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/PageThumbs.jsm");
Cu.import("resource://gre/modules/NewTabUtils.jsm");
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
Cu.import("resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Rect",
"resource://gre/modules/Geometry.jsm");

View File

@ -60,7 +60,7 @@ Site.prototype = {
},
/**
* Unpins the site and calls the given callback when done.
* Unpins the site.
*/
unpin: function Site_unpin() {
if (this.isPinned()) {
@ -79,8 +79,7 @@ Site.prototype = {
},
/**
* Blocks the site (removes it from the grid) and calls the given callback
* when done.
* Blocks the site (removes it from the grid).
*/
block: function Site_block() {
if (!gBlockedLinks.isBlocked(this._link)) {

View File

@ -38,46 +38,24 @@ let gTransformation = {
return new Rect(left + scrollX, top + scrollY, width, height);
},
/**
* Fades a given node from zero to full opacity.
* @param aNode The node to fade.
* @param aCallback The callback to call when finished.
*/
fadeNodeIn: function Transformation_fadeNodeIn(aNode, aCallback) {
this._setNodeOpacity(aNode, 1, function () {
// Clear the style property.
aNode.style.opacity = "";
if (aCallback)
aCallback();
});
},
/**
* Fades a given node from full to zero opacity.
* @param aNode The node to fade.
* @param aCallback The callback to call when finished.
*/
fadeNodeOut: function Transformation_fadeNodeOut(aNode, aCallback) {
this._setNodeOpacity(aNode, 0, aCallback);
},
/**
* Fades a given site from zero to full opacity.
* @param aSite The site to fade.
* @param aCallback The callback to call when finished.
*/
showSite: function Transformation_showSite(aSite, aCallback) {
this.fadeNodeIn(aSite.node, aCallback);
showSite: function (aSite) {
let node = aSite.node;
return this._setNodeOpacity(node, 1).then(() => {
// Clear the style property.
node.style.opacity = "";
});
},
/**
* Fades a given site from full to zero opacity.
* @param aSite The site to fade.
* @param aCallback The callback to call when finished.
*/
hideSite: function Transformation_hideSite(aSite, aCallback) {
this.fadeNodeOut(aSite.node, aCallback);
hideSite: function (aSite) {
return this._setNodeOpacity(aSite.node, 0);
},
/**
@ -129,22 +107,11 @@ let gTransformation = {
* @param aTarget The slide target.
* @param aOptions Set of options (see below).
* unfreeze - unfreeze the site after sliding
* callback - the callback to call when finished
*/
slideSiteTo: function Transformation_slideSiteTo(aSite, aTarget, aOptions) {
slideSiteTo: function (aSite, aTarget, aOptions) {
let currentPosition = this.getNodePosition(aSite.node);
let targetPosition = this.getNodePosition(aTarget.node)
let callback = aOptions && aOptions.callback;
let self = this;
function finish() {
if (aOptions && aOptions.unfreeze)
self.unfreezeSitePosition(aSite);
if (callback)
callback();
}
let promise;
// We need to take the width of a cell's border into account.
targetPosition.left += this._cellBorderWidths.left;
@ -153,11 +120,17 @@ let gTransformation = {
// Nothing to do here if the positions already match.
if (currentPosition.left == targetPosition.left &&
currentPosition.top == targetPosition.top) {
finish();
promise = Promise.resolve();
} else {
this.setSitePosition(aSite, targetPosition);
this._whenTransitionEnded(aSite.node, ["left", "top"], finish);
promise = this._whenTransitionEnded(aSite.node, ["left", "top"]);
}
if (aOptions && aOptions.unfreeze) {
promise = promise.then(() => this.unfreezeSitePosition(aSite));
}
return promise;
},
/**
@ -166,55 +139,50 @@ let gTransformation = {
* @param aSites An array of sites to rearrange.
* @param aOptions Set of options (see below).
* unfreeze - unfreeze the site after rearranging
* callback - the callback to call when finished
*/
rearrangeSites: function Transformation_rearrangeSites(aSites, aOptions) {
let batch = [];
rearrangeSites: function (aSites, aOptions) {
let self = this;
let cells = gGrid.cells;
let callback = aOptions && aOptions.callback;
let unfreeze = aOptions && aOptions.unfreeze;
aSites.forEach(function (aSite, aIndex) {
// Do not re-arrange empty cells or the dragged site.
if (!aSite || aSite == gDrag.draggedSite)
return;
function promises() {
let index = 0;
let deferred = Promise.defer();
batch.push(deferred.promise);
let cb = function () deferred.resolve();
for (let site of aSites) {
if (site && site !== gDrag.draggedSite) {
if (!cells[index]) {
// The site disappeared from the grid, hide it.
yield self.hideSite(site);
} else if (self._getNodeOpacity(site.node) != 1) {
// The site disappeared before but is now back, show it.
yield self.showSite(site);
} else {
// The site's position has changed, move it around.
yield self._moveSite(site, index, {unfreeze: unfreeze});
}
}
index++;
}
}
if (!cells[aIndex])
// The site disappeared from the grid, hide it.
this.hideSite(aSite, cb);
else if (this._getNodeOpacity(aSite.node) != 1)
// The site disappeared before but is now back, show it.
this.showSite(aSite, cb);
else
// The site's position has changed, move it around.
this._moveSite(aSite, aIndex, {unfreeze: unfreeze, callback: cb});
}, this);
let wait = Promise.promised(function () callback && callback());
wait.apply(null, batch);
return Promise.every([p for (p of promises())]);
},
/**
* Listens for the 'transitionend' event on a given node and calls the given
* callback.
* Listens for the 'transitionend' event on a given node.
* @param aNode The node that is transitioned.
* @param aProperties The properties we'll wait to be transitioned.
* @param aCallback The callback to call when finished.
*/
_whenTransitionEnded:
function Transformation_whenTransitionEnded(aNode, aProperties, aCallback) {
_whenTransitionEnded: function (aNode, aProperties) {
let deferred = Promise.defer();
let props = new Set(aProperties);
aNode.addEventListener("transitionend", function onEnd(e) {
if (props.has(e.propertyName)) {
aNode.removeEventListener("transitionend", onEnd);
aCallback();
deferred.resolve();
}
});
return deferred.promise;
},
/**
@ -231,21 +199,14 @@ let gTransformation = {
* Sets a given node's opacity.
* @param aNode The node to set the opacity value for.
* @param aOpacity The opacity value to set.
* @param aCallback The callback to call when finished.
*/
_setNodeOpacity:
function Transformation_setNodeOpacity(aNode, aOpacity, aCallback) {
_setNodeOpacity: function (aNode, aOpacity) {
if (this._getNodeOpacity(aNode) == aOpacity) {
if (aCallback)
aCallback();
} else {
if (aCallback) {
this._whenTransitionEnded(aNode, ["opacity"], aCallback);
}
aNode.style.opacity = aOpacity;
return Promise.resolve();
}
aNode.style.opacity = aOpacity;
return this._whenTransitionEnded(aNode, ["opacity"]);
},
/**
@ -254,9 +215,9 @@ let gTransformation = {
* @param aIndex The target cell's index.
* @param aOptions Options that are directly passed to slideSiteTo().
*/
_moveSite: function Transformation_moveSite(aSite, aIndex, aOptions) {
_moveSite: function (aSite, aIndex, aOptions) {
this.freezeSitePosition(aSite);
this.slideSiteTo(aSite, gGrid.cells[aIndex], aOptions);
return this.slideSiteTo(aSite, gGrid.cells[aIndex], aOptions);
},
/**

View File

@ -12,32 +12,29 @@ let gUpdater = {
/**
* Updates the current grid according to its pinned and blocked sites.
* This removes old, moves existing and creates new sites to fill gaps.
* @param aCallback The callback to call when finished.
*/
updateGrid: function Updater_updateGrid(aCallback) {
updateGrid: function Updater_updateGrid() {
let links = gLinks.getLinks().slice(0, gGrid.cells.length);
// Find all sites that remain in the grid.
let sites = this._findRemainingSites(links);
let self = this;
// Remove sites that are no longer in the grid.
this._removeLegacySites(sites, function () {
this._removeLegacySites(sites).then(() => {
// Freeze all site positions so that we can move their DOM nodes around
// without any visual impact.
self._freezeSitePositions(sites);
this._freezeSitePositions(sites);
// Move the sites' DOM nodes to their new position in the DOM. This will
// have no visual effect as all the sites have been frozen and will
// remain in their current position.
self._moveSiteNodes(sites);
this._moveSiteNodes(sites);
// Now it's time to animate the sites actually moving to their new
// positions.
self._rearrangeSites(sites, function () {
this._rearrangeSites(sites).then(() => {
// Try to fill empty cells and finish.
self._fillEmptyCells(links, aCallback);
this._fillEmptyCells(links);
// Update other pages that might be open to keep them synced.
gAllPages.update(gPage);
@ -112,75 +109,57 @@ let gUpdater = {
/**
* Rearranges the given sites and slides them to their new positions.
* @param aSites The array of sites to re-arrange.
* @param aCallback The callback to call when finished.
*/
_rearrangeSites: function Updater_rearrangeSites(aSites, aCallback) {
let options = {callback: aCallback, unfreeze: true};
gTransformation.rearrangeSites(aSites, options);
_rearrangeSites: function (aSites) {
return gTransformation.rearrangeSites(aSites, {unfreeze: true});
},
/**
* Removes all sites from the grid that are not in the given links array or
* exceed the grid.
* @param aSites The array of sites remaining in the grid.
* @param aCallback The callback to call when finished.
*/
_removeLegacySites: function Updater_removeLegacySites(aSites, aCallback) {
let batch = [];
_removeLegacySites: function (aSites) {
let remainingSites = new Set(aSites);
// Delete sites that were removed from the grid.
gGrid.sites.forEach(function (aSite) {
// The site must be valid and not in the current grid.
if (!aSite || aSites.indexOf(aSite) != -1)
return;
function promises() {
for (let site of gGrid.sites) {
// The site must be valid and not in the current grid.
if (site && !remainingSites.has(site)) {
// Hide the site and remove it from the DOM.
let remove = site.node.remove.bind(site.node);
yield gTransformation.hideSite(site).then(remove);
}
}
}
let deferred = Promise.defer();
batch.push(deferred.promise);
// Fade out the to-be-removed site.
gTransformation.hideSite(aSite, function () {
let node = aSite.node;
// Remove the site from the DOM.
node.parentNode.removeChild(node);
deferred.resolve();
});
});
let wait = Promise.promised(aCallback);
wait.apply(null, batch);
return Promise.every([p for (p of promises())]);
},
/**
* Tries to fill empty cells with new links if available.
* @param aLinks The array of links.
* @param aCallback The callback to call when finished.
*/
_fillEmptyCells: function Updater_fillEmptyCells(aLinks, aCallback) {
_fillEmptyCells: function (aLinks) {
let {cells, sites} = gGrid;
let batch = [];
let index = 0;
// Find empty cells and fill them.
sites.forEach(function (aSite, aIndex) {
if (aSite || !aLinks[aIndex])
return;
for (let site of sites) {
if (!site && aLinks[index]) {
// Create the new site and fade it in.
site = gGrid.createSite(aLinks[index], cells[index]);
let deferred = Promise.defer();
batch.push(deferred.promise);
// Set the site's initial opacity to zero.
site.node.style.opacity = 0;
// Create the new site and fade it in.
let site = gGrid.createSite(aLinks[aIndex], cells[aIndex]);
// Flush all style changes for the dynamically inserted site to make
// the fade-in transition work.
window.getComputedStyle(site.node).opacity;
gTransformation.showSite(site);
}
// Set the site's initial opacity to zero.
site.node.style.opacity = 0;
// Flush all style changes for the dynamically inserted site to make
// the fade-in transition work.
window.getComputedStyle(site.node).opacity;
gTransformation.showSite(site, function () deferred.resolve());
});
let wait = Promise.promised(aCallback);
wait.apply(null, batch);
index++;
}
}
};