Bug 385266 - New starring, bookmarking and tagging UI. This is work-in-progress and not yet enabled for anything but the star button itself. Smart folders are not yet hooked up either. r=dietrich.
authormozilla.mano@sent.com
Wed, 15 Aug 2007 18:15:50 -0700
changeset 4710 080d227fff72f4b5602c9aa1ed264ad4d75cdb38
parent 4709 d0ae0ad6801559d1781f674029d1526b8a7ef5c5
child 4711 5417417039f95676a9f7e57d75cb8f05566a1e2a
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdietrich
bugs385266
milestone1.9a8pre
Bug 385266 - New starring, bookmarking and tagging UI. This is work-in-progress and not yet enabled for anything but the star button itself. Smart folders are not yet hooked up either. r=dietrich.
browser/base/content/browser-places.js
browser/base/content/browser.js
browser/base/content/browser.xul
browser/components/places/content/editBookmarkOverlay.js
browser/components/places/content/editBookmarkOverlay.xul
browser/components/places/content/treeView.js
browser/components/places/content/utils.js
browser/components/places/jar.mn
browser/locales/en-US/chrome/browser/places/editBookmarkOverlay.dtd
browser/locales/jar.mn
browser/themes/pinstripe/browser/browser.css
browser/themes/pinstripe/browser/jar.mn
browser/themes/pinstripe/browser/places/editBookmarkOverlay.css
browser/themes/pinstripe/browser/places/pageStarred.png
browser/themes/pinstripe/browser/places/starPage.png
browser/themes/winstripe/browser/browser.css
browser/themes/winstripe/browser/jar.mn
browser/themes/winstripe/browser/places/editBookmarkOverlay.css
browser/themes/winstripe/browser/places/pageStarred.png
browser/themes/winstripe/browser/places/starPage.png
toolkit/components/places/public/nsITaggingService.idl
toolkit/components/places/src/nsTaggingService.js
toolkit/components/places/tests/unit/test_tagging.js
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -587,8 +587,146 @@ var PlacesMenuDNDController = {
   // Whether or not drag and drop to menus is supported on this platform
   // Dragging in menus is disabled on OS X due to various repainting issues.
 #ifdef XP_MACOSX
   _dragSupported: false
 #else
   _dragSupported: true
 #endif
 };
+
+var PlacesStarButton = {
+  init: function PSB_init() {
+    PlacesUtils.bookmarks.addObserver(this, false);
+  },
+
+  uninit: function PSB_uninit() {
+    PlacesUtils.bookmarks.removeObserver(this);
+  },
+
+  QueryInterface: function PSB_QueryInterface(aIID) {
+    if (aIID.equals(Ci.nsIDOMEventListener) ||
+        aIID.equals(Ci.nsINavBookmarkObserver) ||
+        aIID.equals(Ci.nsISupports))
+      return this;
+
+    throw Cr.NS_NOINTERFACE;
+  },
+
+  get panel() {
+    return document.getElementById("editBookmarkPanel");
+  },
+
+  _starred: false,
+  _batching: false,
+
+  updateState: function PSB_updateState() {
+    var uri = getBrowser().currentURI;
+    this._starred = uri && PlacesUtils.bookmarks.isBookmarked(uri);
+    if (this._starred)
+      document.getElementById("star-icon").setAttribute("starred", "true");
+    else
+      document.getElementById("star-icon").removeAttribute("starred");
+  },
+
+  _star: function PSB_star(aBrowser) {
+    var uri = aBrowser.currentURI;
+    if (!uri)
+      throw "No URL";
+
+    var title = PlacesUtils.history.getPageTitle(uri);
+
+    var descAnno = {
+      name: DESCRIPTION_ANNO,
+      value: PlacesUtils.getDescriptionFromDocument(aBrowser.contentDocument)
+    };
+    var txn = PlacesUtils.ptm.createItem(uri, PlacesUtils.placesRootId, -1,
+                                         title, null, [descAnno]);
+    PlacesUtils.ptm.commitTransaction(txn);
+  },
+
+  // nsIDOMEventListener
+  handleEvent: function PSB_handleEvent(aEvent) {
+    if (aEvent.originalTarget != this.panel)
+      return;
+
+    // This only happens for auto-hide. When the panel is closed from within
+    // itself, doneCallback removes the listener and only then hides the popup
+    gAddBookmarksPanel.saveItem();
+    gAddBookmarksPanel.uninitPanel();
+  },
+
+  showBookmarkPagePopup: function PSB_showBookmarkPagePopup(aBrowser) {
+    const bms = PlacesUtils.bookmarks;
+
+    var dockTo = document.getElementById("star-icon");
+    if (!dockTo)
+      dockTo = getBrowser();
+
+    var panel = this.panel;
+    panel.showPopup(dockTo, -1, -1, "popup", "bottomright", "topright");
+
+    var uri = aBrowser.currentURI;
+
+    var itemId = -1;
+    var bmkIds = bms.getBookmarkIdsForURI(uri, {});
+    for each (var bk in bmkIds) {
+      // Find the first folder which isn't a tag container
+      var folder = bms.getFolderIdForItem(bk);
+      if (folder == PlacesUtils.placesRootId ||
+          bms.getFolderIdForItem(folder) != PlacesUtils.tagRootId) {
+        itemId = bk;
+        break;
+      }
+    }
+    if (itemId == -1) {
+      // if we're called before the URI is bookmarked, or if the remaining
+      // items for this url are under tag containers, star the page first
+      itemId = this._star(aBrowser);
+    }
+    gAddBookmarksPanel.initPanel(itemId, PlacesUtils.tm, this.doneCallback,
+                                 { hiddenRows: "description" });
+    panel.addEventListener("popuphiding", this, false);
+  },
+
+  onClick: function PSB_onClick(aEvent) {
+    if (this._starred)
+      this.showBookmarkPagePopup(getBrowser());
+    else
+      this._star(getBrowser());
+  },
+
+  doneCallback: function PSB_doneCallback(aSavedChanges) {
+    var panel = PlacesStarButton.panel;
+    panel.removeEventListener("popuphiding", PlacesStarButton, false);
+    gAddBookmarksPanel.uninitPanel();
+    panel.hidePopup();
+  },
+
+  // nsINavBookmarkObserver  
+  onBeginUpdateBatch: function PSB_onBeginUpdateBatch() {
+    this._batching = true;
+  },
+
+  onEndUpdateBatch: function PSB_onEndUpdateBatch() {
+    this.updateState();
+    this._batching = false;
+  },
+  
+  onItemAdded: function PSB_onItemAdded(aItemId, aFolder, aIndex) {
+    if (!this._batching && !this._starred)
+      this.updateState();
+  },
+
+  onItemRemoved: function PSB_onItemRemoved(aItemId, aFolder, aIndex) {
+    if (!this._batching)
+      this.updateState();
+  },
+
+  onItemChanged: function PSB_onItemChanged(aItemId, aProperty,
+                                            aIsAnnotationProperty, aValue) {
+    if (!this._batching && aProperty == "uri")
+      this.updateState();
+  },
+
+  onItemVisited: function() { },
+  onItemMoved: function() { }
+};
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1012,16 +1012,17 @@ function delayedStartup()
 
   // add bookmark options to context menu for tabs
   addBookmarkMenuitems();
 
   PlacesMenuDNDController.init();
 
   initBookmarksToolbar();
   PlacesUtils.bookmarks.addObserver(gBookmarksObserver, false);
+  PlacesStarButton.init();
 
   // called when we go into full screen, even if it is
   // initiated by a web page script
   window.addEventListener("fullscreen", onFullScreen, true);
 
   if (gIsLoadingBlank && gURLBar && isElementVisible(gURLBar))
     focusElement(gURLBar);
   else
@@ -1152,16 +1153,17 @@ function BrowserShutdown()
   os.removeObserver(gXPInstallObserver, "xpinstall-install-blocked");
 
   try {
     gBrowser.removeProgressListener(window.XULBrowserWindow);
   } catch (ex) {
   }
 
   PlacesUtils.bookmarks.removeObserver(gBookmarksObserver);
+  PlacesStarButton.uninit();
 
   try {
     gPrefService.removeObserver(gAutoHideTabbarPrefListener.domain,
                                 gAutoHideTabbarPrefListener);
     gPrefService.removeObserver(gHomeButton.prefDomain, gHomeButton);
   } catch (ex) {
     Components.utils.reportError(ex);
   }
@@ -3514,16 +3516,19 @@ nsBrowserStatusHandler.prototype =
 
           // Setting the urlBar value in some cases causes userTypedValue to
           // become set because of oninput, so reset it to its old value.
           browser.userTypedValue = userTypedValue;
         } else {
           gURLBar.value = userTypedValue;
           SetPageProxyState("invalid");
         }
+
+        // Update starring UI
+        PlacesStarButton.updateState(aLocationURI);
       }
     }
     UpdateBackForwardCommands(gBrowser.webNavigation);
 
     if (gFindBar.findMode != gFindBar.FIND_NORMAL) {
       // Close the Find toolbar if we're in old-style TAF mode
       gFindBar.close();
     }
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -48,16 +48,17 @@
 
 <?xml-stylesheet href="chrome://browser/skin/" type="text/css"?> 
 
 <?xml-stylesheet href="chrome://global/skin/toolbar.css" type="text/css"?>
 
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/baseMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
+<?xul-overlay href="chrome://browser/content/places/editBookmarkOverlay.xul"?>
 
 # All DTD information is stored in a separate file so that it can be shared by
 # hiddenWindow.xul.
 #include browser-doctype.inc
  
 <window id="main-window"
         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
@@ -95,16 +96,21 @@
            position="after_start"
            onpopupshowing="return BrowserForwardMenu(event);"
            oncommand="gotoHistoryIndex(event);"
            onclick="checkForMiddleClick(this, event);"/>
     <tooltip id="aHTMLTooltip" onpopupshowing="return FillInHTMLTooltip(document.tooltipNode);"/>
 
     <panel type="autocomplete" chromedir="&locale.dir;" id="PopupAutoComplete" noautofocus="true"/>
 
+    <panel id="editBookmarkPanel"
+           position="after_end">
+      <vbox id="editBookmarkPanelContent" flex="1"/>
+    </panel>
+
     <popup id="toolbar-context-menu"
            onpopupshowing="onViewToolbarsPopupShowing(event);">
       <menuseparator/>
       <menuitem command="cmd_CustomizeToolbars"
                 label="&viewCustomizeToolbar.label;"
                 accesskey="&viewCustomizeToolbar.accesskey;"/>
     </popup>
 
@@ -244,16 +250,17 @@
                              onclick="checkForMiddleClick(this, event);"/>
                 </button>
                 <image id="lock-icon" onclick="if (event.button == 0) displaySecurityInfo(); event.stopPropagation();"/>
 #ifdef MOZ_SAFE_BROWSING
                 <image id="safebrowsing-urlbar-icon" tooltiptext="&safeb.urlbaricon.tooltip;"
                        level="safe"
                        onclick="goDoCommand('safebrowsing-show-warning')" />
 #endif
+                <image id="star-icon" onclick="if (event.button == 0) PlacesStarButton.onClick(event);"/>
               </hbox>
             </textbox>
             <stack id="go-button-stack">
               <vbox>
                 <!-- These image segments allow the button to stretch nicely
                      in larger urlbars. -->
                 <image id="go-button-top"
                        class="go-button-background"
