зеркало из https://github.com/mozilla/gecko-dev.git
Bug 941321 - try-finally all of Australis' begin/endBatchUpdate calls, r=mconley
--HG-- extra : rebase_source : 06102ada89853f201baacbca37dc9ced1243acca
This commit is contained in:
Родитель
225944ff63
Коммит
6a64c70da7
|
@ -220,8 +220,11 @@ const PanelUI = {
|
|||
CustomizableUI.registerMenuPanel(this.contents);
|
||||
} else {
|
||||
this.beginBatchUpdate();
|
||||
CustomizableUI.registerMenuPanel(this.contents);
|
||||
this.endBatchUpdate();
|
||||
try {
|
||||
CustomizableUI.registerMenuPanel(this.contents);
|
||||
} finally {
|
||||
this.endBatchUpdate();
|
||||
}
|
||||
}
|
||||
this.panel.hidden = false;
|
||||
}.bind(this)).then(null, Cu.reportError);
|
||||
|
|
|
@ -193,29 +193,32 @@
|
|||
// Get a list of items only in the new list
|
||||
let newIds = [id for (id of newVal) if (oldIds.indexOf(id) == -1)];
|
||||
CustomizableUI.beginBatchUpdate();
|
||||
for (let newId of newIds) {
|
||||
oldIds = CustomizableUI.getWidgetIdsInArea(this.id);
|
||||
let nextId = newId;
|
||||
let pos;
|
||||
do {
|
||||
// Get the next item
|
||||
nextId = newVal[newVal.indexOf(nextId) + 1];
|
||||
// Figure out where it is in the old list
|
||||
pos = oldIds.indexOf(nextId);
|
||||
// If it's not in the old list, repeat:
|
||||
} while (pos == -1 && nextId);
|
||||
if (pos == -1) {
|
||||
pos = null; // We didn't find anything, insert at the end
|
||||
try {
|
||||
for (let newId of newIds) {
|
||||
oldIds = CustomizableUI.getWidgetIdsInArea(this.id);
|
||||
let nextId = newId;
|
||||
let pos;
|
||||
do {
|
||||
// Get the next item
|
||||
nextId = newVal[newVal.indexOf(nextId) + 1];
|
||||
// Figure out where it is in the old list
|
||||
pos = oldIds.indexOf(nextId);
|
||||
// If it's not in the old list, repeat:
|
||||
} while (pos == -1 && nextId);
|
||||
if (pos == -1) {
|
||||
pos = null; // We didn't find anything, insert at the end
|
||||
}
|
||||
CustomizableUI.addWidgetToArea(newId, this.id, pos);
|
||||
}
|
||||
CustomizableUI.addWidgetToArea(newId, this.id, pos);
|
||||
}
|
||||
|
||||
let currentIds = this.currentSet.split(',');
|
||||
let removedIds = [id for (id of currentIds) if (newIds.indexOf(id) == -1 && newVal.indexOf(id) == -1)];
|
||||
for (let removedId of removedIds) {
|
||||
CustomizableUI.removeWidgetFromArea(removedId);
|
||||
let currentIds = this.currentSet.split(',');
|
||||
let removedIds = [id for (id of currentIds) if (newIds.indexOf(id) == -1 && newVal.indexOf(id) == -1)];
|
||||
for (let removedId of removedIds) {
|
||||
CustomizableUI.removeWidgetFromArea(removedId);
|
||||
}
|
||||
} finally {
|
||||
CustomizableUI.endBatchUpdate();
|
||||
}
|
||||
CustomizableUI.endBatchUpdate();
|
||||
]]></setter>
|
||||
</property>
|
||||
|
||||
|
|
|
@ -279,15 +279,18 @@ let CustomizableUIInternal = {
|
|||
|
||||
// Move all the widgets out
|
||||
this.beginBatchUpdate();
|
||||
let placements = gPlacements.get(aName);
|
||||
placements.forEach(this.removeWidgetFromArea, this);
|
||||
try {
|
||||
let placements = gPlacements.get(aName);
|
||||
placements.forEach(this.removeWidgetFromArea, this);
|
||||
|
||||
// Delete all remaining traces.
|
||||
gAreas.delete(aName);
|
||||
gPlacements.delete(aName);
|
||||
gFuturePlacements.delete(aName);
|
||||
gBuildAreas.delete(aName);
|
||||
this.endBatchUpdate(true);
|
||||
// Delete all remaining traces.
|
||||
gAreas.delete(aName);
|
||||
gPlacements.delete(aName);
|
||||
gFuturePlacements.delete(aName);
|
||||
gBuildAreas.delete(aName);
|
||||
} finally {
|
||||
this.endBatchUpdate(true);
|
||||
}
|
||||
},
|
||||
|
||||
registerToolbarNode: function(aToolbar, aExistingChildren) {
|
||||
|
@ -303,44 +306,47 @@ let CustomizableUIInternal = {
|
|||
}
|
||||
|
||||
this.beginBatchUpdate();
|
||||
let placements = gPlacements.get(area);
|
||||
if (!placements && areaProperties.has("legacy")) {
|
||||
let legacyState = aToolbar.getAttribute("currentset");
|
||||
if (legacyState) {
|
||||
legacyState = legacyState.split(",").filter(s => s);
|
||||
try {
|
||||
let placements = gPlacements.get(area);
|
||||
if (!placements && areaProperties.has("legacy")) {
|
||||
let legacyState = aToolbar.getAttribute("currentset");
|
||||
if (legacyState) {
|
||||
legacyState = legacyState.split(",").filter(s => s);
|
||||
}
|
||||
|
||||
// Manually restore the state here, so the legacy state can be converted.
|
||||
this.restoreStateForArea(area, legacyState);
|
||||
placements = gPlacements.get(area);
|
||||
}
|
||||
|
||||
// Manually restore the state here, so the legacy state can be converted.
|
||||
this.restoreStateForArea(area, legacyState);
|
||||
placements = gPlacements.get(area);
|
||||
}
|
||||
// Check that the current children and the current placements match. If
|
||||
// not, mark it as dirty:
|
||||
if (aExistingChildren.length != placements.length ||
|
||||
aExistingChildren.every((id, i) => id == placements[i])) {
|
||||
gDirtyAreaCache.add(area);
|
||||
}
|
||||
|
||||
// Check that the current children and the current placements match. If
|
||||
// not, mark it as dirty:
|
||||
if (aExistingChildren.length != placements.length ||
|
||||
aExistingChildren.every((id, i) => id == placements[i])) {
|
||||
gDirtyAreaCache.add(area);
|
||||
}
|
||||
if (areaProperties.has("overflowable")) {
|
||||
aToolbar.overflowable = new OverflowableToolbar(aToolbar);
|
||||
}
|
||||
|
||||
if (areaProperties.has("overflowable")) {
|
||||
aToolbar.overflowable = new OverflowableToolbar(aToolbar);
|
||||
}
|
||||
this.registerBuildArea(area, aToolbar);
|
||||
|
||||
this.registerBuildArea(area, aToolbar);
|
||||
|
||||
// We only build the toolbar if it's been marked as "dirty". Dirty means
|
||||
// one of the following things:
|
||||
// 1) Items have been added, moved or removed from this toolbar before.
|
||||
// 2) The number of children of the toolbar does not match the length of
|
||||
// the placements array for that area.
|
||||
//
|
||||
// This notion of being "dirty" is stored in a cache which is persisted
|
||||
// in the saved state.
|
||||
if (gDirtyAreaCache.has(area)) {
|
||||
this.buildArea(area, placements, aToolbar);
|
||||
// We only build the toolbar if it's been marked as "dirty". Dirty means
|
||||
// one of the following things:
|
||||
// 1) Items have been added, moved or removed from this toolbar before.
|
||||
// 2) The number of children of the toolbar does not match the length of
|
||||
// the placements array for that area.
|
||||
//
|
||||
// This notion of being "dirty" is stored in a cache which is persisted
|
||||
// in the saved state.
|
||||
if (gDirtyAreaCache.has(area)) {
|
||||
this.buildArea(area, placements, aToolbar);
|
||||
}
|
||||
aToolbar.setAttribute("currentset", placements.join(","));
|
||||
} finally {
|
||||
this.endBatchUpdate();
|
||||
}
|
||||
aToolbar.setAttribute("currentset", placements.join(","));
|
||||
this.endBatchUpdate();
|
||||
},
|
||||
|
||||
buildArea: function(aArea, aPlacements, aAreaNode) {
|
||||
|
@ -354,110 +360,112 @@ let CustomizableUIInternal = {
|
|||
+ " to have a customizationTarget attribute.");
|
||||
}
|
||||
|
||||
this.beginBatchUpdate();
|
||||
|
||||
// Restore nav-bar visibility since it may have been hidden
|
||||
// through a migration path (bug 938980) or an add-on.
|
||||
if (aArea == CustomizableUI.AREA_NAVBAR) {
|
||||
aAreaNode.collapsed = false;
|
||||
}
|
||||
|
||||
let currentNode = container.firstChild;
|
||||
let placementsToRemove = new Set();
|
||||
for (let id of aPlacements) {
|
||||
while (currentNode && currentNode.getAttribute("skipintoolbarset") == "true") {
|
||||
currentNode = currentNode.nextSibling;
|
||||
}
|
||||
this.beginBatchUpdate();
|
||||
|
||||
if (currentNode && currentNode.id == id) {
|
||||
currentNode = currentNode.nextSibling;
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
let currentNode = container.firstChild;
|
||||
let placementsToRemove = new Set();
|
||||
for (let id of aPlacements) {
|
||||
while (currentNode && currentNode.getAttribute("skipintoolbarset") == "true") {
|
||||
currentNode = currentNode.nextSibling;
|
||||
}
|
||||
|
||||
let [provider, node] = this.getWidgetNode(id, window);
|
||||
if (!node) {
|
||||
LOG("Unknown widget: " + id);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the placements have items in them which are (now) no longer removable,
|
||||
// we shouldn't be moving them:
|
||||
if (node.parentNode != container && !this.isWidgetRemovable(node)) {
|
||||
placementsToRemove.add(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inPrivateWindow && provider == CustomizableUI.PROVIDER_API) {
|
||||
let widget = gPalette.get(id);
|
||||
if (!widget.showInPrivateBrowsing && inPrivateWindow) {
|
||||
if (currentNode && currentNode.id == id) {
|
||||
currentNode = currentNode.nextSibling;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
this.ensureButtonContextMenu(node, aAreaNode);
|
||||
if (node.localName == "toolbarbutton" && aArea == CustomizableUI.AREA_PANEL) {
|
||||
node.setAttribute("tabindex", "0");
|
||||
if (!node.hasAttribute("type")) {
|
||||
node.setAttribute("type", "wrap");
|
||||
let [provider, node] = this.getWidgetNode(id, window);
|
||||
if (!node) {
|
||||
LOG("Unknown widget: " + id);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
this.insertWidgetBefore(node, currentNode, container, aArea);
|
||||
if (gResetting) {
|
||||
this.notifyListeners("onWidgetReset", id, aArea);
|
||||
}
|
||||
}
|
||||
// If the placements have items in them which are (now) no longer removable,
|
||||
// we shouldn't be moving them:
|
||||
if (node.parentNode != container && !this.isWidgetRemovable(node)) {
|
||||
placementsToRemove.add(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentNode) {
|
||||
let palette = aAreaNode.toolbox ? aAreaNode.toolbox.palette : null;
|
||||
let limit = currentNode.previousSibling;
|
||||
let node = container.lastChild;
|
||||
while (node && node != limit) {
|
||||
let previousSibling = node.previousSibling;
|
||||
// Nodes opt-in to removability. If they're removable, and we haven't
|
||||
// seen them in the placements array, then we toss them into the palette
|
||||
// if one exists. If no palette exists, we just remove the node. If the
|
||||
// node is not removable, we leave it where it is. However, we can only
|
||||
// safely touch elements that have an ID - both because we depend on
|
||||
// IDs, and because such elements are not intended to be widgets
|
||||
// (eg, titlebar-placeholder elements).
|
||||
if (node.id && node.getAttribute("skipintoolbarset") != "true") {
|
||||
if (this.isWidgetRemovable(node)) {
|
||||
if (palette && !this.isSpecialWidget(node.id)) {
|
||||
palette.appendChild(node);
|
||||
this.removeLocationAttributes(node);
|
||||
} else {
|
||||
container.removeChild(node);
|
||||
}
|
||||
} else {
|
||||
this.setLocationAttributes(currentNode, aArea);
|
||||
node.setAttribute("removable", false);
|
||||
LOG("Adding non-removable widget to placements of " + aArea + ": " +
|
||||
node.id);
|
||||
gPlacements.get(aArea).push(node.id);
|
||||
gDirty = true;
|
||||
if (inPrivateWindow && provider == CustomizableUI.PROVIDER_API) {
|
||||
let widget = gPalette.get(id);
|
||||
if (!widget.showInPrivateBrowsing && inPrivateWindow) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
node = previousSibling;
|
||||
|
||||
this.ensureButtonContextMenu(node, aAreaNode);
|
||||
if (node.localName == "toolbarbutton" && aArea == CustomizableUI.AREA_PANEL) {
|
||||
node.setAttribute("tabindex", "0");
|
||||
if (!node.hasAttribute("type")) {
|
||||
node.setAttribute("type", "wrap");
|
||||
}
|
||||
}
|
||||
|
||||
this.insertWidgetBefore(node, currentNode, container, aArea);
|
||||
if (gResetting) {
|
||||
this.notifyListeners("onWidgetReset", id, aArea);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there are placements in here which aren't removable from their original area,
|
||||
// we remove them from this area's placement array. They will (have) be(en) added
|
||||
// to their original area's placements array in the block above this one.
|
||||
if (placementsToRemove.size) {
|
||||
let placementAry = gPlacements.get(aArea);
|
||||
for (let id of placementsToRemove) {
|
||||
let index = placementAry.indexOf(id);
|
||||
placementAry.splice(index, 1);
|
||||
if (currentNode) {
|
||||
let palette = aAreaNode.toolbox ? aAreaNode.toolbox.palette : null;
|
||||
let limit = currentNode.previousSibling;
|
||||
let node = container.lastChild;
|
||||
while (node && node != limit) {
|
||||
let previousSibling = node.previousSibling;
|
||||
// Nodes opt-in to removability. If they're removable, and we haven't
|
||||
// seen them in the placements array, then we toss them into the palette
|
||||
// if one exists. If no palette exists, we just remove the node. If the
|
||||
// node is not removable, we leave it where it is. However, we can only
|
||||
// safely touch elements that have an ID - both because we depend on
|
||||
// IDs, and because such elements are not intended to be widgets
|
||||
// (eg, titlebar-placeholder elements).
|
||||
if (node.id && node.getAttribute("skipintoolbarset") != "true") {
|
||||
if (this.isWidgetRemovable(node)) {
|
||||
if (palette && !this.isSpecialWidget(node.id)) {
|
||||
palette.appendChild(node);
|
||||
this.removeLocationAttributes(node);
|
||||
} else {
|
||||
container.removeChild(node);
|
||||
}
|
||||
} else {
|
||||
this.setLocationAttributes(currentNode, aArea);
|
||||
node.setAttribute("removable", false);
|
||||
LOG("Adding non-removable widget to placements of " + aArea + ": " +
|
||||
node.id);
|
||||
gPlacements.get(aArea).push(node.id);
|
||||
gDirty = true;
|
||||
}
|
||||
}
|
||||
node = previousSibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gResetting) {
|
||||
this.notifyListeners("onAreaReset", aArea);
|
||||
}
|
||||
// If there are placements in here which aren't removable from their original area,
|
||||
// we remove them from this area's placement array. They will (have) be(en) added
|
||||
// to their original area's placements array in the block above this one.
|
||||
if (placementsToRemove.size) {
|
||||
let placementAry = gPlacements.get(aArea);
|
||||
for (let id of placementsToRemove) {
|
||||
let index = placementAry.indexOf(id);
|
||||
placementAry.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.endBatchUpdate();
|
||||
if (gResetting) {
|
||||
this.notifyListeners("onAreaReset", aArea);
|
||||
}
|
||||
} finally {
|
||||
this.endBatchUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
addPanelCloseListeners: function(aPanel) {
|
||||
|
@ -1382,51 +1390,54 @@ let CustomizableUIInternal = {
|
|||
}
|
||||
|
||||
this.beginBatchUpdate();
|
||||
gRestoring = true;
|
||||
try {
|
||||
gRestoring = true;
|
||||
|
||||
let restored = false;
|
||||
gPlacements.set(aArea, []);
|
||||
let restored = false;
|
||||
gPlacements.set(aArea, []);
|
||||
|
||||
if (gSavedState && aArea in gSavedState.placements) {
|
||||
LOG("Restoring " + aArea + " from saved state");
|
||||
let placements = gSavedState.placements[aArea];
|
||||
for (let id of placements)
|
||||
this.addWidgetToArea(id, aArea);
|
||||
gDirty = false;
|
||||
restored = true;
|
||||
}
|
||||
|
||||
if (!restored && aLegacyState) {
|
||||
LOG("Restoring " + aArea + " from legacy state");
|
||||
for (let id of aLegacyState)
|
||||
this.addWidgetToArea(id, aArea);
|
||||
// Don't override dirty state, to ensure legacy state is saved here and
|
||||
// therefore only used once.
|
||||
restored = true;
|
||||
}
|
||||
|
||||
if (!restored) {
|
||||
LOG("Restoring " + aArea + " from default state");
|
||||
let defaults = gAreas.get(aArea).get("defaultPlacements");
|
||||
if (defaults) {
|
||||
for (let id of defaults)
|
||||
this.addWidgetToArea(id, aArea, null, true);
|
||||
if (gSavedState && aArea in gSavedState.placements) {
|
||||
LOG("Restoring " + aArea + " from saved state");
|
||||
let placements = gSavedState.placements[aArea];
|
||||
for (let id of placements)
|
||||
this.addWidgetToArea(id, aArea);
|
||||
gDirty = false;
|
||||
restored = true;
|
||||
}
|
||||
gDirty = false;
|
||||
|
||||
if (!restored && aLegacyState) {
|
||||
LOG("Restoring " + aArea + " from legacy state");
|
||||
for (let id of aLegacyState)
|
||||
this.addWidgetToArea(id, aArea);
|
||||
// Don't override dirty state, to ensure legacy state is saved here and
|
||||
// therefore only used once.
|
||||
restored = true;
|
||||
}
|
||||
|
||||
if (!restored) {
|
||||
LOG("Restoring " + aArea + " from default state");
|
||||
let defaults = gAreas.get(aArea).get("defaultPlacements");
|
||||
if (defaults) {
|
||||
for (let id of defaults)
|
||||
this.addWidgetToArea(id, aArea, null, true);
|
||||
}
|
||||
gDirty = false;
|
||||
}
|
||||
|
||||
// Finally, add widgets to the area that were added before the it was able
|
||||
// to be restored. This can occur when add-ons register widgets for a
|
||||
// lazily-restored area before it's been restored.
|
||||
if (gFuturePlacements.has(aArea)) {
|
||||
for (let id of gFuturePlacements.get(aArea))
|
||||
this.addWidgetToArea(id, aArea);
|
||||
}
|
||||
|
||||
LOG("Placements for " + aArea + ":\n\t" + gPlacements.get(aArea).join("\n\t"));
|
||||
|
||||
gRestoring = false;
|
||||
} finally {
|
||||
this.endBatchUpdate();
|
||||
}
|
||||
|
||||
// Finally, add widgets to the area that were added before the it was able
|
||||
// to be restored. This can occur when add-ons register widgets for a
|
||||
// lazily-restored area before it's been restored.
|
||||
if (gFuturePlacements.has(aArea)) {
|
||||
for (let id of gFuturePlacements.get(aArea))
|
||||
this.addWidgetToArea(id, aArea);
|
||||
}
|
||||
|
||||
LOG("Placements for " + aArea + ":\n\t" + gPlacements.get(aArea).join("\n\t"));
|
||||
|
||||
gRestoring = false;
|
||||
this.endBatchUpdate();
|
||||
},
|
||||
|
||||
saveState: function() {
|
||||
|
@ -1581,17 +1592,19 @@ let CustomizableUIInternal = {
|
|||
// seen before, then add it to its default area so it can be used.
|
||||
if (autoAdd && !widget.currentArea && !gSeenWidgets.has(widget.id)) {
|
||||
this.beginBatchUpdate();
|
||||
gSeenWidgets.add(widget.id);
|
||||
try {
|
||||
gSeenWidgets.add(widget.id);
|
||||
|
||||
if (widget.defaultArea) {
|
||||
if (this.isAreaLazy(widget.defaultArea)) {
|
||||
gFuturePlacements.get(widget.defaultArea).add(widget.id);
|
||||
} else {
|
||||
this.addWidgetToArea(widget.id, widget.defaultArea);
|
||||
if (widget.defaultArea) {
|
||||
if (this.isAreaLazy(widget.defaultArea)) {
|
||||
gFuturePlacements.get(widget.defaultArea).add(widget.id);
|
||||
} else {
|
||||
this.addWidgetToArea(widget.id, widget.defaultArea);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.endBatchUpdate(true);
|
||||
}
|
||||
|
||||
this.endBatchUpdate(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -93,6 +93,9 @@ CustomizeMode.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
let window = this.window;
|
||||
let document = this.document;
|
||||
|
||||
Task.spawn(function() {
|
||||
// We shouldn't start customize mode until after browser-delayed-startup has finished:
|
||||
if (!this.window.gBrowserInit.delayedStartupFinished) {
|
||||
|
@ -115,9 +118,6 @@ CustomizeMode.prototype = {
|
|||
this.dispatchToolboxEvent("beforecustomization");
|
||||
CustomizableUI.notifyStartCustomizing(this.window);
|
||||
|
||||
let window = this.window;
|
||||
let document = this.document;
|
||||
|
||||
// Add a keypress listener to the document so that we can quickly exit
|
||||
// customization mode when pressing ESC.
|
||||
document.addEventListener("keypress", this);
|
||||
|
@ -159,7 +159,6 @@ CustomizeMode.prototype = {
|
|||
}
|
||||
|
||||
this._showPanelCustomizationPlaceholders();
|
||||
CustomizableUI.addListener(this);
|
||||
|
||||
yield this._wrapToolbarItems();
|
||||
yield this.populatePalette();
|
||||
|
@ -184,11 +183,16 @@ CustomizeMode.prototype = {
|
|||
for (let toolbar of customizableToolbars)
|
||||
toolbar.setAttribute("customizing", true);
|
||||
|
||||
CustomizableUI.addListener(this);
|
||||
window.PanelUI.endBatchUpdate();
|
||||
this._customizing = true;
|
||||
this._transitioning = false;
|
||||
this.dispatchToolboxEvent("customizationready");
|
||||
}.bind(this)).then(null, ERROR);
|
||||
}.bind(this)).then(null, function(e) {
|
||||
ERROR(e);
|
||||
// We should ensure this has been called, and calling it again doesn't hurt:
|
||||
window.PanelUI.endBatchUpdate();
|
||||
});
|
||||
},
|
||||
|
||||
exit: function() {
|
||||
|
@ -298,7 +302,11 @@ CustomizeMode.prototype = {
|
|||
this._transitioning = false;
|
||||
this.dispatchToolboxEvent("aftercustomization");
|
||||
CustomizableUI.notifyEndCustomizing(this.window);
|
||||
}.bind(this)).then(null, ERROR);
|
||||
}.bind(this)).then(null, function(e) {
|
||||
ERROR(e);
|
||||
// We should ensure this has been called, and calling it again doesn't hurt:
|
||||
window.PanelUI.endBatchUpdate();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче