Bug 394353 (Part 2) - Tag list is not updated when manually adding, renaming, or deleting tags from within the tags field.

r+a=dietrich
This commit is contained in:
Marco Bonardo 2011-01-27 12:18:20 +01:00
parent 0d565615d8
commit cda3091ae8
4 changed files with 233 additions and 85 deletions

View File

@ -138,7 +138,7 @@ var gEditItemOverlay = {
this.uninitPanel(false); this.uninitPanel(false);
var aItemIdList; var aItemIdList;
if (aFor.length) { if (Array.isArray(aFor)) {
aItemIdList = aFor; aItemIdList = aFor;
aFor = aItemIdList[0]; aFor = aItemIdList[0];
} }
@ -211,7 +211,6 @@ var gEditItemOverlay = {
this._multiEdit = true; this._multiEdit = true;
this._allTags = []; this._allTags = [];
this._itemIds = aItemIdList; this._itemIds = aItemIdList;
var nodeToCheck = 0;
for (var i = 0; i < aItemIdList.length; i++) { for (var i = 0; i < aItemIdList.length; i++) {
if (aItemIdList[i] instanceof Ci.nsIURI) { if (aItemIdList[i] instanceof Ci.nsIURI) {
this._uris[i] = aItemIdList[i]; this._uris[i] = aItemIdList[i];
@ -220,10 +219,8 @@ var gEditItemOverlay = {
else else
this._uris[i] = PlacesUtils.bookmarks.getBookmarkURI(this._itemIds[i]); this._uris[i] = PlacesUtils.bookmarks.getBookmarkURI(this._itemIds[i]);
this._tags[i] = PlacesUtils.tagging.getTagsForURI(this._uris[i]); this._tags[i] = PlacesUtils.tagging.getTagsForURI(this._uris[i]);
if (this._tags[i].length < this._tags[nodeToCheck].length)
nodeToCheck = i;
} }
this._getCommonTags(nodeToCheck); this._allTags = this._getCommonTags();
this._initTextField("tagsField", this._allTags.join(", "), false); this._initTextField("tagsField", this._allTags.join(", "), false);
this._element("itemsCountText").value = this._element("itemsCountText").value =
PlacesUIUtils.getFormattedString("detailsPane.multipleItems", PlacesUIUtils.getFormattedString("detailsPane.multipleItems",
@ -241,7 +238,9 @@ var gEditItemOverlay = {
// observe changes // observe changes
if (!this._observersAdded) { if (!this._observersAdded) {
if (this._itemId != -1) // Single bookmarks observe any change. History entries and multiEdit
// observe only tags changes, through bookmarks.
if (this._itemId != -1 || this._uri || this._multiEdit)
PlacesUtils.bookmarks.addObserver(this, false); PlacesUtils.bookmarks.addObserver(this, false);
window.addEventListener("unload", this, false); window.addEventListener("unload", this, false);
this._observersAdded = true; this._observersAdded = true;
@ -250,22 +249,19 @@ var gEditItemOverlay = {
this._initialized = true; this._initialized = true;
}, },
_getCommonTags: function(aArrIndex) { /**
var tempArray = this._tags[aArrIndex]; * Finds tags that are in common among this._tags entries that track tags
var isAllTag; * for each selected uri.
for (var k = 0; k < tempArray.length; k++) { * The tags arrays should be kept up-to-date for this to work properly.
isAllTag = true; *
for (var j = 0; j < this._tags.length; j++) { * @return array of common tags for the selected uris.
if (j == aArrIndex) */
continue; _getCommonTags: function() {
if (this._tags[j].indexOf(tempArray[k]) == -1) { return this._tags[0].filter(
isAllTag = false; function (aTag) this._tags.every(
break; function (aTags) aTags.indexOf(aTag) != -1
} ), this
} );
if (isAllTag)
this._allTags.push(tempArray[k]);
}
}, },
_initTextField: function(aTextFieldId, aValue, aReadOnly) { _initTextField: function(aTextFieldId, aValue, aReadOnly) {
@ -542,7 +538,7 @@ var gEditItemOverlay = {
} }
if (this._observersAdded) { if (this._observersAdded) {
if (this._itemId != -1) if (this._itemId != -1 || this._uri || this._multiEdit)
PlacesUtils.bookmarks.removeObserver(this); PlacesUtils.bookmarks.removeObserver(this);
this._observersAdded = false; this._observersAdded = false;
@ -1069,6 +1065,42 @@ var gEditItemOverlay = {
onItemChanged: function EIO_onItemChanged(aItemId, aProperty, onItemChanged: function EIO_onItemChanged(aItemId, aProperty,
aIsAnnotationProperty, aValue, aIsAnnotationProperty, aValue,
aLastModified, aItemType) { aLastModified, aItemType) {
if (aProperty == "tags") {
// Tags case is special, since they should be updated if either:
// - the notification is for the edited bookmark
// - the notification is for the edited history entry
// - the notification is for one of edited uris
let shouldUpdateTagsField = this._itemId == aItemId;
if (this._itemId == -1 || this._multiEdit) {
// Check if the changed uri is part of the modified ones.
let changedURI = PlacesUtils.bookmarks.getBookmarkURI(aItemId);
let uris = this._multiEdit ? this._uris : [this._uri];
uris.forEach(function (aURI, aIndex) {
if (aURI.equals(changedURI)) {
shouldUpdateTagsField = true;
if (this._multiEdit) {
this._tags[aIndex] = PlacesUtils.tagging.getTagsForURI(this._uris[aIndex]);
}
}
}, this);
}
if (shouldUpdateTagsField) {
if (this._multiEdit) {
this._allTags = this._getCommonTags();
this._initTextField("tagsField", this._allTags.join(", "), false);
}
else {
let tags = PlacesUtils.tagging.getTagsForURI(this._uri).join(", ");
this._initTextField("tagsField", tags, false);
}
}
// Any tags change should be reflected in the tags selector.
this._rebuildTagsSelectorList();
return;
}
if (this._itemId != aItemId) { if (this._itemId != aItemId) {
if (aProperty == "title") { if (aProperty == "title") {
// If the title of a folder which is listed within the folders // If the title of a folder which is listed within the folders
@ -1164,25 +1196,9 @@ var gEditItemOverlay = {
onItemAdded: function EIO_onItemAdded(aItemId, aParentId, aIndex, aItemType, onItemAdded: function EIO_onItemAdded(aItemId, aParentId, aIndex, aItemType,
aURI) { aURI) {
this._lastNewItem = aItemId; this._lastNewItem = aItemId;
if (this._uri && aItemType == PlacesUtils.bookmarks.TYPE_BOOKMARK &&
PlacesUtils.bookmarks.getFolderIdForItem(aParentId) ==
PlacesUtils.tagsFolderId) {
// Ensure the tagsField is in sync.
let tags = PlacesUtils.tagging.getTagsForURI(this._uri).join(", ");
this._initTextField("tagsField", tags, false);
}
},
onItemRemoved: function(aItemId, aParentId, aIndex, aItemType) {
if (this._uri && aItemType == PlacesUtils.bookmarks.TYPE_BOOKMARK &&
PlacesUtils.bookmarks.getFolderIdForItem(aParentId) ==
PlacesUtils.tagsFolderId) {
// Ensure the tagsField is in sync.
let tags = PlacesUtils.tagging.getTagsForURI(this._uri).join(", ");
this._initTextField("tagsField", tags, false);
}
}, },
onItemRemoved: function() { },
onBeginUpdateBatch: function() { }, onBeginUpdateBatch: function() { },
onEndUpdateBatch: function() { }, onEndUpdateBatch: function() { },
onBeforeItemRemoved: function() { }, onBeforeItemRemoved: function() { },

View File

@ -36,43 +36,163 @@
<script type="application/javascript"> <script type="application/javascript">
<![CDATA[ <![CDATA[
function runTest() { function runTest()
{
Components.utils.import("resource://gre/modules/NetUtil.jsm"); Components.utils.import("resource://gre/modules/NetUtil.jsm");
const TEST_URI = NetUtil.newURI("http://www.test.me"); const TEST_URI = NetUtil.newURI("http://www.test.me/");
const TEST_URI2 = NetUtil.newURI("http://www.test.again.me/");
const TEST_TAG = "test-tag"; const TEST_TAG = "test-tag";
// Add a bookmark, init the panel then add a tag to the bookmark ok(gEditItemOverlay, "Sanity check: gEditItemOverlay is in context");
// through the Places API. The editBookmarkPanel should live update.
// Open the tags selector.
document.getElementById("editBMPanel_tagsSelectorRow").collapsed = false;
function checkTagsSelector(aAvailableTags, aCheckedTags)
{
is(PlacesUtils.tagging.allTags.length, aAvailableTags.length,
"tagging service is in sync.");
let tagsSelector = document.getElementById("editBMPanel_tagsSelector");
let children = tagsSelector.childNodes;
is(children.length, aAvailableTags.length,
"Found expected number of tags in the tags selector");
Array.forEach(children, function (aChild) {
let tag = aChild.getAttribute("label");
ok(true, "Found tag '" + tag + "' in the selector");
ok(aAvailableTags.indexOf(tag) != -1, "Found expected tag");
let checked = aChild.getAttribute("checked") == "true";
is(checked, aCheckedTags.indexOf(tag) != -1,
"Tag is correctly marked");
});
}
// Add a bookmark.
let itemId = PlacesUtils.bookmarks.insertBookmark( let itemId = PlacesUtils.bookmarks.insertBookmark(
PlacesUtils.toolbarFolderId, TEST_URI, PlacesUtils.unfiledBookmarksFolderId, TEST_URI,
PlacesUtils.bookmarks.DEFAULT_INDEX, "test.me" PlacesUtils.bookmarks.DEFAULT_INDEX, "test.me"
); );
// Init panel. // Init panel.
ok(gEditItemOverlay, "gEditItemOverlay is in context");
gEditItemOverlay.initPanel(itemId); gEditItemOverlay.initPanel(itemId);
// Add a tag. // Add a tag.
PlacesUtils.tagging.tagURI(TEST_URI, [TEST_TAG]); PlacesUtils.tagging.tagURI(TEST_URI, [TEST_TAG]);
// Test that the tag has been added.
is(PlacesUtils.tagging.getTagsForURI(TEST_URI)[0], TEST_TAG, is(PlacesUtils.tagging.getTagsForURI(TEST_URI)[0], TEST_TAG,
"Tag correctly added."); "Correctly added tag to a single bookmark");
// Check the panel. is(document.getElementById("editBMPanel_tagsField").value, TEST_TAG,
is (document.getElementById("editBMPanel_tagsField").value, TEST_TAG, "Editing a single bookmark shows the added tag");
"Tags match."); checkTagsSelector([TEST_TAG], [TEST_TAG]);
// Remove tag. // Remove tag.
PlacesUtils.tagging.untagURI(TEST_URI, [TEST_TAG]); PlacesUtils.tagging.untagURI(TEST_URI, [TEST_TAG]);
// Test that the tag has been removed.
is(PlacesUtils.tagging.getTagsForURI(TEST_URI)[0], undefined, is(PlacesUtils.tagging.getTagsForURI(TEST_URI)[0], undefined,
"Tag correctly removed."); "The tag has been removed");
// Check the panel. is(document.getElementById("editBMPanel_tagsField").value, "",
is (document.getElementById("editBMPanel_tagsField").value, "", "Editing a single bookmark should not show any tag");
"Tags match."); checkTagsSelector([], []);
// Add a second bookmark.
let itemId2 = PlacesUtils.bookmarks.insertBookmark(
PlacesUtils.unfiledBookmarksFolderId, TEST_URI2,
PlacesUtils.bookmarks.DEFAULT_INDEX, "test.again.me"
);
// Init panel with multiple bookmarks.
gEditItemOverlay.initPanel([itemId, itemId2]);
// Add a tag to the first bookmark.
PlacesUtils.tagging.tagURI(TEST_URI, [TEST_TAG]);
is(PlacesUtils.tagging.getTagsForURI(TEST_URI)[0], TEST_TAG,
"Correctly added a tag to the first bookmark.");
is(document.getElementById("editBMPanel_tagsField").value, "",
"Editing multiple bookmarks without matching tags should not show any tag.");
checkTagsSelector([TEST_TAG], []);
// Add a tag to the second bookmark.
PlacesUtils.tagging.tagURI(TEST_URI2, [TEST_TAG]);
is(PlacesUtils.tagging.getTagsForURI(TEST_URI2)[0], TEST_TAG,
"Correctly added a tag to the second bookmark.");
is(document.getElementById("editBMPanel_tagsField").value, TEST_TAG,
"Editing multiple bookmarks should show matching tags.");
checkTagsSelector([TEST_TAG], [TEST_TAG]);
// Remove tag from the first bookmark.
PlacesUtils.tagging.untagURI(TEST_URI, [TEST_TAG]);
is(PlacesUtils.tagging.getTagsForURI(TEST_URI)[0], undefined,
"Correctly removed tag from the first bookmark.");
is(document.getElementById("editBMPanel_tagsField").value, "",
"Editing multiple bookmarks without matching tags should not show any tag.");
checkTagsSelector([TEST_TAG], []);
// Remove tag from the second bookmark.
PlacesUtils.tagging.untagURI(TEST_URI2, [TEST_TAG]);
is(PlacesUtils.tagging.getTagsForURI(TEST_URI2)[0], undefined,
"Correctly removed tag from the second bookmark.");
is(document.getElementById("editBMPanel_tagsField").value, "",
"Editing multiple bookmarks without matching tags should not show any tag.");
checkTagsSelector([], []);
// Init panel with a nsIURI entry.
gEditItemOverlay.initPanel(TEST_URI);
// Add a tag.
PlacesUtils.tagging.tagURI(TEST_URI, [TEST_TAG]);
is(PlacesUtils.tagging.getTagsForURI(TEST_URI)[0], TEST_TAG,
"Correctly added tag to the first entry.");
is(document.getElementById("editBMPanel_tagsField").value, TEST_TAG,
"Editing a single nsIURI entry shows the added tag");
checkTagsSelector([TEST_TAG], [TEST_TAG]);
// Remove tag.
PlacesUtils.tagging.untagURI(TEST_URI, [TEST_TAG]);
is(PlacesUtils.tagging.getTagsForURI(TEST_URI)[0], undefined,
"Correctly removed tag from the nsIURI entry.");
is(document.getElementById("editBMPanel_tagsField").value, "",
"Editing a single nsIURI entry should not show any tag");
checkTagsSelector([], []);
// Init panel with multiple nsIURI entries.
gEditItemOverlay.initPanel([TEST_URI, TEST_URI2]);
// Add a tag to the first entry.
PlacesUtils.tagging.tagURI(TEST_URI, [TEST_TAG]);
is(PlacesUtils.tagging.getTagsForURI(TEST_URI)[0], TEST_TAG,
"Tag correctly added.");
is(document.getElementById("editBMPanel_tagsField").value, "",
"Editing multiple nsIURIs without matching tags should not show any tag.");
checkTagsSelector([TEST_TAG], []);
// Add a tag to the second entry.
PlacesUtils.tagging.tagURI(TEST_URI2, [TEST_TAG]);
is(PlacesUtils.tagging.getTagsForURI(TEST_URI2)[0], TEST_TAG,
"Tag correctly added.");
is(document.getElementById("editBMPanel_tagsField").value, TEST_TAG,
"Editing multiple nsIURIs should show matching tags");
checkTagsSelector([TEST_TAG], [TEST_TAG]);
// Remove tag from the first entry.
PlacesUtils.tagging.untagURI(TEST_URI, [TEST_TAG]);
is(PlacesUtils.tagging.getTagsForURI(TEST_URI)[0], undefined,
"Correctly removed tag from the first entry.");
is(document.getElementById("editBMPanel_tagsField").value, "",
"Editing multiple nsIURIs without matching tags should not show any tag.");
checkTagsSelector([TEST_TAG], []);
// Remove tag from the second entry.
PlacesUtils.tagging.untagURI(TEST_URI2, [TEST_TAG]);
is(PlacesUtils.tagging.getTagsForURI(TEST_URI2)[0], undefined,
"Correctly removed tag from the second entry.");
is(document.getElementById("editBMPanel_tagsField").value, "",
"Editing multiple nsIURIs without matching tags should not show any tag.");
checkTagsSelector([], []);
// Cleanup. // Cleanup.
PlacesUtils.bookmarks.removeItem(itemId); PlacesUtils.bookmarks.removeFolderChildren(
PlacesUtils.unfiledBookmarksFolderId
);
} }
]]> ]]>
</script> </script>

View File

@ -941,6 +941,11 @@ nsNavBookmarks::InsertBookmark(PRInt64 aFolder,
if (bookmarks.Length()) { if (bookmarks.Length()) {
for (PRUint32 i = 0; i < bookmarks.Length(); i++) { for (PRUint32 i = 0; i < bookmarks.Length(); i++) {
// Don't notify to the same tag entry we just added.
if (bookmarks[i] == *aNewBookmarkId) {
continue;
}
NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
nsINavBookmarkObserver, nsINavBookmarkObserver,
OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("tags"), OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("tags"),
@ -1042,37 +1047,40 @@ nsNavBookmarks::RemoveItem(PRInt64 aItemId)
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
bool isTagEntry = false;
if (itemType == TYPE_BOOKMARK) {
// Check if the removed bookmark was child of a tag container.
// This is done before notifying since during the notification the parent
// could be removed as well.
PRInt64 grandParentId;
rv = GetFolderIdForItem(folderId, &grandParentId);
NS_ENSURE_SUCCESS(rv, rv);
isTagEntry = grandParentId == mTagsRoot;
}
NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
nsINavBookmarkObserver, nsINavBookmarkObserver,
OnItemRemoved(aItemId, folderId, childIndex, itemType)); OnItemRemoved(aItemId, folderId, childIndex, itemType));
if (itemType == TYPE_BOOKMARK) { if (isTagEntry) {
// If the removed bookmark was a child of a tag container, notify all // Get all bookmarks pointing to the same uri as this tag entry and
// bookmark-folder result nodes which contain a bookmark for the removed // notify them that tags changed.
// bookmark's url. nsCOMPtr<nsIURI> uri;
PRInt64 grandParentId; rv = NS_NewURI(getter_AddRefs(uri), spec);
rv = GetFolderIdForItem(folderId, &grandParentId); NS_ENSURE_SUCCESS(rv, rv);
nsTArray<PRInt64> bookmarks;
rv = GetBookmarkIdsForURITArray(uri, bookmarks);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
if (grandParentId == mTagsRoot) {
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), spec);
NS_ENSURE_SUCCESS(rv, rv);
nsTArray<PRInt64> bookmarks;
rv = GetBookmarkIdsForURITArray(uri, bookmarks); for (PRUint32 i = 0; i < bookmarks.Length(); i++) {
NS_ENSURE_SUCCESS(rv, rv); NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
nsINavBookmarkObserver,
if (bookmarks.Length()) { OnItemChanged(bookmarks[i],
for (PRUint32 i = 0; i < bookmarks.Length(); i++) { NS_LITERAL_CSTRING("tags"), PR_FALSE,
NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, EmptyCString(), 0, TYPE_BOOKMARK));
nsINavBookmarkObserver,
OnItemChanged(bookmarks[i],
NS_LITERAL_CSTRING("tags"), PR_FALSE,
EmptyCString(), 0, TYPE_BOOKMARK));
}
}
} }
} }
return NS_OK; return NS_OK;
} }

View File

@ -226,13 +226,13 @@ TaggingService.prototype = {
var result = this._getTagResult(aTagId); var result = this._getTagResult(aTagId);
if (!result) if (!result)
return; return;
var node = result.root; var node = PlacesUtils.asContainer(result.root);
node.QueryInterface(Ci.nsINavHistoryContainerResultNode);
node.containerOpen = true; node.containerOpen = true;
var cc = node.childCount; var cc = node.childCount;
node.containerOpen = false; node.containerOpen = false;
if (cc == 0) if (cc == 0) {
PlacesUtils.bookmarks.removeItem(node.itemId); PlacesUtils.bookmarks.removeItem(aTagId);
}
}, },
// nsITaggingService // nsITaggingService
@ -263,7 +263,6 @@ TaggingService.prototype = {
if (itemId != -1) { if (itemId != -1) {
// There is a tagged item. // There is a tagged item.
PlacesUtils.bookmarks.removeItem(itemId); PlacesUtils.bookmarks.removeItem(itemId);
this._removeTagIfEmpty(tag.id);
} }
} }
}, taggingService); }, taggingService);
@ -432,6 +431,11 @@ TaggingService.prototype = {
if (tagIds) if (tagIds)
this.untagURI(itemURI, tagIds); this.untagURI(itemURI, tagIds);
} }
// Item is a tag entry. If this was the last entry for this tag, remove it.
else if (itemURI && this._tagFolders[aFolderId]) {
this._removeTagIfEmpty(aFolderId);
}
}, },
onItemChanged: function(aItemId, aProperty, aIsAnnotationProperty, aNewValue, onItemChanged: function(aItemId, aProperty, aIsAnnotationProperty, aNewValue,