new file mode 100644
--- /dev/null
+++ b/browser/components/places/content/editBookmarkOverlay.js
@@ -0,0 +1,581 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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 the Places Bookmark Properties dialog.
+ *
+ * The Initial Developer of the Original Code is Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Asaf Romano <mano@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const LAST_USED_ANNO = "bookmarkPropertiesDialog/lastUsed";
+const MAX_FOLDER_ITEM_IN_MENU_LIST = 5;
+
+var gAddBookmarksPanel = {
+  /**
+   * The Microsummary Service for displaying microsummaries.
+   */
+  __mss: null,
+  get _mss() {
+    if (!this.__mss)
+      this.__mss = Cc["@mozilla.org/microsummary/service;1"].
+                  getService(Ci.nsIMicrosummaryService);
+    return this.__mss;
+  },
+
+  _uri: null,
+  _itemId: -1,
+  _itemType: -1,
+  _microsummaries: null,
+  _doneCallback: null,
+  _currentTags: [],
+  _hiddenRows: [],
+
+  /**
+   * Determines the initial data for the item edited or added by this dialog
+   */
+  _determineInfo: function ABP__determineInfo(aInfo) {
+    const bms = PlacesUtils.bookmarks;
+    this._itemType = bms.getItemType(this._itemId);
+    if (this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK)
+      this._currentTags = PlacesUtils.tagging.getTagsForURI(this._uri);
+    else
+      this._currentTags.splice(0);
+
+    // hidden rows
+    if (aInfo && aInfo.hiddenRows)
+      this._hiddenRows = aInfo.hiddenRows;
+    else
+      this._hiddenRows.splice(0);
+  },
+
+  _showHideRows: function EBP__showHideRows() {
+    this._element("nameRow").hidden = this._hiddenRows.indexOf("name") != -1;
+    this._element("folderRow").hidden =
+      this._hiddenRows.indexOf("folderPicker") != -1;
+    this._element("tagsRow").hidden = this._hiddenRows.indexOf("tags") != -1 ||
+      this._itemType != Ci.nsINavBookmarksService.TYPE_BOOKMARK;
+    this._element("descriptionRow").hidden =
+      this._hiddenRows.indexOf("description") != -1;
+  },
+
+  /**
+   * Initialize the panel
+   */
+  initPanel: function ABP_initPanel(aItemId, aTm, aDoneCallback, aInfo) {
+    this._folderMenuList = this._element("folderMenuList");
+    this._folderTree = this._element("folderTree");
+    this._tm = aTm;
+    this._itemId = aItemId;
+    this._uri = PlacesUtils.bookmarks.getBookmarkURI(this._itemId);
+    this._doneCallback = aDoneCallback;
+    this._determineInfo(aInfo);
+
+    // folder picker
+    this._initFolderMenuList();
+    
+    // name picker
+    this._initNamePicker();
+
+    // tags field
+    this._element("tagsField").value = this._currentTags.join(", ");
+
+    // description field
+    this._element("descriptionField").value =
+      PlacesUtils.getItemDescription(this._itemId);
+
+    this._showHideRows();
+  },
+
+  /**
+   * Appends a menu-item representing a bookmarks folder to a menu-popup.
+   * @param aMenupopup
+   *        The popup to which the menu-item should be added.
+   * @param aFolderId
+   *        The identifier of the bookmarks folder.
+   * @return the new menu item.
+   */
+  _appendFolderItemToMenupopup:
+  function BPP__appendFolderItemToMenuList(aMenupopup, aFolderId) {
+    // First make sure the folders-separator is visible
+    this._element("foldersSeparator").hidden = false;
+
+    var folderMenuItem = document.createElement("menuitem");
+    var folderTitle = PlacesUtils.bookmarks.getItemTitle(aFolderId)
+    folderMenuItem.folderId = aFolderId;
+    folderMenuItem.setAttribute("label", folderTitle);
+    folderMenuItem.className = "menuitem-iconic folder-icon";
+    aMenupopup.appendChild(folderMenuItem);
+    return folderMenuItem;
+  },
+
+  _initFolderMenuList: function BPP__initFolderMenuList() {
+    // clean up first
+    var menupopup = this._folderMenuList.menupopup;
+    while (menupopup.childNodes.length > 4)
+      menupopup.removeChild(menupopup.lastChild);
+
+    var container = PlacesUtils.bookmarks.getFolderIdForItem(this._itemId);
+
+    // only show "All Bookmarks" if the url isn't bookmarked somewhere else
+    this._element("placesRootItem").hidden = container != PlacesUtils.placesRootId;
+
+    // List of recently used folders:
+    var annos = PlacesUtils.annotations;
+    var folderIds = annos.getItemsWithAnnotation(LAST_USED_ANNO, { });
+
+    /**
+     * The value of the LAST_USED_ANNO annotation is the time (in the form of
+     * Date.getTime) at which the folder has been last used.
+     *
+     * First we build the annotated folders array, each item has both the
+     * folder identifier and the time at which it was last-used by this dialog
+     * set. Then we sort it descendingly based on the time field.
+     */
+    var folders = [];
+    for (var i=0; i < folderIds.length; i++) {
+      var lastUsed = annos.getItemAnnotation(folderIds[i], LAST_USED_ANNO);
+      folders.push({ folderId: folderIds[i], lastUsed: lastUsed });
+    }
+    folders.sort(function(a, b) {
+      if (b.lastUsed < a.lastUsed)
+        return -1;
+      if (b.lastUsed > a.lastUsed)
+        return 1;
+      return 0;
+    });
+
+    var numberOfItems = Math.min(MAX_FOLDER_ITEM_IN_MENU_LIST, folders.length);
+    for (i=0; i < numberOfItems; i++) {
+      this._appendFolderItemToMenupopup(menupopup, folders[i].folderId);
+    }
+
+    var defaultItem = this._getFolderMenuItem(container, true);
+    this._folderMenuList.selectedItem = defaultItem;
+
+    // Hide the folders-separator if no folder is annotated as recently-used
+    this._element("foldersSeparator").hidden = (menupopup.childNodes.length <= 4);
+  },
+
+  QueryInterface: function BPP_QueryInterface(aIID) {
+    if (aIID.equals(Ci.nsIMicrosummaryObserver) ||
+        aIID.equals(Ci.nsIDOMEventListener) ||
+        aIID.eqauls(Ci.nsISupports))
+      return this;
+
+    throw Cr.NS_ERROR_NO_INTERFACE;
+  },
+
+  _element: function BPP__element(aID) {
+    return document.getElementById("editBMPanel_" + aID);
+  },
+
+  _createMicrosummaryMenuItem:
+  function BPP__createMicrosummaryMenuItem(aMicrosummary) {
+    var menuItem = document.createElement("menuitem");
+
+    // Store a reference to the microsummary in the menu item, so we know
+    // which microsummary this menu item represents when it's time to
+    // save changes or load its content.
+    menuItem.microsummary = aMicrosummary;
+
+    // Content may have to be generated asynchronously; we don't necessarily
+    // have it now.  If we do, great; otherwise, fall back to the generator
+    // name, then the URI, and we trigger a microsummary content update. Once
+    // the update completes, the microsummary will notify our observer to
+    // update the corresponding menu-item.
+    // XXX Instead of just showing the generator name or (heaven forbid)
+    // its URI when we don't have content, we should tell the user that
+    // we're loading the microsummary, perhaps with some throbbing to let
+    // her know it is in progress.
+    if (aMicrosummary.content)
+      menuItem.setAttribute("label", aMicrosummary.content);
+    else {
+      menuItem.setAttribute("label", aMicrosummary.generator.name ||
+                                     aMicrosummary.generator.uri.spec);
+      aMicrosummary.update();
+    }
+
+    return menuItem;
+  },
+
+  _initNamePicker: function ABP_initNamePicker() {
+    var userEnteredNameField = this._element("userEnteredName");
+    var namePicker = this._element("namePicker");
+    var droppable = false;
+
+    userEnteredNameField.label =
+      PlacesUtils.bookmarks.getItemTitle(this._itemId);
+
+    // clean up old entries
+    var menupopup = namePicker.menupopup;
+    while (menupopup.childNodes.length > 2)
+      menupopup.removeChild(menupopup.lastChild);
+
+    var itemToSelect = userEnteredNameField;
+    try {
+      this._microsummaries = this._mss.getMicrosummaries(this._uri, -1);
+    }
+    catch(ex) {
+      // getMicrosummaries will throw an exception in at least two cases:
+      // 1. the bookmarked URI contains a scheme that the service won't
+      //    download for security reasons (currently it only handles http,
+      //    https, and file);
+      // 2. the page to which the URI refers isn't HTML or XML (the only two
+      //    content types the service knows how to summarize).
+      this._microsummaries = null;
+    }
+    if (this._microsummaries) {
+      var enumerator = this._microsummaries.Enumerate();
+
+      if (enumerator.hasMoreElements()) {
+        // Show the drop marker if there are microsummaries
+        droppable = true;
+        while (enumerator.hasMoreElements()) {
+          var microsummary = enumerator.getNext()
+                                       .QueryInterface(Ci.nsIMicrosummary);
+          var menuItem = this._createMicrosummaryMenuItem(microsummary);
+          menupopup.appendChild(menuItem);
+        }
+      }
+
+      this._microsummaries.addObserver(this);
+    }
+
+    if (namePicker.selectedItem == itemToSelect)
+      namePicker.value = itemToSelect.label;
+    else
+      namePicker.selectedItem = itemToSelect;
+
+    namePicker.setAttribute("droppable", droppable);
+  },
+
+  // nsIMicrosummaryObserver
+  onContentLoaded: function ABP_onContentLoaded(aMicrosummary) {
+    var namePicker = this._element("namePicker");
+    var childNodes = namePicker.menupopup.childNodes;
+
+    // 0: user-entered item; 1: separator
+    for (var i = 2; i < childNodes.length; i++) {
+      if (childNodes[i].microsummary == aMicrosummary) {
+        var newLabel = aMicrosummary.content;
+        // XXXmano: non-editable menulist would do this for us, see bug 360220
+        // We should fix editable-menulists to set the DOMAttrModified handler
+        // as well.
+        //
+        // Also note the order importance: if the label of the menu-item is
+        // set to something different than the menulist's current value,
+        // the menulist no longer has selectedItem set
+        if (namePicker.selectedItem == childNodes[i])
+          namePicker.value = newLabel;
+
+        childNodes[i].label = newLabel;
+        return;
+      }
+    }
+  },
+
+  onElementAppended: function BPP_onElementAppended(aMicrosummary) {
+    var namePicker = this._element("namePicker");
+    namePicker.menupopup
+              .appendChild(this._createMicrosummaryMenuItem(aMicrosummary));
+
+    // Make sure the drop-marker is shown
+    namePicker.setAttribute("droppable", "true");
+  },
+
+  uninitPanel: function ABP_uninitPanel() {
+    if (this._microsummaries)
+      this._microsummaries.removeObserver(this);
+
+    // hide the folder tree if it was previously visible
+    if (!this._folderTree.collapsed)
+      this.toggleFolderTreeVisibility();
+
+    // hide the tag selector if it was previously visible
+    var tagsSelector = this._element("tagsSelector");
+    if (!tagsSelector.collapsed)
+      tagsSelector.collapsed = true;
+  },
+
+  saveItem: function ABP_saveItem() {
+    var container = this._getFolderIdFromMenuList();
+    const bms = PlacesUtils.bookmarks;
+    const ptm = PlacesUtils.ptm;
+    var txns = [];
+
+    // container
+    if (bms.getFolderIdForItem(this._itemId) != container)
+      txns.push(ptm.moveItem(this._itemId, container, -1));
+
+    // title
+    var newTitle = this._element("userEnteredName").label;
+    if (bms.getItemTitle(this._itemId) != newTitle)
+      txns.push(ptm.editItemTitle(this._itemId, newTitle));
+
+    // description
+    var newDescription = this._element("descriptionField").value;
+    if (newDescription != PlacesUtils.getItemDescription(this._itemId))
+      txns.push(ptm.editItemDescription(this._itemId, newDescription));
+
+    // Tags, NOT YET UNDOABLE
+    var tags = this._getTagsArrayFromTagField();
+    if (tags.length > 0 || this._currentTags.length > 0) {
+      var tagsToRemove = [];
+      var tagsToAdd = [];
+      var t;
+      for each (t in this._currentTags) {
+        if (tags.indexOf(t) == -1)
+          tagsToRemove.push(t);
+      }
+      for each (t in tags) {
+        if (this._currentTags.indexOf(t) == -1)
+          tagsToAdd.push(t);
+      }
+
+      if (tagsToAdd.length > 0)
+        PlacesUtils.tagging.tagURI(this._uri, tagsToAdd);
+      if (tagsToRemove.length > 0)
+        PlacesUtils.tagging.untagURI(this._uri, tagsToRemove);
+    }
+
+    if (txns.length > 0) {
+      // Mark the containing folder as recently-used if it isn't the
+      // "All Bookmarks" root
+      if (container != PlacesUtils.placesRootId)
+        this._markFolderAsRecentlyUsed(container);
+    }
+
+    if (txns.length > 0)
+      ptm.commitTransaction(ptm.aggregateTransactions("Edit Item", txns));
+  },
+
+  onNamePickerInput: function ABP_onNamePickerInput() {
+    this._element("userEnteredName").label = this._element("namePicker").value;
+  },
+
+  toggleFolderTreeVisibility: function ABP_toggleFolderTreeVisibility() {
+    var expander = this._element("foldersExpander");
+    if (!this._folderTree.collapsed) {
+      expander.className = "expander-down";
+      expander.setAttribute("tooltiptext",
+                            expander.getAttribute("tooltiptextdown"));
+      this._folderTree.collapsed = true;
+    }
+    else {
+      expander.className = "expander-up"
+      expander.setAttribute("tooltiptext",
+                            expander.getAttribute("tooltiptextup"));
+      if (!this._folderTree.treeBoxObject.view.isContainerOpen(0))
+        this._folderTree.treeBoxObject.view.toggleOpenState(0);
+      this._folderTree.selectFolders([this._getFolderIdFromMenuList()]);
+      this._folderTree.collapsed = false;
+      this._folderTree.focus();
+    }
+  },
+
+  _getFolderIdFromMenuList:
+  function BPP__getFolderIdFromMenuList() {
+    var selectedItem = this._folderMenuList.selectedItem
+    switch (selectedItem.id) {
+      case "editBMPanel_placesRootItem":
+        return PlacesUtils.placesRootId;
+      case "editBMPanel_bmRootItem":
+        return PlacesUtils.bookmarksRootId;
+      case "editBMPanel_toolbarFolderItem":
+        return PlacesUtils.toolbarFolderId;
+    }
+
+    NS_ASSERT("folderId" in selectedItem,
+              "Invalid menuitem in the folders-menulist");
+    return selectedItem.folderId;
+  },
+
+  /**
+   * Get the corresponding menu-item in the folder-menu-list for a bookmarks
+   * folder if such an item exists. Otherwise, this creates a menu-item for the
+   * folder. If the items-count limit (see MAX_FOLDERS_IN_MENU_LIST) is reached,
+   * the new item replaces the last menu-item.
+   * @param aFolderId
+   *        The identifier of the bookmarks folder
+   * @param aCheckStaticFolderItems
+   *        whether or not to also treat the static items at the top of
+   *        menu-list. Note dynamic items get precedence even if this is set to
+   *        true.
+   */
+  _getFolderMenuItem:
+  function BPP__getFolderMenuItem(aFolderId, aCheckStaticFolderItems) {
+    var menupopup = this._folderMenuList.menupopup;
+
+    // 0: All Bookmarks, 1: Bookmarks root, 2: toolbar folder, 3: separator
+    for (var i=4;  i < menupopup.childNodes.length; i++) {
+      if (menupopup.childNodes[i].folderId == aFolderId)
+        return menupopup.childNodes[i];
+    }
+
+    if (aCheckStaticFolderItems) {
+      if (aFolderId == PlacesUtils.placesRootId)
+        return this._element("placesRootItem");
+      if (aFolderId == PlacesUtils.bookmarksRootId)
+        return this._element("bmRootItem")
+      if (aFolderId == PlacesUtils.toolbarFolderId)
+        return this._element("toolbarFolderItem")
+    }
+
+    // 3 special folders + separator + folder-items-count limit
+    if (menupopup.childNodes.length == 4 + MAX_FOLDER_ITEM_IN_MENU_LIST)
+      menupopup.removeChild(menupopup.lastChild);
+
+    return this._appendFolderItemToMenupopup(menupopup, aFolderId);
+  },
+
+  onMenuListFolderSelect: function BPP_onMenuListFolderSelect(aEvent) {
+    if (this._folderTree.hidden)
+      return;
+
+    this._folderTree.selectFolders([this._getFolderIdFromMenuList()]);
+  },
+
+  onFolderTreeSelect: function BPP_onFolderTreeSelect() {
+    var selectedNode = this._folderTree.selectedNode;
+    if (!selectedNode)
+      return;
+
+    var folderId = selectedNode.itemId;
+    // Don't set the selected item if the static item for the folder is
+    // already selected
+    var oldSelectedItem = this._folderMenuList.selectedItem;
+    if ((oldSelectedItem.id == "editBMPanel_toolbarFolderItem" &&
+         folderId == PlacesUtils.bookmarks.toolbarFolder) ||
+        (oldSelectedItem.id == "editBMPanel_bmRootItem" &&
+         folderId == PlacesUtils.bookmarks.bookmarksRoot))
+      return;
+
+    var folderItem = this._getFolderMenuItem(folderId, false);
+    this._folderMenuList.selectedItem = folderItem;
+  },
+
+  _markFolderAsRecentlyUsed:
+  function ABP__markFolderAsRecentlyUsed(aFolderId) {
+    // We'll figure out when/if to expire the annotation if it turns out
+    // we keep this recently-used-folders implementation
+    PlacesUtils.annotations
+               .setItemAnnotation(aFolderId, LAST_USED_ANNO,
+                                  new Date().getTime(), 0,
+                                  Ci.nsIAnnotationService.EXPIRE_NEVER);
+  },
+
+  accept: function ABP_accept() {
+    this.saveItem();
+    if (typeof(this._doneCallback) == "function")
+      this._doneCallback();
+  },
+
+  deleteAndClose: function ABP_deleteAndClose() {
+    // remove the item
+    if (this._itemId != -1)
+      PlacesUtils.bookmarks.removeItem(this._itemId);
+
+    // remove all tags for the associated url
+    PlacesUtils.tagging.untagURI(this._uri, null);
+
+    if (typeof(this._doneCallback) == "function")
+      this._doneCallback();
+  },
+
+  toggleTagsSelector: function ABP_toggleTagsSelector() {
+    var tagsSelector = this._element("tagsSelector");
+    var expander = this._element("tagsSelectorExpander");
+    if (tagsSelector.collapsed) {
+      expander.className = "expander-down";
+      expander.setAttribute("tooltiptext",
+                            expander.getAttribute("tooltiptextdown"));
+
+      // rebuild the tag list
+      while (tagsSelector.hasChildNodes())
+        tagsSelector.removeChild(tagsSelector.lastChild);
+
+      var tagsInField = this._getTagsArrayFromTagField();
+      var allTags = PlacesUtils.tagging.allTags;
+      for each (var tag in allTags) {
+        var elt = document.createElement("listitem");
+        elt.setAttribute("type", "checkbox");
+        elt.setAttribute("label", tag);
+        if (tagsInField.indexOf(tag) != -1)
+          elt.setAttribute("checked", "true");
+
+        tagsSelector.appendChild(elt);
+      }
+
+      // This is a no-op if we've added the listener.
+      tagsSelector.addEventListener("CheckboxStateChange", this, false);
+    }
+    else {
+      expander.className = "expander-down";
+      expander.setAttribute("tooltiptext",
+                            expander.getAttribute("tooltiptextdown"));
+    }
+
+    tagsSelector.collapsed = !tagsSelector.collapsed;
+  },
+
+  _getTagsArrayFromTagField: function() {
+    // we don't require the leading space (after each comma)
+    var tags = this._element("tagsField").value.split(",");
+    for (var i=0; i < tags.length; i++) {
+      // remove trailing and leading spaces
+      tags[i] = tags[i].replace(/^\s+/, "").replace(/\s+$/, "");
+
+      // remove empty entries from the array.
+      if (tags[i] == "") {
+        tags.splice(i, 1);
+        i--;
+      }
+    }
+    return tags;
+  },
+
+  // nsIDOMEventListener
+  handleEvent: function ABP_nsIDOMEventListener(aEvent) {
+    if (aEvent.type == "CheckboxStateChange") {
+      // Update the tags field when items are checked/unchecked in the listbox
+      var tags = this._getTagsArrayFromTagField();
+
+      if (aEvent.target.checked)
+        tags.push(aEvent.target.label);
+      else {
+        var indexOfItem = tags.indexOf(aEvent.target.label);
+        if (indexOfItem != -1)
+          tags.splice(indexOfItem, 1);
+      }
+      this._element("tagsField").value = tags.join(", ");
+    }
+  }
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/places/content/editBookmarkOverlay.xul
@@ -0,0 +1,150 @@
+# ***** 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 the Places Edit Bookmarks Panel code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Corporation.
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Asaf Romano <mano@mozilla.com> (Original Author)
+#
+# 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 *****
+
+<!DOCTYPE overlay [
+<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/editBookmarkOverlay.dtd">
+%placesDTD;
+]>
+
+<?xml-stylesheet href="chrome://browser/skin/places/editBookmarkOverlay.css"?>
+
+<overlay id="editBookmarkOverlay"
+         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <script type="application/javascript"
+          src="chrome://browser/content/places/editBookmarkOverlay.js"/>
+
+  <vbox id="editBookmarkPanelContent">
+    <grid id="editBookmarkPanelGrid" flex="1">
+      <columns>
+        <column/>
+        <column flex="1"/>
+      </columns>
+      <rows>
+        <row align="center" id="editBMPanel_nameRow">
+          <label value="&editBookmarkOverlay.name.label;"
+                 contorl="editBMPanel_namePicker"/>
+          <menulist id="editBMPanel_namePicker"
+                    flex="1"
+                    editable="true"
+                    droppable="false"
+                    oninput="gAddBookmarksPanel.onNamePickerInput();">
+            <menupopup>
+              <menuitem id="editBMPanel_userEnteredName"/>
+              <menuitem disabled="true">
+                <menuseparator flex="1"/>
+                <label value="&editBookmarkOverlay.liveTitlesSeparator.label;"/>
+              </menuitem>
+            </menupopup>
+          </menulist>
+        </row>
+        <row align="center" id="editBMPanel_folderRow">
+          <label value="&editBookmarkOverlay.folder.label;"
+                 control="editBMPanel_folderMenuList"/>
+          <menulist id="editBMPanel_folderMenuList"
+                    class="folder-icon"
+                    oncommand="gAddBookmarksPanel.onMenuListFolderSelect();">
+            <menupopup>
+              <!-- Static item for special folders -->
+              <menuitem id="editBMPanel_placesRootItem"
+                        label="&editBookmarkOverlay.allBookmarksFolderItem.label;"
+                        class="menuitem-iconic folder-icon"/>
+              <menuitem id="editBMPanel_bmRootItem"
+                        label="&editBookmarkOverlay.bookmarksMenuFolderItem.label;"
+                        class="menuitem-iconic folder-icon"/>
+              <menuitem id="editBMPanel_toolbarFolderItem"
+                        label="&editBookmarkOverlay.toolbarFolderItem.label;"
+                        class="menuitem-iconic folder-icon"/>
+              <menuseparator id="editBMPanel_foldersSeparator" hidden="true"/>
+            </menupopup>
+          </menulist>
+          <button id="editBMPanel_foldersExpander"
+                  class="expander-down"
+                  tooltiptext="&editBookmarkOverlay.foldersExpanderDown.tooltip;"
+                  tooltiptextdown="&editBookmarkOverlay.foldersExpanderDown.tooltip;"
+                  tooltiptextup="&editBookmarkOverlay.expanderUp.tooltip;"
+                  oncommand="gAddBookmarksPanel.toggleFolderTreeVisibility();"/>
+        </row>
+        <tree id="editBMPanel_folderTree"
+              class="placesTree"
+              type="places"
+              height="150"
+              collapsed="true"
+              onselect="gAddBookmarksPanel.onFolderTreeSelect();"
+              showRoot="true"
+              place="place:folder=2&amp;group=3&amp;excludeItems=1&amp;excludeQueries=1&amp;excludeReadOnlyFolders=1"
+              hidecolumnpicker="true">
+          <treecols>
+            <treecol anonid="title" flex="1" primary="true" hideheader="true"/>
+          </treecols>
+          <treechildren flex="1"/>
+        </tree>
+        <row align="center" id="editBMPanel_tagsRow">
+          <label value="&editBookmarkOverlay.tags.label;"
+                 control="tagsField"/>
+          <textbox id="editBMPanel_tagsField"/>
+          <button id="editBMPanel_tagsSelectorExpander"
+                  class="expander-down"
+                  tooltiptext="&editBookmarkOverlay.tagsExpanderDown.tooltip;"
+                  tooltiptextdown="&editBookmarkOverlay.tagsExpanderDown.tooltip;"
+                  tooltiptextup="&editBookmarkOverlay.expanderUp.tooltip;"
+                  oncommand="gAddBookmarksPanel.toggleTagsSelector();"/>
+        </row>
+
+        <!-- XXX: Temporary workaround -->
+        </rows></grid>
+        <listbox id="editBMPanel_tagsSelector" height="150" collapsed="true"/>
+        <grid flex="1"><columns><column/><column flex="1"/></columns><rows>
+
+        <row id="editBMPanel_descriptionRow" align="center">
+          <label value="&editBookmarkOverlay.description.label;"
+                 control="editBMPanel_descriptionField"/>
+          <textbox id="editBMPanel_descriptionField"/>
+        </row>
+      </rows>
+    </grid>
+
+    <hbox>
+      <spacer flex="1"/>
+      <button label="&editBookmarkOverlay.delete.label;"
+              oncommand="gAddBookmarksPanel.deleteAndClose();"/>
+      <button label="&editBookmarkOverlay.ok.label;"
+              default="true"
+              oncommand="gAddBookmarksPanel.accept();"/>
+    </hbox>
+  </vbox>
+</overlay>
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -422,17 +422,18 @@ PlacesTreeView.prototype = {
   COLUMN_TYPE_DATE: 3,
   COLUMN_TYPE_VISITCOUNT: 4,
   COLUMN_TYPE_KEYWORD: 5,
   COLUMN_TYPE_DESCRIPTION: 6,
   COLUMN_TYPE_DATEADDED: 7,
   COLUMN_TYPE_LASTMODIFIED: 8,
 
   _getColumnType: function PTV__getColumnType(aColumn) {
-    switch (aColumn.id) {
+    var columnType = aColumn.id || aColumn.element.getAttribute("anonid");
+    switch (columnType) {
       case "title":
         return this.COLUMN_TYPE_TITLE;
       case "url":
         return this.COLUMN_TYPE_URI;
       case "date":
         return this.COLUMN_TYPE_DATE;
       case "visitCount":
         return this.COLUMN_TYPE_VISITCOUNT;
@@ -871,17 +872,18 @@ PlacesTreeView.prototype = {
         break;
       case this.SESSION_STATUS_CONTINUE:
         aProperties.AppendElement(this._sessionContinueAtom);
         break
     }
   },
 
   getCellProperties: function PTV_getCellProperties(aRow, aColumn, aProperties) {
-    if (aColumn.id != "title")
+    var columnType = aColumn.id || aColumn.element.getAttribute("anonid") ;
+    if (columnType != "title")
       return;
 
     this._ensureValidRow(aRow);
     var node = this._visibleElements[aRow];
 
     if (PlacesUtils.nodeIsSeparator(node))
       aProperties.AppendElement(this._separatorAtom);
   },
--- a/browser/components/places/content/utils.js
+++ b/browser/components/places/content/utils.js
@@ -154,16 +154,26 @@ var PlacesUtils = {
   _microsummaries: null,
   get microsummaries() {
     if (!this._microsummaries)
       this._microsummaries = Cc["@mozilla.org/microsummary/service;1"].
                              getService(Ci.nsIMicrosummaryService);
     return this._microsummaries;
   },
 
+  /**
+   * The Places Tagging Service
+   */
+  get tagging() {
+    if (!this._tagging)
+      this._tagging = Cc["@mozilla.org/browser/tagging-service;1"].
+                      getService(Ci.nsITaggingService);
+    return this._tagging;
+  },
+
   _RDF: null,
   get RDF() {
     if (!this._RDF)
       this._RDF = Cc["@mozilla.org/rdf/rdf-service;1"].
                   getService(Ci.nsIRDFService);
     return this._RDF;
   },
 
@@ -1344,48 +1354,52 @@ var PlacesUtils = {
    * @param aAnnotations
    *        Array of objects, each containing the following properties:
    *        name, flags, expires, type, mimeType (only used for binary
    *        annotations) value.
    */
   setAnnotationsForURI: function PU_setAnnotationsForURI(aURI, aAnnos) {
     var annosvc = this.annotations;
     aAnnos.forEach(function(anno) {
+      var flags = ("flags" in anno) ? anno.flags : 0;
+      var expires = ("expires" in anno) ?
+        anno.expires : Ci.nsIAnnotationService.EXPIRE_NEVER;
       if (anno.type == annosvc.TYPE_BINARY) {
         annosvc.setPageAnnotationBinary(aURI, anno.name, anno.value,
                                         anno.value.length, anno.mimeType,
-                                        anno.flags, anno.expires);
+                                        flags, expires);
       }
-      else {
-        annosvc.setPageAnnotation(aURI, anno.name, anno.value,
-                                  anno.flags, anno.expires);
-      }
+      else
+        annosvc.setPageAnnotation(aURI, anno.name, anno.value, flags, expires);
     });
   },
 
   /**
    * Annotate an item with a batch of annotations.
    * @param aItemId
    *        The identifier of the item for which annotations are to be set
    * @param aAnnotations
    *        Array of objects, each containing the following properties:
    *        name, flags, expires, type, mimeType (only used for binary
    *        annotations) value.
    */
   setAnnotationsForItem: function PU_setAnnotationsForItem(aItemId, aAnnos) {
     var annosvc = this.annotations;
     aAnnos.forEach(function(anno) {
+      var flags = ("flags" in anno) ? anno.flags : 0;
+      var expires = ("expires" in anno) ?
+        anno.expires : Ci.nsIAnnotationService.EXPIRE_NEVER;
       if (anno.type == annosvc.TYPE_BINARY) {
         annosvc.setItemAnnotationBinary(aItemId, anno.name, anno.value,
                                         anno.value.length, anno.mimeType,
-                                        anno.flags, anno.expires);
+                                        flags, expires);
       }
       else {
-        annosvc.setItemAnnotation(aItemId, anno.name, anno.value,
-                                  anno.flags, anno.expires);
+        annosvc.setItemAnnotation(aItemId, anno.name, anno.value, flags,
+                                  expires);
       }
     });
   },
 
   /**
    * Helper for getting a serialized Places query for a particular folder.
    * @param aFolderId The folder id to get a query for.
    * @return string serialized place URI
@@ -1434,16 +1448,23 @@ var PlacesUtils = {
 
   get toolbarFolderId() {
     if (!("_toolbarFolderId" in this))
       this._toolbarFolderId = this.bookmarks.toolbarFolder;
 
     return this._toolbarFolderId;
   },
 
+  get tagRootId() {
+    if (!("_tagRootId" in this))
+      this._tagRootId = this.bookmarks.tagRoot;
+
+    return this._tagRootId;
+  },
+
   /**
    * Set the POST data associated with a URI, if any.
    * Used by POST keywords.
    *   @param aURI
    *   @returns string of POST data
    */
   setPostDataForURI: function PU_setPostDataForURI(aURI, aPostData) {
     const annos = this.annotations;
@@ -1462,16 +1483,29 @@ var PlacesUtils = {
     const annos = this.annotations;
     if (annos.pageHasAnnotation(aURI, POST_DATA_ANNO))
       return annos.getPageAnnotation(aURI, POST_DATA_ANNO);
 
     return null;
   },
 
   /**
+   * Retrieve the description of an item
+   * @param aItemId
+   *        item identifier
+   * @returns the description of the given item, or an empty string if it is
+   * not set.
+   */
+  getItemDescription: function PU_getItemDescription(aItemId) {
+    if (this.annotations.itemHasAnnotation(aItemId, DESCRIPTION_ANNO))
+      return this.annotations.getItemAnnotation(aItemId, DESCRIPTION_ANNO);
+    return "";
+  },
+
+  /**
    * Converts a JavaScript object into a JSON string
    * (see http://www.json.org/ for the full grammar).
    *
    * The inverse operation consists of eval("(" + JSON_string + ")");
    * and should be provably safe.
    *
    * @param aJSObject is the object to be converted
    * @return the object's JSON representation
--- a/browser/components/places/jar.mn
+++ b/browser/components/places/jar.mn
@@ -26,8 +26,10 @@ browser.jar:
 *   content/browser/history/history-panel.xul            (content/history-panel.xul) 
 *   content/browser/places/history-panel.js              (content/history-panel.js)
 # ditto for the bookmarks sidebar
 *   content/browser/bookmarks/bookmarksPanel.xul         (content/bookmarksPanel.xul)
 *   content/browser/bookmarks/bookmarksPanel.js          (content/bookmarksPanel.js)
 *   content/browser/bookmarks/sidebarUtils.js            (content/sidebarUtils.js)
 *   content/browser/places/moveBookmarks.xul             (content/moveBookmarks.xul)
 *   content/browser/places/moveBookmarks.js              (content/moveBookmarks.js)
+*   content/browser/places/editBookmarkOverlay.xul       (content/editBookmarkOverlay.xul)
+*   content/browser/places/editBookmarkOverlay.js        (content/editBookmarkOverlay.js)
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/places/editBookmarkOverlay.dtd
@@ -0,0 +1,13 @@
+<!ENTITY editBookmarkOverlay.name.label                      "Name:">
+<!ENTITY editBookmarkOverlay.liveTitlesSeparator.label       "Live Titles">
+<!ENTITY editBookmarkOverlay.folder.label                    "Folder:">
+<!ENTITY editBookmarkOverlay.allBookmarksFolderItem.label    "All Bookmarks">
+<!ENTITY editBookmarkOverlay.bookmarksMenuFolderItem.label   "Bookmarks Menu">
+<!ENTITY editBookmarkOverlay.toolbarFolderItem.label         "Bookmarks Toolbar">
+<!ENTITY editBookmarkOverlay.foldersExpanderDown.tooltip     "Show all the bookmarks folders">
+<!ENTITY editBookmarkOverlay.expanderUp.tooltip              "Hide">
+<!ENTITY editBookmarkOverlay.tags.label                      "Tags">
+<!ENTITY editBookmarkOverlay.description.label               "Description:">
+<!ENTITY editBookmarkOverlay.tagsExpanderDown.tooltip        "Show all tags">
+<!ENTITY editBookmarkOverlay.ok.label                        "OK">
+<!ENTITY editBookmarkOverlay.delete.label                    "Delete">
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -22,16 +22,17 @@
     locale/browser/searchbar.dtd                   (%chrome/browser/searchbar.dtd)
     locale/browser/engineManager.dtd               (%chrome/browser/engineManager.dtd)
     locale/browser/engineManager.properties        (%chrome/browser/engineManager.properties)
     locale/browser/setDesktopBackground.dtd        (%chrome/browser/setDesktopBackground.dtd)
     locale/browser/shellservice.properties         (%chrome/browser/shellservice.properties)
     locale/browser/sessionstore.properties         (%chrome/browser/sessionstore.properties)
     locale/browser/places/places.dtd               (%chrome/browser/places/places.dtd)
     locale/browser/places/places.properties        (%chrome/browser/places/places.properties)
+    locale/browser/places/editBookmarkOverlay.dtd  (%chrome/browser/places/editBookmarkOverlay.dtd)
     locale/browser/places/bookmarkProperties.dtd   (%chrome/browser/places/bookmarkProperties.dtd)
     locale/browser/places/bookmarkProperties.properties (%chrome/browser/places/bookmarkProperties.properties)
     locale/browser/preferences/selectBookmark.dtd  (%chrome/browser/preferences/selectBookmark.dtd)
     locale/browser/places/moveBookmarks.dtd        (%chrome/browser/places/moveBookmarks.dtd)
 #ifdef MOZ_SAFE_BROWSING
     locale/browser/safebrowsing/phishing-afterload-warning-message.dtd (%chrome/browser/safebrowsing/phishing-afterload-warning-message.dtd)
     locale/browser/safebrowsing/report-phishing.dtd                    (%chrome/browser/safebrowsing/report-phishing.dtd)
 #endif
--- a/browser/themes/pinstripe/browser/browser.css
+++ b/browser/themes/pinstripe/browser/browser.css
@@ -1264,8 +1264,17 @@ toolbarbutton.bookmark-item[dragover="tr
   border-top: none !important;
   border-left: 2px solid;
   border-right: 2px solid;
   border-bottom: 3px solid;
   -moz-border-right-colors: -moz-mac-menushadow ThreeDLightShadow  !important;
   -moz-border-bottom-colors: -moz-mac-menushadow -moz-mac-menushadow ThreeDShadow  !important;
   -moz-border-left-colors: ThreeDLightShadow ThreeDHighlight !important;
 }
+
+/* star icon */
+#star-icon {
+  list-style-image: url("chrome://browser/skin/places/starPage.png");
+}
+
+#star-icon[starred="true"] {
+  list-style-image: url("chrome://browser/skin/places/pageStarred.png");
+}
--- a/browser/themes/pinstripe/browser/jar.mn
+++ b/browser/themes/pinstripe/browser/jar.mn
@@ -39,16 +39,19 @@ classic.jar:
   skin/classic/browser/places/bookmarksToolbar.png          (places/bookmarksToolbar.png)
   skin/classic/browser/places/toolbarDropMarker.png         (places/toolbarDropMarker.png)
   skin/classic/browser/places/folderDragOver.png            (places/folderDragOver.png)
   skin/classic/browser/places/folderDropArrow.png           (places/folderDropArrow.png)
   skin/classic/browser/places/folderDropHoverArrow.png      (places/folderDropHoverArrow.png)
   skin/classic/browser/places/livemarkFolder.png            (places/livemarkFolder.png)
   skin/classic/browser/places/livemarkFolderHover.png       (places/livemarkFolderHover.png)
   skin/classic/browser/places/bookmarkProperties.css        (places/bookmarkProperties.css)
+  skin/classic/browser/places/editBookmarkOverlay.css       (places/editBookmarkOverlay.css)
+  skin/classic/browser/places/starPage.png                  (places/starPage.png)
+  skin/classic/browser/places/pageStarred.png               (places/pageStarred.png)
   skin/classic/browser/places/organizer-toolbar.png         (bookmarks/Bookmarks-toolbar.png)
   skin/classic/browser/places/expander-closed-active.png    (bookmarks/expander-closed-active.png)
   skin/classic/browser/places/expander-closed.png           (bookmarks/expander-closed.png)
   skin/classic/browser/places/expander-open-active.png      (bookmarks/expander-open-active.png)
   skin/classic/browser/places/expander-open.png             (bookmarks/expander-open.png)
 #ifdef MOZ_SAFE_BROWSING
   skin/classic/browser/safebrowsing/browser-protection.css  (safebrowsing/browser-protection.css)
   skin/classic/browser/safebrowsing/close16x16.png          (safebrowsing/close16x16.png)
new file mode 100644
--- /dev/null
+++ b/browser/themes/pinstripe/browser/places/editBookmarkOverlay.css
@@ -0,0 +1,143 @@
+/* ***** 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 Firefox.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Myk Melez <myk@mozilla.org>
+ *   Asaf Romano <mano@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+/**** folder menulist ****/
+.folder-icon > .menulist-label-box > .menulist-icon,
+.folder-icon > .menu-iconic-left > .menu-iconic-icon {
+  width: 16px;
+  height: 16px;
+}
+
+.folder-icon > .menu-iconic-left {
+  display: -moz-box;
+}
+
+.folder-icon {
+  list-style-image: url("chrome://global/skin/tree/folder.png") !important;
+}
+
+.menulist-icon {
+	margin: 0 !important;
+}
+
+/**** folder tree ****/
+
+#editBMPanel_folderTree {
+   margin: 6px 0;
+}
+
+/**** expanders ****/
+
+.expander-up,
+.expander-down {
+  -moz-appearance: none;
+  margin-left: 8px;
+  padding: 0;
+  min-width: 0;
+}
+
+.expander-up {
+	list-style-image: url("chrome://browser/skin/places/expander-open.png") !important;
+}
+
+.expander-down {
+	list-style-image: url("chrome://browser/skin/places/expander-closed.png") !important;
+}
+
+.expander-down:hover:active {
+	list-style-image: url("chrome://browser/skin/places/expander-closed-active.png") !important;
+}
+
+.expander-up:hover:active {
+	list-style-image: url("chrome://browser/skin/places/expander-open-active.png") !important;
+}
+
+/**** name picker ****/
+
+/* Make the microsummary picker look like a regular textbox instead of 
+ * an editable menulist when no microsummaries are available.
+ */
+#editBMPanel_namePicker[droppable="false"] {
+  -moz-appearance: none;
+  margin: 0px;
+  border: none;
+  padding: 0px;
+  height: auto !important;
+}
+
+#editBMPanel_namePicker[droppable="false"] > .menulist-dropmarker {
+  display: none;
+}
+
+#editBMPanel_namePicker[droppable="false"] > .menulist-editable-box {
+  /* These rules are duplicates of the rules for the textbox element
+   * in textbox.css and should track changes in that file.
+   */
+  -moz-appearance: textfield;
+  cursor: text;
+  margin: 2px 4px;
+  border: 2px solid;
+  -moz-border-top-colors: ThreeDShadow ThreeDDarkShadow;
+  -moz-border-right-colors: ThreeDHighlight ThreeDLightShadow;
+  -moz-border-bottom-colors: ThreeDHighlight ThreeDLightShadow;
+  -moz-border-left-colors: ThreeDShadow ThreeDDarkShadow;
+  padding: 2px 2px 3px 4px;
+  background-color: -moz-Field;
+  color: -moz-FieldText;
+}
+
+#editBMPanel_namePicker[droppable="false"] > .menulist-editable-box > html|*.textbox-input {
+  margin: 0px !important;
+  border: none !important;
+  padding: 0px !important;
+  background-color: inherit;
+  color: inherit;
+  font: inherit;
+}
+
+/* Hide the drop marker and the popup. */
+
+#editBMPanel_namePicker[droppable="false"] > .menulist-dropmarker {
+  display: none;
+}
+
+#editBMPanel_namePicker[droppable="false"] > menupopup {
+  display: none;
+}
new file mode 100644
index 0000000000000000000000000000000000000000..b63f06afbcdc46f379d1ce0fb0783ef4a0ffc90b
GIT binary patch
literal 3270
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
Eg3c`;d;kCd
new file mode 100644
index 0000000000000000000000000000000000000000..5223196e8ea68fbd7d714e8820a0bcc25f184d2a
GIT binary patch
literal 3075
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
R_H+OM002ovPDHLkV1iqKt?2*&
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -1434,8 +1434,17 @@ toolbarbutton.bookmark-item[dragover="tr
 
 .bookmark-item[dragover-top="true"] {
   -moz-border-top-colors: #000000;
 }
 
 .bookmark-item[dragover-bottom="true"] {
   -moz-border-bottom-colors: #000000;
 }
+
+/* star icon */
+#star-icon {
+  list-style-image: url("chrome://browser/skin/places/starPage.png");
+}
+
+#star-icon[starred="true"] {
+  list-style-image: url("chrome://browser/skin/places/pageStarred.png");
+}
--- a/browser/themes/winstripe/browser/jar.mn
+++ b/browser/themes/winstripe/browser/jar.mn
@@ -45,16 +45,19 @@ classic.jar:
         skin/classic/browser/feeds/subscribe.css                (feeds/subscribe.css)
         skin/classic/browser/places/places.css                  (places/places.css)
         skin/classic/browser/places/query.png                   (places/query.png)
         skin/classic/browser/places/livemarkItem.png            (places/livemarkItem.png)
         skin/classic/browser/places/bookmarksMenu.png           (places/bookmarksMenu.png)
         skin/classic/browser/places/bookmarksToolbar.png        (places/bookmarksToolbar.png)
         skin/classic/browser/places/toolbarDropMarker.png       (places/toolbarDropMarker.png)
         skin/classic/browser/places/folderDragOver.png          (places/folderDragOver.png)
+        skin/classic/browser/places/editBookmarkOverlay.css     (places/editBookmarkOverlay.css)
+        skin/classic/browser/places/starPage.png                (places/starPage.png)
+        skin/classic/browser/places/pageStarred.png             (places/pageStarred.png)
         skin/classic/browser/places/bookmarkProperties.css      (places/bookmarkProperties.css)
         skin/classic/browser/places/organizer-toolbar.png       (bookmarks/Bookmarks-toolbar.png)
 #ifdef MOZ_SAFE_BROWSING
         skin/classic/browser/safebrowsing/browser-protection.css      (safebrowsing/browser-protection.css)
         skin/classic/browser/safebrowsing/close16x16.png              (safebrowsing/close16x16.png)
         skin/classic/browser/safebrowsing/dim.png                     (safebrowsing/dim.png)
         skin/classic/browser/safebrowsing/tail.png                    (safebrowsing/tail.png)
         skin/classic/browser/safebrowsing/warning16x16.png            (safebrowsing/warning16x16.png)
new file mode 100644
--- /dev/null
+++ b/browser/themes/winstripe/browser/places/editBookmarkOverlay.css
@@ -0,0 +1,110 @@
+/* ***** 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 Firefox.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Myk Melez <myk@mozilla.org>
+ *   Asaf Romano <mano@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**** folder menulist ****/
+.folder-icon > .menulist-label-box > .menulist-icon {
+  width: 16px;
+  height: 16px;
+}
+
+.folder-icon > .menu-iconic-left {
+  display: -moz-box;
+}
+
+.folder-icon {
+  list-style-image: url("chrome://global/skin/icons/folder-item.png") !important;
+  -moz-image-region: rect(0px, 32px, 16px, 16px) !important;
+}
+
+
+/**** expanders ****/
+
+.expander-up,
+.expander-down {
+  min-width: 0;
+}
+
+.expander-up {
+	list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
+}
+
+.expander-down {
+  list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
+}
+
+.expander-down:hover:active {
+	list-style-image: url("chrome://global/skin/arrow/arrow-dn-hov.gif");
+}
+
+.expander-up:hover:active {
+	list-style-image: url("chrome://global/skin/arrow/arrow-up-hov.gif");
+}
+
+/**** name picker ****/
+
+/* Make the microsummary picker look like a regular textbox instead of 
+ * an editable menulist when no microsummaries are available.
+ */
+#editBMPanel_namePicker[droppable="false"] {
+  /* These rules come from the textbox element in textbox.css. */
+
+  /* Normal editable menulists set this to "none". */
+  -moz-appearance: textfield;
+  cursor: text;
+
+  border: 2px solid;
+  -moz-border-top-colors: ThreeDShadow ThreeDDarkShadow;
+  -moz-border-right-colors: ThreeDHighlight ThreeDLightShadow;
+  -moz-border-bottom-colors: ThreeDHighlight ThreeDLightShadow;
+  -moz-border-left-colors: ThreeDShadow ThreeDDarkShadow;
+  background-color: -moz-Field;
+  color: -moz-FieldText;
+}
+
+#editBMPanel_namePicker[droppable="false"] > .menulist-dropmarker {
+  /* Normal editable menulists set this to "menulist-textfield". */
+  -moz-appearance: none;
+  padding: 2px 2px 3px 4px;
+}
+
+
+/* Hide the drop marker and the popup. */
+
+#editBMPanel_namePicker[droppable="false"] > .menulist-dropmarker,
+#editBMPanel_namePicker[droppable="false"] > menupopup {
+  display: none;
+}
new file mode 100644
index 0000000000000000000000000000000000000000..b63f06afbcdc46f379d1ce0fb0783ef4a0ffc90b
GIT binary patch
literal 3270
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
Eg3c`;d;kCd
new file mode 100644
index 0000000000000000000000000000000000000000..5223196e8ea68fbd7d714e8820a0bcc25f184d2a
GIT binary patch
literal 3075
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
R_H+OM002ovPDHLkV1iqKt?2*&
--- a/toolkit/components/places/public/nsITaggingService.idl
+++ b/toolkit/components/places/public/nsITaggingService.idl
@@ -10,22 +10,22 @@
  * 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 Places Tagging Service code.
  *
  * The Initial Developer of the Original Code is
- * Mozilla Corporation
+ * Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2007
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
- *   Asaf Romano <mano@mozilla.com>
+ *   Asaf Romano <mano@mozilla.com> (Original Author)
  *
  * 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
@@ -36,45 +36,42 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIURI;
 interface nsIVariant;
 
-[scriptable, uuid(962bf94f-e719-4cf6-8967-74ff5d2020df)]
+[scriptable, uuid(03dd79c2-c834-4747-a204-79d5a29c6bd9)]
 interface nsITaggingService : nsISupports
 {
   /**
    * Tags a URL with the given set of tags. Current tags set for the URL
    * persist. Tags in aTags which are already set for the given URL are
    * ignored.
    *
    * @param aURI
    *        the URL to tag.
    * @param aTags
    *        Array of tags to set for the given URL.
    */
-  void tagURI(in nsIURI aURI,
-              [const, array, size_is(aCount)] in wstring aTags,
-              in unsigned long aCount);
+  void tagURI(in nsIURI aURI, in nsIVariant aTags);
 
   /**
    * Removes tags from a URL. Tags from aTags which are not set for the
    * given URL are ignored.
    *
    * @param aURI
    *        the URL to un-tag.
    * @param aTags
-   *        Array of tags to unset.
+   *        Array of tags to unset. pass null to remove all tags from the given
+   *        url.
    */
-  void untagURI(in nsIURI aURI,
-                [const, array, size_is(aCount)] in wstring aTags,
-                in unsigned long aCount);
+  void untagURI(in nsIURI aURI, in nsIVariant aTags);
 
   /**
    * Retrieves all URLs tagged with the given tag.
    *
    * @param aTag
    *        tag name
    * @returns Array of uris tagged with aTag.
    */
@@ -83,9 +80,14 @@ interface nsITaggingService : nsISupport
   /**
    * Retrieves all tags set for the given URL.
    *
    * @param aURI
    *        a URL.
    * @returns array of tags (sorted by name).
    */
   nsIVariant getTagsForURI(in nsIURI aURI);
+
+  /**
+   * Retrieves all tags used to tag URIs in the data-base (sorted by name).
+   */
+  readonly attribute nsIVariant allTags;
 };
--- a/toolkit/components/places/src/nsTaggingService.js
+++ b/toolkit/components/places/src/nsTaggingService.js
@@ -7,25 +7,25 @@
  * 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 the Places Tagging Service
+ * The Original Code is the Places Tagging Service.
  *
  * The Initial Developer of the Original Code is
  * Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2007
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
- *   Asaf Romano <mano@mozilla.com>
+ *   Asaf Romano <mano@mozilla.com> (Original Author)
  *
  * 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
@@ -35,102 +35,112 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
-const TAGS_CLASSID = Components.ID("{6a059068-1630-11dc-8314-0800200c9a66}");
-const TAGS_CLASSNAME = "Places Tagging Service";
-const TAGS_CONTRACTID = "@mozilla.org/browser/tagging-service;1";
-
-const TAG_CONTAINER_ICON_URI = "chrome://mozapps/skin/places/tagContainerIcon.png"
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const NH_CONTRACTID = "@mozilla.org/browser/nav-history-service;1";
 const BMS_CONTRACTID = "@mozilla.org/browser/nav-bookmarks-service;1";
 const IO_CONTRACTID = "@mozilla.org/network/io-service;1";
+const ANNO_CONTRACTID = "@mozilla.org/browser/annotation-service;1";
 const FAV_CONTRACTID = "@mozilla.org/browser/favicon-service;1";
+const OBSS_CONTRACTID = "@mozilla.org/observer-service;1";
+
+const TAG_CONTAINER_ICON_URI = "chrome://mozapps/skin/places/tagContainerIcon.png"
 
 var gIoService = Cc[IO_CONTRACTID].getService(Ci.nsIIOService);
 
 /**
  * The Places Tagging Service
  */
 function TaggingService() {
-  this._tags = [];
-  
-  var options = this._history.getNewQueryOptions();
-  var query = this._history.getNewQuery();
-  query.setFolders([this._bms.tagRoot], 1);
-  var result = this._history.executeQuery(query, options);
-  var rootNode = result.root;
-  rootNode.containerOpen = true;
-
-  var cc = rootNode.childCount;
-  for (var i=0; i < cc; i++) {
-    var child = rootNode.getChild(i);
-    this._tags.push({ itemId: child.itemId, name: child.title });
-  }
 }
 
 TaggingService.prototype = {
   get _bms() {
     if (!this.__bms)
       this.__bms = Cc[BMS_CONTRACTID].getService(Ci.nsINavBookmarksService);
     return this.__bms;
   },
 
   get _history() {
     if (!this.__history)
       this.__history = Cc[NH_CONTRACTID].getService(Ci.nsINavHistoryService);
     return this.__history;
   },
 
+  get _annos() {
+    if (!this.__annos)
+      this.__annos =  Cc[ANNO_CONTRACTID].getService(Ci.nsIAnnotationService);
+    return this.__annos;
+  },
+
+  get _tagsResult() {
+    if (!this.__tagsResult) {
+      var options = this._history.getNewQueryOptions();
+      var query = this._history.getNewQuery();
+      query.setFolders([this._bms.tagRoot], 1);
+      this.__tagsResult = this._history.executeQuery(query, options);
+      this.__tagsResult.root.containerOpen = true;
+
+      // we need to null out the result on shutdown
+      var observerSvc = Cc[OBSS_CONTRACTID].getService(Ci.nsIObserverService);
+      observerSvc.addObserver(this, "xpcom-shutdown", false);
+    }
+    return this.__tagsResult;
+  },
+
+  // Feed XPCOMUtils
+  classDescription: "Places Tagging Service",
+  contractID: "@mozilla.org/browser/tagging-service;1",
+  classID: Components.ID("{6a059068-1630-11dc-8314-0800200c9a66}"),
+
   // nsISupports
-  QueryInterface: function TS_QueryInterface(iid) {
-    if (iid.equals(Ci.nsITaggingService) ||
-        iid.equals(Ci.nsISupports))
-      return this;
-    throw Cr.NO_INTERFACE;
-  },
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsITaggingService,
+                                         Ci.nsIObserver]),
 
   /**
-   * If there's no tag with the given name, -1 is returned.
+   * If there's no tag with the given name, null is returned;
    */
-  _getTagIndex: function TS__getTagIndex(aName) {
-    for (var i=0; i < this._tags.length; i++) {
-      if (this._tags[i].name == aName)
-        return i;
+  _getTagNode: function TS__getTagIndex(aName) {
+    var root = this._tagsResult.root;
+    var cc = root.childCount;
+    for (var i=0; i < cc; i++) {
+      var child = root.getChild(i);
+      if (child.title == aName)
+        return child;
     }
 
-    return -1;
+    return null;
   },
 
   get _tagContainerIcon() {
     if (!this.__tagContainerIcon) {
       this.__tagContainerIcon =
         gIoService.newURI(TAG_CONTAINER_ICON_URI, null, null);
     }
 
     return this.__tagContainerIcon;
   },
 
   /**
-   * Creates a tag container under the tags-root with the given name
+   * Creates a tag container under the tags-root with the given name.
    *
    * @param aName
    *        the name for the new container.
    * @returns the id of the new container.
    */
   _createTag: function TS__createTag(aName) {
     var id = this._bms.createFolder(this._bms.tagRoot, aName,
                                     this._bms.DEFAULT_INDEX);
-    this._tags.push({ itemId: id, name: aName});
 
     // Set the favicon
     var faviconService = Cc[FAV_CONTRACTID].getService(Ci.nsIFaviconService);
     var uri = this._bms.getFolderURI(id);
     faviconService.setFaviconUrlForPage(uri, this._tagContainerIcon);
   
     return id;
   },
@@ -159,166 +169,142 @@ TaggingService.prototype = {
     if (rootNode.childCount != 0) {
       aItemId.value = rootNode.getChild(0).itemId;
       return true;
     }
     return false;
   },
 
   // nsITaggingService
-  tagURI: function TS_tagURI(aURI, aTags, aCount) {
-    if (!aURI)
+  tagURI: function TS_tagURI(aURI, aTags) {
+    if (!aURI || !aTags)
       throw Cr.NS_ERROR_INVALID_ARG;
 
     for (var i=0; i < aTags.length; i++) {
       if (aTags[i].length == 0)
         throw Cr.NS_ERROR_INVALID_ARG;
 
-      var tagIndex = this._getTagIndex(aTags[i]);
-      if (tagIndex == -1) {
+      var tagNode = this._getTagNode(aTags[i]);
+      if (!tagNode) {
         var tagId = this._createTag(aTags[i]);
         this._bms.insertBookmark(tagId, aURI, this._bms.DEFAULT_INDEX, "");
       }
       else {
-        var tagId = this._tags[tagIndex].itemId;
-        if (!this._isURITaggedInternal(aURI, tagId, {}))
+        var tagId = tagNode.itemId;
+        if (!this._isURITaggedInternal(aURI, tagNode.itemId, {}))
           this._bms.insertBookmark(tagId, aURI, this._bms.DEFAULT_INDEX, "");
       }
     }
   },
 
   /**
    * Removes the tag container from the tags-root if the given tag is empty.
    *
-   * @param aTagIndex
-   *        the index of a tag element under the _tags array
+   * @param aTagId
+   *        the item-id of the tag element under the tags root
    */
-  _removeTagAtIndexIfEmpty: function TS__removeTagAtIndexIfEmpty(aTagIndex) {
+  _removeTagIfEmpty: function TS__removeTagIfEmpty(aTagId) {
     var options = this._history.getNewQueryOptions();
     var query = this._history.getNewQuery();
-    query.setFolders([this._tags[aTagIndex].itemId], 1);
+    query.setFolders([aTagId], 1);
     var result = this._history.executeQuery(query, options);
     var rootNode = result.root;
     rootNode.containerOpen = true;
-    if (rootNode.childCount == 0) {
-      this._bms.removeFolder(this._tags[aTagIndex].itemId);
-      this._tags.splice(aTagIndex, 1);
-    }
+    if (rootNode.childCount == 0)
+      this._bms.removeFolder(aTagId);
   },
 
   // nsITaggingService
-  untagURI: function TS_untagURI(aURI, aTags, aCount) {
+  untagURI: function TS_untagURI(aURI, aTags) {
     if (!aURI)
       throw Cr.NS_ERROR_INVALID_ARG;
 
+    if (!aTags) {
+      // see IDL.
+      // XXXmano: write a perf-sensitive version of this code path...
+      aTags = this.getTagsForURI(aURI);
+    }
+
     for (var i=0; i < aTags.length; i++) {
       if (aTags[i].length == 0)
         throw Cr.NS_ERROR_INVALID_ARG;
 
-      var tagIndex = this._getTagIndex(aTags[i]);
-      if (tagIndex != -1) {
+      var tagNode = this._getTagNode(aTags[i]);
+      if (tagNode) {
         var itemId = { };
-        if (this._isURITaggedInternal(aURI, this._tags[tagIndex].itemId,
-                                      itemId)) {
+        if (this._isURITaggedInternal(aURI, tagNode.itemId, itemId)) {
           this._bms.removeItem(itemId.value);
-          this._removeTagAtIndexIfEmpty(tagIndex);
+          this._removeTagIfEmpty(tagNode.itemId);
         }
       }
     }
   },
 
   // nsITaggingService
   getURIsForTag: function TS_getURIsForTag(aTag) {
     if (aTag.length == 0)
       throw Cr.NS_ERROR_INVALID_ARG;
 
     var uris = [];
-    var tagIndex = this._getTagIndex(aTag);
-    if (tagIndex != -1) {
-      var tagId = this._tags[tagIndex].itemId;
-      var options = this._history.getNewQueryOptions();
-      var query = this._history.getNewQuery();
-      query.setFolders([tagId], 1);
-      var result = this._history.executeQuery(query, options);
-      var rootNode = result.root;
-      rootNode.containerOpen = true;
-      var cc = rootNode.childCount;
+    var tagNode = this._getTagNode(aTag);
+    if (tagNode) {
+      tagNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
+      tagNode.containerOpen = true;
+      var cc = tagNode.childCount;
       for (var i=0; i < cc; i++)
-        uris.push(gIoService.newURI(rootNode.getChild(i).uri, null, null));
+        uris.push(gIoService.newURI(tagNode.getChild(i).uri, null, null));
+      tagNode.containerOpen = false;
     }
     return uris;
   },
 
   // nsITaggingService
   getTagsForURI: function TS_getTagsForURI(aURI) {
     if (!aURI)
       throw Cr.NS_ERROR_INVALID_ARG;
 
     var tags = [];
-
     var bookmarkIds = this._bms.getBookmarkIdsForURI(aURI, {});
+    var root = this._tagsResult.root;
+    var cc = root.childCount;
     for (var i=0; i < bookmarkIds.length; i++) {
       var parent = this._bms.getFolderIdForItem(bookmarkIds[i]);
-      for (var j=0; j < this._tags.length; j++) {
-        if (this._tags[j].itemId == parent)
-          tags.push(this._tags[j].name);
+      for (var j=0; j < cc; j++) {
+        var child = root.getChild(j);
+        if (child.itemId == parent)
+          tags.push(child.title);
       }
     }
 
     // sort the tag list
     tags.sort();
     return tags;
-  }
-};
+  },
 
-var gModule = {
-  registerSelf: function(componentManager, fileSpec, location, type) {
-    componentManager = componentManager.QueryInterface(Ci.nsIComponentRegistrar);
-    
-    for (var key in this._objects) {
-      var obj = this._objects[key];
-      componentManager.registerFactoryLocation(obj.CID,
-                                               obj.className,
-                                               obj.contractID,
-                                               fileSpec,
-                                               location,
-                                               type);
-    }
-  },
-  
-  unregisterSelf: function(componentManager, fileSpec, location) {},
-
-  getClassObject: function(componentManager, cid, iid) {
-    if (!iid.equals(Components.interfaces.nsIFactory))
-      throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-  
-    for (var key in this._objects) {
-      if (cid.equals(this._objects[key].CID))
-        return this._objects[key].factory;
+  // nsITaggingService
+  get allTags() {
+    var tags = [];
+    var root = this._tagsResult.root;
+    var cc = root.childCount;
+    for (var j=0; j < cc; j++) {
+      var child = root.getChild(j);
+      tags.push(child.title);
     }
 
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
-  
-  _objects: {
-    service: {
-      CID        : TAGS_CLASSID,
-      contractID : TAGS_CONTRACTID,
-      className  : TAGS_CLASSNAME,
-      factory    : TaggingServiceFactory = {
-                     createInstance: function(aOuter, aIID) {
-                       if (aOuter != null)
-                         throw Cr.NS_ERROR_NO_AGGREGATION;
-                       var svc = new TaggingService();
-                      return svc.QueryInterface(aIID);
-                     }
-                   }
-    }
+    // sort the tag list
+    tags.sort();
+    return tags;
   },
 
-  canUnload: function(componentManager) {
-    return true;
+  // nsIObserver
+  observe: function TS_observe(subject, topic, data) {
+    if (topic == "xpcom-shutdown") {
+      this.__tagsResult.root.containerOpen = false;
+      this.__tagsResult = null;
+      var observerSvc = Cc[OBSS_CONTRACTID].getService(Ci.nsIObserverService);
+      observerSvc.removeObserver(this, "xpcom-shutdown");
+    }
   }
 };
 
 function NSGetModule(compMgr, fileSpec) {
-  return gModule;
+  return XPCOMUtils.generateModule([TaggingService]);
 }
--- a/toolkit/components/places/tests/unit/test_tagging.js
+++ b/toolkit/components/places/tests/unit/test_tagging.js
@@ -11,22 +11,22 @@
  * 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 Places Tagging Service unit test code.
  *
  * The Initial Developer of the Original Code is
- * Mozilla Corporation
+ * Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2007
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
- *   Asaf Romano <mano@mozilla.com>
+ *   Asaf Romano <mano@mozilla.com> (Original Author)
  *
  * 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
@@ -73,33 +73,33 @@ function run_test() {
   tagRoot.containerOpen = true;
 
   do_check_eq(tagRoot.childCount, 0);
 
   var uri1 = uri("http://foo.tld/");
   var uri2 = uri("https://bar.tld/");
 
   // this also tests that the multiple folders are not created for the same tag
-  tagssvc.tagURI(uri1, ["tag 1"], 1);
-  tagssvc.tagURI(uri2, ["tag 1"], 1);
+  tagssvc.tagURI(uri1, ["tag 1"]);
+  tagssvc.tagURI(uri2, ["tag 1"]);
   do_check_eq(tagRoot.childCount, 1);
 
   var tag1node = tagRoot.getChild(0)
                         .QueryInterface(Ci.nsINavHistoryContainerResultNode);
   do_check_eq(tag1node.title, "tag 1");
   tag1node.containerOpen = true;
   do_check_eq(tag1node.childCount, 2);
 
   // Tagging the same url twice with the same tag should be a no-op
-  tagssvc.tagURI(uri1, ["tag 1"], 1);
+  tagssvc.tagURI(uri1, ["tag 1"]);
   do_check_eq(tag1node.childCount, 2);
 
   // the former should be ignored.
   do_check_eq(tagRoot.childCount, 1);
-  tagssvc.tagURI(uri1, ["tag 1", "tag 2"], 2);
+  tagssvc.tagURI(uri1, ["tag 1", "tag 2"]);
   do_check_eq(tagRoot.childCount, 2);
 
   // test getTagsForURI
   var uri1tags = tagssvc.getTagsForURI(uri1, {});
   do_check_eq(uri1tags.length, 2);
   do_check_eq(uri1tags[0], "tag 1");
   do_check_eq(uri1tags[1], "tag 2");
   var uri2tags = tagssvc.getTagsForURI(uri2, {});
@@ -107,15 +107,21 @@ function run_test() {
   do_check_eq(uri2tags[0], "tag 1");
 
   // test getURIsForTag
   var tag1uris = tagssvc.getURIsForTag("tag 1");
   do_check_eq(tag1uris.length, 2);
   do_check_true(tag1uris[0].equals(uri1));
   do_check_true(tag1uris[1].equals(uri2));
 
-  tagssvc.untagURI(uri1, ["tag 1"], 1);
+  // test allTags attribute
+  var allTags = tagssvc.allTags;
+  do_check_eq(allTags.length, 2);
+  do_check_eq(allTags[0], "tag 1");
+  do_check_eq(allTags[1], "tag 2");
+
+  tagssvc.untagURI(uri1, ["tag 1"]);
   do_check_eq(tag1node.childCount, 1);
 
   // removing the last uri from a tag should remove the tag-container
-  tagssvc.untagURI(uri2, ["tag 1"], 1);
+  tagssvc.untagURI(uri2, ["tag 1"]);
   do_check_eq(tagRoot.childCount, 1);
 }