home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / lib / firefox-3.0.14 / chrome / browser.jar / content / browser / places / utils.js < prev   
Encoding:
JavaScript  |  2008-11-18  |  45.9 KB  |  1,245 lines

  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is the Places Command Controller.
  16.  *
  17.  * The Initial Developer of the Original Code is Google Inc.
  18.  * Portions created by the Initial Developer are Copyright (C) 2005
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *   Ben Goodger <beng@google.com>
  23.  *   Myk Melez <myk@mozilla.org>
  24.  *   Asaf Romano <mano@mozilla.com>
  25.  *   Sungjoon Steve Won <stevewon@gmail.com>
  26.  *   Dietrich Ayala <dietrich@mozilla.com>
  27.  *
  28.  * Alternatively, the contents of this file may be used under the terms of
  29.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  30.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  31.  * in which case the provisions of the GPL or the LGPL are applicable instead
  32.  * of those above. If you wish to allow use of your version of this file only
  33.  * under the terms of either the GPL or the LGPL, and not to allow others to
  34.  * use your version of this file under the terms of the MPL, indicate your
  35.  * decision by deleting the provisions above and replace them with the notice
  36.  * and other provisions required by the GPL or the LGPL. If you do not delete
  37.  * the provisions above, a recipient may use your version of this file under
  38.  * the terms of any one of the MPL, the GPL or the LGPL.
  39.  *
  40.  * ***** END LICENSE BLOCK ***** */
  41.  
  42. function LOG(str) {
  43.   dump("*** " + str + "\n");
  44. }
  45.  
  46. var Ci = Components.interfaces;
  47. var Cc = Components.classes;
  48. var Cr = Components.results;
  49.  
  50. __defineGetter__("PlacesUtils", function() {
  51.   delete this.PlacesUtils
  52.   var tmpScope = {};
  53.   Components.utils.import("resource://gre/modules/utils.js", tmpScope);
  54.   return this.PlacesUtils = tmpScope.PlacesUtils;
  55. });
  56.  
  57. const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
  58. const DESCRIPTION_ANNO = "bookmarkProperties/description";
  59. const GUID_ANNO = "placesInternal/GUID";
  60. const LMANNO_FEEDURI = "livemark/feedURI";
  61. const LMANNO_SITEURI = "livemark/siteURI";
  62. const ORGANIZER_FOLDER_ANNO = "PlacesOrganizer/OrganizerFolder";
  63. const ORGANIZER_QUERY_ANNO = "PlacesOrganizer/OrganizerQuery";
  64. const ORGANIZER_LEFTPANE_VERSION = 4;
  65.  
  66. //@line 71 "/build/buildd/firefox-3.0-3.0.14+build2+nobinonly/build-tree/mozilla/browser/components/places/content/utils.js"
  67. // On other platforms, the transferable system converts "\r\n" to "\n".
  68. const NEWLINE = "\r\n";
  69. //@line 74 "/build/buildd/firefox-3.0-3.0.14+build2+nobinonly/build-tree/mozilla/browser/components/places/content/utils.js"
  70.  
  71. function QI_node(aNode, aIID) {
  72.   return aNode.QueryInterface(aIID);
  73. }
  74. function asVisit(aNode)    { return QI_node(aNode, Ci.nsINavHistoryVisitResultNode);    }
  75. function asFullVisit(aNode){ return QI_node(aNode, Ci.nsINavHistoryFullVisitResultNode);}
  76. function asContainer(aNode){ return QI_node(aNode, Ci.nsINavHistoryContainerResultNode);}
  77. function asQuery(aNode)    { return QI_node(aNode, Ci.nsINavHistoryQueryResultNode);    }
  78.  
  79. var PlacesUIUtils = {
  80.   /**
  81.    * The Microsummary Service
  82.    */
  83.   get microsummaries() {
  84.     delete this.microsummaries;
  85.     return this.microsummaries = Cc["@mozilla.org/microsummary/service;1"].
  86.                                  getService(Ci.nsIMicrosummaryService);
  87.   },
  88.  
  89.   get RDF() {
  90.     delete this.RDF;
  91.     return this.RDF = Cc["@mozilla.org/rdf/rdf-service;1"].
  92.                       getService(Ci.nsIRDFService);
  93.   },
  94.  
  95.   get localStore() {
  96.     delete this.localStore;
  97.     return this.localStore = this.RDF.GetDataSource("rdf:local-store");
  98.   },
  99.  
  100.   get ptm() {
  101.     delete this.ptm;
  102.     return this.ptm = Cc["@mozilla.org/browser/placesTransactionsService;1"].
  103.                       getService(Ci.nsIPlacesTransactionsService);
  104.   },
  105.  
  106.   get clipboard() {
  107.     delete this.clipboard;
  108.     return this.clipboard = Cc["@mozilla.org/widget/clipboard;1"].
  109.                             getService(Ci.nsIClipboard);
  110.   },
  111.  
  112.   get URIFixup() {
  113.     delete this.URIFixup;
  114.     return this.URIFixup = Cc["@mozilla.org/docshell/urifixup;1"].
  115.                            getService(Ci.nsIURIFixup);
  116.   },
  117.  
  118.   get ellipsis() {
  119.     delete this.ellipsis;
  120.     var pref = Cc["@mozilla.org/preferences-service;1"].
  121.                getService(Ci.nsIPrefBranch);
  122.     return this.ellipsis = pref.getComplexValue("intl.ellipsis",
  123.                                                 Ci.nsIPrefLocalizedString).data;
  124.   },
  125.  
  126.   /**
  127.    * Makes a URI from a spec, and do fixup
  128.    * @param   aSpec
  129.    *          The string spec of the URI
  130.    * @returns A URI object for the spec.
  131.    */
  132.   createFixedURI: function PU_createFixedURI(aSpec) {
  133.     return this.URIFixup.createFixupURI(aSpec, 0);
  134.   },
  135.  
  136.   /**
  137.    * Wraps a string in a nsISupportsString wrapper
  138.    * @param   aString
  139.    *          The string to wrap
  140.    * @returns A nsISupportsString object containing a string.
  141.    */
  142.   _wrapString: function PU__wrapString(aString) {
  143.     var s = Cc["@mozilla.org/supports-string;1"].
  144.             createInstance(Ci.nsISupportsString);
  145.     s.data = aString;
  146.     return s;
  147.   },
  148.  
  149.   /**
  150.    * String bundle helpers
  151.    */
  152.   get _bundle() {
  153.     const PLACES_STRING_BUNDLE_URI =
  154.         "chrome://browser/locale/places/places.properties";
  155.     delete this._bundle;
  156.     return this._bundle = Cc["@mozilla.org/intl/stringbundle;1"].
  157.                           getService(Ci.nsIStringBundleService).
  158.                           createBundle(PLACES_STRING_BUNDLE_URI);
  159.   },
  160.  
  161.   getFormattedString: function PU_getFormattedString(key, params) {
  162.     return this._bundle.formatStringFromName(key, params, params.length);
  163.   },
  164.  
  165.   getString: function PU_getString(key) {
  166.     return this._bundle.GetStringFromName(key);
  167.   },
  168.  
  169.   /**
  170.    * Get a transaction for copying a uri item from one container to another
  171.    * as a bookmark.
  172.    * @param   aData
  173.    *          JSON object of dropped or pasted item properties
  174.    * @param   aContainer
  175.    *          The container being copied into
  176.    * @param   aIndex
  177.    *          The index within the container the item is copied to
  178.    * @returns A nsITransaction object that performs the copy.
  179.    */
  180.   _getURIItemCopyTransaction: function (aData, aContainer, aIndex) {
  181.     return this.ptm.createItem(PlacesUtils._uri(aData.uri), aContainer, aIndex,
  182.                                aData.title, "");
  183.   },
  184.  
  185.   /**
  186.    * Get a transaction for copying a bookmark item from one container to
  187.    * another.
  188.    * @param   aData
  189.    *          JSON object of dropped or pasted item properties
  190.    * @param   aContainer
  191.    *          The container being copied into
  192.    * @param   aIndex
  193.    *          The index within the container the item is copied to
  194.    * @param   [optional] aExcludeAnnotations
  195.    *          Optional, array of annotations (listed by their names) to exclude
  196.    *          when copying the item.
  197.    * @returns A nsITransaction object that performs the copy.
  198.    */
  199.   _getBookmarkItemCopyTransaction:
  200.   function PU__getBookmarkItemCopyTransaction(aData, aContainer, aIndex,
  201.                                               aExcludeAnnotations) {
  202.     var itemURL = PlacesUtils._uri(aData.uri);
  203.     var itemTitle = aData.title;
  204.     var keyword = aData.keyword || null;
  205.     var annos = aData.annos || [];
  206.     // always exclude GUID when copying any item
  207.     var excludeAnnos = [GUID_ANNO];
  208.     if (aExcludeAnnotations)
  209.       excludeAnnos = excludeAnnos.concat(aExcludeAnnotations);
  210.     annos = annos.filter(function(aValue, aIndex, aArray) {
  211.       return excludeAnnos.indexOf(aValue.name) == -1;
  212.     });
  213.     var childTxns = [];
  214.     if (aData.dateAdded)
  215.       childTxns.push(this.ptm.editItemDateAdded(null, aData.dateAdded));
  216.     if (aData.lastModified)
  217.       childTxns.push(this.ptm.editItemLastModified(null, aData.lastModified));
  218.     if (aData.tags) {
  219.       var tags = aData.tags.split(", ");
  220.       // filter out tags already present, so that undo doesn't remove them
  221.       // from pre-existing bookmarks
  222.       var storedTags = PlacesUtils.tagging.getTagsForURI(itemURL, {});
  223.       tags = tags.filter(function (aTag) {
  224.         return (storedTags.indexOf(aTag) == -1);
  225.       }, this);
  226.       if (tags.length)
  227.         childTxns.push(this.ptm.tagURI(itemURL, tags));
  228.     }
  229.  
  230.     return this.ptm.createItem(itemURL, aContainer, aIndex, itemTitle, keyword,
  231.                                annos, childTxns);
  232.   },
  233.  
  234.   /**
  235.    * Gets a transaction for copying (recursively nesting to include children)
  236.    * a folder (or container) and its contents from one folder to another.
  237.    *
  238.    * @param   aData
  239.    *          Unwrapped dropped folder data - Obj containing folder and children
  240.    * @param   aContainer
  241.    *          The container we are copying into
  242.    * @param   aIndex
  243.    *          The index in the destination container to insert the new items
  244.    * @returns A nsITransaction object that will perform the copy.
  245.    */
  246.   _getFolderCopyTransaction:
  247.   function PU__getFolderCopyTransaction(aData, aContainer, aIndex) {
  248.     var self = this;
  249.     function getChildItemsTransactions(aChildren) {
  250.       var childItemsTransactions = [];
  251.       var cc = aChildren.length;
  252.       var index = aIndex;
  253.       for (var i = 0; i < cc; ++i) {
  254.         var txn = null;
  255.         var node = aChildren[i];
  256.  
  257.         // Make sure that items are given the correct index, this will be
  258.         // passed by the transaction manager to the backend for the insertion.
  259.         // Insertion behaves differently if index == DEFAULT_INDEX (append)
  260.         if (aIndex != PlacesUtils.bookmarks.DEFAULT_INDEX)
  261.           index = i;
  262.  
  263.         if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER) {
  264.           if (node.livemark && node.annos) // node is a livemark
  265.             txn = self._getLivemarkCopyTransaction(node, aContainer, index);
  266.           else
  267.             txn = self._getFolderCopyTransaction(node, aContainer, index);
  268.         }
  269.         else if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR)
  270.           txn = self.ptm.createSeparator(-1, index);
  271.         else if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE)
  272.           txn = self._getBookmarkItemCopyTransaction(node, -1, index);
  273.  
  274.         NS_ASSERT(txn, "Unexpected item under a bookmarks folder");
  275.         if (txn)
  276.           childItemsTransactions.push(txn);
  277.       }
  278.       return childItemsTransactions;
  279.     }
  280.  
  281.     // tag folders use tag transactions
  282.     if (aContainer == PlacesUtils.bookmarks.tagsFolder) {
  283.       var txns = [];
  284.       if (aData.children) {
  285.         aData.children.forEach(function(aChild) {
  286.           txns.push(this.ptm.tagURI(PlacesUtils._uri(aChild.uri), [aData.title]));
  287.         }, this);
  288.       }
  289.       return this.ptm.aggregateTransactions("addTags", txns);
  290.     }
  291.     else if (aData.livemark && aData.annos) {
  292.       // Place is a Livemark Container
  293.       return this._getLivemarkCopyTransaction(aData, aContainer, aIndex);
  294.     }
  295.     else {
  296.       var childItems = getChildItemsTransactions(aData.children);
  297.       if (aData.dateAdded)
  298.         childItems.push(this.ptm.editItemDateAdded(null, aData.dateAdded));
  299.       if (aData.lastModified)
  300.         childItems.push(this.ptm.editItemLastModified(null, aData.lastModified));
  301.  
  302.       var annos = aData.annos || [];
  303.       annos = annos.filter(function(aAnno) {
  304.         // always exclude GUID when copying any item
  305.         return aAnno.name != GUID_ANNO;
  306.       });
  307.       return this.ptm.createFolder(aData.title, aContainer, aIndex, annos, childItems);
  308.     }
  309.   },
  310.  
  311.   _getLivemarkCopyTransaction:
  312.   function PU__getLivemarkCopyTransaction(aData, aContainer, aIndex) {
  313.     NS_ASSERT(aData.livemark && aData.annos, "node is not a livemark");
  314.     // Place is a Livemark Container
  315.     var feedURI = null;
  316.     var siteURI = null;
  317.     aData.annos = aData.annos.filter(function(aAnno) {
  318.       if (aAnno.name == LMANNO_FEEDURI) {
  319.         feedURI = PlacesUtils._uri(aAnno.value);
  320.         return false;
  321.       }
  322.       else if (aAnno.name == LMANNO_SITEURI) {
  323.         siteURI = PlacesUtils._uri(aAnno.value);
  324.         return false;
  325.       }
  326.       // always exclude GUID when copying any item
  327.       return aAnno.name != GUID_ANNO;
  328.     });
  329.     return this.ptm.createLivemark(feedURI, siteURI, aData.title, aContainer,
  330.                                    aIndex, aData.annos);
  331.   },
  332.  
  333.   /**
  334.    * Constructs a Transaction for the drop or paste of a blob of data into
  335.    * a container.
  336.    * @param   data
  337.    *          The unwrapped data blob of dropped or pasted data.
  338.    * @param   type
  339.    *          The content type of the data
  340.    * @param   container
  341.    *          The container the data was dropped or pasted into
  342.    * @param   index
  343.    *          The index within the container the item was dropped or pasted at
  344.    * @param   copy
  345.    *          The drag action was copy, so don't move folders or links.
  346.    * @returns An object implementing nsITransaction that can perform
  347.    *          the move/insert.
  348.    */
  349.   makeTransaction: function PU_makeTransaction(data, type, container,
  350.                                                index, copy) {
  351.     switch (data.type) {
  352.       case PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER:
  353.         if (copy)
  354.           return this._getFolderCopyTransaction(data, container, index);
  355.         else { // Move the item
  356.           var id = data.folder ? data.folder.id : data.id;
  357.           return this.ptm.moveItem(id, container, index);
  358.         }
  359.         break;
  360.       case PlacesUtils.TYPE_X_MOZ_PLACE:
  361.         if (data.id <= 0) // non-bookmark item
  362.           return this._getURIItemCopyTransaction(data, container, index);
  363.   
  364.         if (copy) {
  365.           // Copying a child of a live-bookmark by itself should result
  366.           // as a new normal bookmark item (bug 376731)
  367.           return this._getBookmarkItemCopyTransaction(data, container, index,
  368.                                                       ["livemark/bookmarkFeedURI"]);
  369.         }
  370.         else
  371.           return this.ptm.moveItem(data.id, container, index);
  372.         break;
  373.       case PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR:
  374.         // There is no data in a separator, so copying it just amounts to
  375.         // inserting a new separator.
  376.         if (copy)
  377.           return this.ptm.createSeparator(container, index);
  378.         // Move the separator otherwise
  379.         return this.ptm.moveItem(data.id, container, index);
  380.         break;
  381.       default:
  382.         if (type == PlacesUtils.TYPE_X_MOZ_URL || type == PlacesUtils.TYPE_UNICODE) {
  383.           var title = (type == PlacesUtils.TYPE_X_MOZ_URL) ? data.title : data.uri;
  384.           return this.ptm.createItem(PlacesUtils._uri(data.uri), container, index,
  385.                                      title);
  386.         }
  387.     }
  388.     return null;
  389.   },
  390.  
  391.   /**
  392.    * Methods to show the bookmarkProperties dialog in its various modes.
  393.    *
  394.    * The showMinimalAdd* methods open the dialog by its alternative URI. Thus
  395.    * they persist the dialog dimensions separately from the showAdd* methods.
  396.    * Note these variants also do not return the dialog "performed" state since
  397.    * they may not open the dialog modally.
  398.    */
  399.  
  400.   /**
  401.    * Shows the "Add Bookmark" dialog.
  402.    *
  403.    * @param [optional] aURI
  404.    *        An nsIURI object for which the "add bookmark" dialog is
  405.    *        to be shown.
  406.    * @param [optional] aTitle
  407.    *        The default title for the new bookmark.
  408.    * @param [optional] aDescription
  409.             The default description for the new bookmark
  410.    * @param [optional] aDefaultInsertionPoint
  411.    *        The default insertion point for the new item. If set, the folder
  412.    *        picker would be hidden unless aShowPicker is set to true, in which
  413.    *        case the dialog only uses the folder identifier from the insertion
  414.    *        point as the initially selected item in the folder picker.
  415.    * @param [optional] aShowPicker
  416.    *        see above
  417.    * @param [optional] aLoadInSidebar
  418.    *        If true, the dialog will default to load the new item in the
  419.    *        sidebar (as a web panel).
  420.    * @param [optional] aKeyword
  421.    *        The default keyword for the new bookmark. The keyword field
  422.    *        will be shown in the dialog if this is used.
  423.    * @param [optional] aPostData
  424.    *        POST data for POST-style keywords.
  425.    * @param [optional] aCharSet
  426.    *        The character set for the bookmarked page.
  427.    * @return true if any transaction has been performed.
  428.    *
  429.    * Notes:
  430.    *  - the location, description and "loadInSidebar" fields are
  431.    *    visible only if there is no initial URI (aURI is null).
  432.    *  - When aDefaultInsertionPoint is not set, the dialog defaults to the
  433.    *    bookmarks root folder.
  434.    */
  435.   showAddBookmarkUI: function PU_showAddBookmarkUI(aURI,
  436.                                                    aTitle,
  437.                                                    aDescription,
  438.                                                    aDefaultInsertionPoint,
  439.                                                    aShowPicker,
  440.                                                    aLoadInSidebar,
  441.                                                    aKeyword,
  442.                                                    aPostData,
  443.                                                    aCharSet) {
  444.     var info = {
  445.       action: "add",
  446.       type: "bookmark"
  447.     };
  448.  
  449.     if (aURI)
  450.       info.uri = aURI;
  451.  
  452.     // allow default empty title
  453.     if (typeof(aTitle) == "string")
  454.       info.title = aTitle;
  455.  
  456.     if (aDescription)
  457.       info.description = aDescription;
  458.  
  459.     if (aDefaultInsertionPoint) {
  460.       info.defaultInsertionPoint = aDefaultInsertionPoint;
  461.       if (!aShowPicker)
  462.         info.hiddenRows = ["folder picker"];
  463.     }
  464.  
  465.     if (aLoadInSidebar)
  466.       info.loadBookmarkInSidebar = true;
  467.  
  468.     if (typeof(aKeyword) == "string") {
  469.       info.keyword = aKeyword;
  470.       if (typeof(aPostData) == "string")
  471.         info.postData = aPostData;
  472.       if (typeof(aCharSet) == "string")
  473.         info.charSet = aCharSet;
  474.     }
  475.  
  476.     return this._showBookmarkDialog(info);
  477.   },
  478.  
  479.   /**
  480.    * @see showAddBookmarkUI
  481.    * This opens the dialog with only the name and folder pickers visible by
  482.    * default.
  483.    *
  484.    * You can still pass in the various paramaters as the default properties
  485.    * for the new bookmark.
  486.    *
  487.    * The keyword field will be visible only if the aKeyword parameter
  488.    * was used.
  489.    */
  490.   showMinimalAddBookmarkUI:
  491.   function PU_showMinimalAddBookmarkUI(aURI, aTitle, aDescription,
  492.                                        aDefaultInsertionPoint, aShowPicker,
  493.                                        aLoadInSidebar, aKeyword, aPostData,
  494.                                        aCharSet) {
  495.     var info = {
  496.       action: "add",
  497.       type: "bookmark",
  498.       hiddenRows: ["location", "description", "loadInSidebar"]
  499.     };
  500.     if (aURI)
  501.       info.uri = aURI;
  502.  
  503.     // allow default empty title
  504.     if (typeof(aTitle) == "string")
  505.       info.title = aTitle;
  506.  
  507.     if (aDescription)
  508.       info.description = aDescription;
  509.  
  510.     if (aDefaultInsertionPoint) {
  511.       info.defaultInsertionPoint = aDefaultInsertionPoint;
  512.       if (!aShowPicker)
  513.         info.hiddenRows.push("folder picker");
  514.     }
  515.  
  516.     if (aLoadInSidebar)
  517.       info.loadBookmarkInSidebar = true;
  518.  
  519.     if (typeof(aKeyword) == "string") {
  520.       info.keyword = aKeyword;
  521.       if (typeof(aPostData) == "string")
  522.         info.postData = aPostData;
  523.       if (typeof(aCharSet) == "string")
  524.         info.charSet = aCharSet;
  525.     }
  526.     else
  527.       info.hiddenRows.push("keyword");
  528.  
  529.     this._showBookmarkDialog(info, true);
  530.   },
  531.  
  532.   /**
  533.    * Shows the "Add Live Bookmark" dialog.
  534.    *
  535.    * @param [optional] aFeedURI
  536.    *        The feed URI for which the dialog is to be shown (nsIURI).
  537.    * @param [optional] aSiteURI
  538.    *        The site URI for the new live-bookmark (nsIURI).
  539.    * @param [optional] aDefaultInsertionPoint
  540.    *        The default insertion point for the new item. If set, the folder
  541.    *        picker would be hidden unless aShowPicker is set to true, in which
  542.    *        case the dialog only uses the folder identifier from the insertion
  543.    *        point as the initially selected item in the folder picker.
  544.    * @param [optional] aShowPicker
  545.    *        see above
  546.    * @return true if any transaction has been performed.
  547.    *
  548.    * Notes:
  549.    *  - the feedURI and description fields are visible only if there is no
  550.    *    initial feed URI (aFeedURI is null).
  551.    *  - When aDefaultInsertionPoint is not set, the dialog defaults to the
  552.    *    bookmarks root folder.
  553.    */
  554.   showAddLivemarkUI: function PU_showAddLivemarkURI(aFeedURI,
  555.                                                     aSiteURI,
  556.                                                     aTitle,
  557.                                                     aDescription,
  558.                                                     aDefaultInsertionPoint,
  559.                                                     aShowPicker) {
  560.     var info = {
  561.       action: "add",
  562.       type: "livemark"
  563.     };
  564.  
  565.     if (aFeedURI)
  566.       info.feedURI = aFeedURI;
  567.     if (aSiteURI)
  568.       info.siteURI = aSiteURI;
  569.  
  570.     // allow default empty title
  571.     if (typeof(aTitle) == "string")
  572.       info.title = aTitle;
  573.  
  574.     if (aDescription)
  575.       info.description = aDescription;
  576.  
  577.     if (aDefaultInsertionPoint) {
  578.       info.defaultInsertionPoint = aDefaultInsertionPoint;
  579.       if (!aShowPicker)
  580.         info.hiddenRows = ["folder picker"];
  581.     }
  582.     return this._showBookmarkDialog(info);
  583.   },
  584.  
  585.   /**
  586.    * @see showAddLivemarkUI
  587.    * This opens the dialog with only the name and folder pickers visible by
  588.    * default.
  589.    *
  590.    * You can still pass in the various paramaters as the default properties
  591.    * for the new live-bookmark.
  592.    */
  593.   showMinimalAddLivemarkUI:
  594.   function PU_showMinimalAddLivemarkURI(aFeedURI, aSiteURI, aTitle,
  595.                                         aDescription, aDefaultInsertionPoint,
  596.                                         aShowPicker) {
  597.     var info = {
  598.       action: "add",
  599.       type: "livemark",
  600.       hiddenRows: ["feedURI", "siteURI", "description"]
  601.     };
  602.  
  603.     if (aFeedURI)
  604.       info.feedURI = aFeedURI;
  605.     if (aSiteURI)
  606.       info.siteURI = aSiteURI;
  607.  
  608.     // allow default empty title
  609.     if (typeof(aTitle) == "string")
  610.       info.title = aTitle;
  611.  
  612.     if (aDescription)
  613.       info.description = aDescription;
  614.  
  615.     if (aDefaultInsertionPoint) {
  616.       info.defaultInsertionPoint = aDefaultInsertionPoint;
  617.       if (!aShowPicker)
  618.         info.hiddenRows.push("folder picker");
  619.     }
  620.     this._showBookmarkDialog(info, true);
  621.   },
  622.  
  623.   /**
  624.    * Show an "Add Bookmarks" dialog to allow the adding of a folder full
  625.    * of bookmarks corresponding to the objects in the uriList.  This will
  626.    * be called most often as the result of a "Bookmark All Tabs..." command.
  627.    *
  628.    * @param aURIList  List of nsIURI objects representing the locations
  629.    *                  to be bookmarked.
  630.    * @return true if any transaction has been performed.
  631.    */
  632.   showMinimalAddMultiBookmarkUI: function PU_showAddMultiBookmarkUI(aURIList) {
  633.     NS_ASSERT(aURIList.length,
  634.               "showAddMultiBookmarkUI expects a list of nsIURI objects");
  635.     var info = {
  636.       action: "add",
  637.       type: "folder",
  638.       hiddenRows: ["description"],
  639.       URIList: aURIList
  640.     };
  641.     this._showBookmarkDialog(info, true);
  642.   },
  643.  
  644.   /**
  645.    * Opens the properties dialog for a given item identifier.
  646.    *
  647.    * @param aItemId
  648.    *        item identifier for which the properties are to be shown
  649.    * @param aType
  650.    *        item type, either "bookmark" or "folder"
  651.    * @return true if any transaction has been performed.
  652.    */
  653.   showItemProperties: function PU_showItemProperties(aItemId, aType) {
  654.     var info = {
  655.       action: "edit",
  656.       type: aType,
  657.       itemId: aItemId
  658.     };
  659.     return this._showBookmarkDialog(info);
  660.   },
  661.  
  662.   /**
  663.    * Shows the "New Folder" dialog.
  664.    *
  665.    * @param [optional] aTitle
  666.    *        The default title for the new bookmark.
  667.    * @param [optional] aDefaultInsertionPoint
  668.    *        The default insertion point for the new item. If set, the folder
  669.    *        picker would be hidden unless aShowPicker is set to true, in which
  670.    *        case the dialog only uses the folder identifier from the insertion
  671.    *        point as the initially selected item in the folder picker.
  672.    * @param [optional] aShowPicker
  673.    *        see above
  674.    * @return true if any transaction has been performed.
  675.    */
  676.   showAddFolderUI:
  677.   function PU_showAddFolderUI(aTitle, aDefaultInsertionPoint, aShowPicker) {
  678.     var info = {
  679.       action: "add",
  680.       type: "folder",
  681.       hiddenRows: []
  682.     };
  683.  
  684.     // allow default empty title
  685.     if (typeof(aTitle) == "string")
  686.       info.title = aTitle;
  687.  
  688.     if (aDefaultInsertionPoint) {
  689.       info.defaultInsertionPoint = aDefaultInsertionPoint;
  690.       if (!aShowPicker)
  691.         info.hiddenRows.push("folder picker");
  692.     }
  693.     return this._showBookmarkDialog(info);
  694.   },
  695.  
  696.   /**
  697.    * Shows the bookmark dialog corresponding to the specified info
  698.    *
  699.    * @param aInfo
  700.    *        Describes the item to be edited/added in the dialog.
  701.    *        See documentation at the top of bookmarkProperties.js
  702.    * @param aMinimalUI
  703.    *        [optional] if true, the dialog is opened by its alternative
  704.    *        chrome: uri.
  705.    *
  706.    * Note: In minimal UI mode, we open the dialog non-modal on any system but
  707.    *       Mac OS X.
  708.    * @return true if any transaction has been performed, false otherwise.
  709.    * Note: the return value of this method is not reliable in minimal UI mode
  710.    * since the dialog may not be opened modally.
  711.    */
  712.   _showBookmarkDialog: function PU__showBookmarkDialog(aInfo, aMinimalUI) {
  713.     var dialogURL = aMinimalUI ?
  714.                     "chrome://browser/content/places/bookmarkProperties2.xul" :
  715.                     "chrome://browser/content/places/bookmarkProperties.xul";
  716.  
  717.     var features;
  718.     if (aMinimalUI)
  719. //@line 726 "/build/buildd/firefox-3.0-3.0.14+build2+nobinonly/build-tree/mozilla/browser/components/places/content/utils.js"
  720.       features = "centerscreen,chrome,dialog,resizable,dependent";
  721. //@line 728 "/build/buildd/firefox-3.0-3.0.14+build2+nobinonly/build-tree/mozilla/browser/components/places/content/utils.js"
  722.     else
  723.       features = "centerscreen,chrome,modal,resizable=no";
  724.     window.openDialog(dialogURL, "",  features, aInfo);
  725.     return ("performed" in aInfo && aInfo.performed);
  726.   },
  727.  
  728.   /**
  729.    * Returns the closet ancestor places view for the given DOM node
  730.    * @param aNode
  731.    *        a DOM node
  732.    * @return the closet ancestor places view if exists, null otherwsie.
  733.    */
  734.   getViewForNode: function PU_getViewForNode(aNode) {
  735.     var node = aNode;
  736.  
  737.     // the view for a <menu> of which its associated menupopup is a places view,
  738.     // is the menupopup
  739.     if (node.localName == "menu" && !node.node &&
  740.         node.firstChild.getAttribute("type") == "places")
  741.       return node.firstChild;
  742.  
  743.     while (node) {
  744.       // XXXmano: Use QueryInterface(nsIPlacesView) once we implement it...
  745.       if (node.getAttribute("type") == "places")
  746.         return node;
  747.  
  748.       node = node.parentNode;
  749.     }
  750.  
  751.     return null;
  752.   },
  753.  
  754.   /**
  755.    * By calling this before we visit a URL, we will use TRANSITION_TYPED
  756.    * as the transition for the visit to that URL (if we don't have a referrer).
  757.    * This is used when visiting pages from the history menu, history sidebar,
  758.    * url bar, url autocomplete results, and history searches from the places
  759.    * organizer.  If we don't call this, we'll treat those visits as
  760.    * TRANSITION_LINK.
  761.    */
  762.   markPageAsTyped: function PU_markPageAsTyped(aURL) {
  763.     PlacesUtils.history.QueryInterface(Ci.nsIBrowserHistory)
  764.                .markPageAsTyped(this.createFixedURI(aURL));
  765.   },
  766.  
  767.   /**
  768.    * By calling this before we visit a URL, we will use TRANSITION_BOOKMARK
  769.    * as the transition for the visit to that URL (if we don't have a referrer).
  770.    * This is used when visiting pages from the bookmarks menu, 
  771.    * personal toolbar, and bookmarks from within the places organizer.
  772.    * If we don't call this, we'll treat those visits as TRANSITION_LINK.
  773.    */
  774.   markPageAsFollowedBookmark: function PU_markPageAsFollowedBookmark(aURL) {
  775.     PlacesUtils.history.markPageAsFollowedBookmark(this.createFixedURI(aURL));
  776.   },
  777.  
  778.   /**
  779.    * Allows opening of javascript/data URI only if the given node is
  780.    * bookmarked (see bug 224521).
  781.    * @param aURINode
  782.    *        a URI node
  783.    * @return true if it's safe to open the node in the browser, false otherwise.
  784.    *
  785.    */
  786.   checkURLSecurity: function PU_checkURLSecurity(aURINode) {
  787.     if (!PlacesUtils.nodeIsBookmark(aURINode)) {
  788.       var uri = PlacesUtils._uri(aURINode.uri);
  789.       if (uri.schemeIs("javascript") || uri.schemeIs("data")) {
  790.         const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties";
  791.         var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"].
  792.                              getService(Ci.nsIStringBundleService).
  793.                              createBundle(BRANDING_BUNDLE_URI).
  794.                              GetStringFromName("brandShortName");
  795.         var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
  796.                             getService(Ci.nsIPromptService);
  797.  
  798.         var errorStr = this.getString("load-js-data-url-error");
  799.         promptService.alert(window, brandShortName, errorStr);
  800.         return false;
  801.       }
  802.     }
  803.     return true;
  804.   },
  805.  
  806.   /**
  807.    * Get the description associated with a document, as specified in a <META>
  808.    * element.
  809.    * @param   doc
  810.    *          A DOM Document to get a description for
  811.    * @returns A description string if a META element was discovered with a
  812.    *          "description" or "httpequiv" attribute, empty string otherwise.
  813.    */
  814.   getDescriptionFromDocument: function PU_getDescriptionFromDocument(doc) {
  815.     var metaElements = doc.getElementsByTagName("META");
  816.     for (var i = 0; i < metaElements.length; ++i) {
  817.       if (metaElements[i].name.toLowerCase() == "description" ||
  818.           metaElements[i].httpEquiv.toLowerCase() == "description") {
  819.         return metaElements[i].content;
  820.       }
  821.     }
  822.     return "";
  823.   },
  824.  
  825.   /**
  826.    * Retrieve the description of an item
  827.    * @param aItemId
  828.    *        item identifier
  829.    * @returns the description of the given item, or an empty string if it is
  830.    * not set.
  831.    */
  832.   getItemDescription: function PU_getItemDescription(aItemId) {
  833.     if (PlacesUtils.annotations.itemHasAnnotation(aItemId, DESCRIPTION_ANNO))
  834.       return PlacesUtils.annotations.getItemAnnotation(aItemId, DESCRIPTION_ANNO);
  835.     return "";
  836.   },
  837.  
  838.   /**
  839.    * Gives the user a chance to cancel loading lots of tabs at once
  840.    */
  841.   _confirmOpenInTabs: function PU__confirmOpenInTabs(numTabsToOpen) {
  842.     var pref = Cc["@mozilla.org/preferences-service;1"].
  843.                getService(Ci.nsIPrefBranch);
  844.  
  845.     const kWarnOnOpenPref = "browser.tabs.warnOnOpen";
  846.     var reallyOpen = true;
  847.     if (pref.getBoolPref(kWarnOnOpenPref)) {
  848.       if (numTabsToOpen >= pref.getIntPref("browser.tabs.maxOpenBeforeWarn")) {
  849.         var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
  850.                             getService(Ci.nsIPromptService);
  851.  
  852.         // default to true: if it were false, we wouldn't get this far
  853.         var warnOnOpen = { value: true };
  854.  
  855.         var messageKey = "tabs.openWarningMultipleBranded";
  856.         var openKey = "tabs.openButtonMultiple";
  857.         const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties";
  858.         var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"].
  859.                              getService(Ci.nsIStringBundleService).
  860.                              createBundle(BRANDING_BUNDLE_URI).
  861.                              GetStringFromName("brandShortName");
  862.  
  863.         var buttonPressed = promptService.confirmEx(window,
  864.           this.getString("tabs.openWarningTitle"),
  865.           this.getFormattedString(messageKey, [numTabsToOpen, brandShortName]),
  866.           (promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0)
  867.            + (promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1),
  868.           this.getString(openKey), null, null,
  869.           this.getFormattedString("tabs.openWarningPromptMeBranded",
  870.                                   [brandShortName]), warnOnOpen);
  871.  
  872.         reallyOpen = (buttonPressed == 0);
  873.         // don't set the pref unless they press OK and it's false
  874.         if (reallyOpen && !warnOnOpen.value)
  875.           pref.setBoolPref(kWarnOnOpenPref, false);
  876.       }
  877.     }
  878.     return reallyOpen;
  879.   },
  880.  
  881.   /** aItemsToOpen needs to be an array of objects of the form:
  882.     * {uri: string, isBookmark: boolean}
  883.     */
  884.   _openTabset: function PU__openTabset(aItemsToOpen, aEvent) {
  885.     var urls = [];
  886.     for (var i = 0; i < aItemsToOpen.length; i++) {
  887.       var item = aItemsToOpen[i];
  888.       if (item.isBookmark)
  889.         this.markPageAsFollowedBookmark(item.uri);
  890.       else
  891.         this.markPageAsTyped(item.uri);
  892.  
  893.       urls.push(item.uri);
  894.     }
  895.  
  896.     var browserWindow = getTopWin();
  897.     var where = browserWindow ?
  898.                 whereToOpenLink(aEvent, false, true) : "window";
  899.     if (where == "window") {
  900.       window.openDialog(getBrowserURL(), "_blank",
  901.                         "chrome,all,dialog=no", urls.join("|"));
  902.       return;
  903.     }
  904.  
  905.     var loadInBackground = where == "tabshifted" ? true : false;
  906.     var replaceCurrentTab = where == "tab" ? false : true;
  907.     browserWindow.getBrowser().loadTabs(urls, loadInBackground,
  908.                                         replaceCurrentTab);
  909.   },
  910.  
  911.   openContainerNodeInTabs: function PU_openContainerInTabs(aNode, aEvent) {
  912.     var urlsToOpen = PlacesUtils.getURLsForContainerNode(aNode);
  913.     if (!this._confirmOpenInTabs(urlsToOpen.length))
  914.       return;
  915.  
  916.     this._openTabset(urlsToOpen, aEvent);
  917.   },
  918.  
  919.   openURINodesInTabs: function PU_openURINodesInTabs(aNodes, aEvent) {
  920.     var urlsToOpen = [];
  921.     for (var i=0; i < aNodes.length; i++) {
  922.       // skip over separators and folders
  923.       if (PlacesUtils.nodeIsURI(aNodes[i]))
  924.         urlsToOpen.push({uri: aNodes[i].uri, isBookmark: PlacesUtils.nodeIsBookmark(aNodes[i])});
  925.     }
  926.     this._openTabset(urlsToOpen, aEvent);
  927.   },
  928.  
  929.   /**
  930.    * Loads the node's URL in the appropriate tab or window or as a web
  931.    * panel given the user's preference specified by modifier keys tracked by a
  932.    * DOM mouse/key event.
  933.    * @param   aNode
  934.    *          An uri result node.
  935.    * @param   aEvent
  936.    *          The DOM mouse/key event with modifier keys set that track the
  937.    *          user's preferred destination window or tab.
  938.    */
  939.   openNodeWithEvent: function PU_openNodeWithEvent(aNode, aEvent) {
  940.     this.openNodeIn(aNode, whereToOpenLink(aEvent));
  941.   },
  942.   
  943.   /**
  944.    * Loads the node's URL in the appropriate tab or window or as a
  945.    * web panel.
  946.    * see also openUILinkIn
  947.    */
  948.   openNodeIn: function PU_openNodeIn(aNode, aWhere) {
  949.     if (aNode && PlacesUtils.nodeIsURI(aNode) &&
  950.         this.checkURLSecurity(aNode)) {
  951.       var isBookmark = PlacesUtils.nodeIsBookmark(aNode);
  952.  
  953.       if (isBookmark)
  954.         this.markPageAsFollowedBookmark(aNode.uri);
  955.       else
  956.         this.markPageAsTyped(aNode.uri);
  957.  
  958.       // Check whether the node is a bookmark which should be opened as
  959.       // a web panel
  960.       if (aWhere == "current" && isBookmark) {
  961.         if (PlacesUtils.annotations
  962.                        .itemHasAnnotation(aNode.itemId, LOAD_IN_SIDEBAR_ANNO)) {
  963.           var w = getTopWin();
  964.           if (w) {
  965.             w.openWebPanel(aNode.title, aNode.uri);
  966.             return;
  967.           }
  968.         }
  969.       }
  970.       openUILinkIn(aNode.uri, aWhere);
  971.     }
  972.   },
  973.  
  974.   /**
  975.    * Helper for the toolbar and menu views
  976.    */
  977.   createMenuItemForNode:
  978.   function PUU_createMenuItemForNode(aNode, aContainersMap) {
  979.     var element;
  980.     var type = aNode.type;
  981.     if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR)
  982.       element = document.createElement("menuseparator");
  983.     else {
  984.       var iconURI = aNode.icon;
  985.       var iconURISpec = "";
  986.       if (iconURI)
  987.         iconURISpec = iconURI.spec;
  988.  
  989.       if (PlacesUtils.uriTypes.indexOf(type) != -1) {
  990.         element = document.createElement("menuitem");
  991.         element.className = "menuitem-iconic bookmark-item";
  992.       }
  993.       else if (PlacesUtils.containerTypes.indexOf(type) != -1) {
  994.         element = document.createElement("menu");
  995.         element.setAttribute("container", "true");
  996.  
  997.         if (aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY) {
  998.           element.setAttribute("query", "true");
  999.           if (PlacesUtils.nodeIsTagQuery(aNode))
  1000.             element.setAttribute("tagContainer", "true");
  1001.           else if (PlacesUtils.nodeIsDay(aNode))
  1002.             element.setAttribute("dayContainer", "true");
  1003.           else if (PlacesUtils.nodeIsHost(aNode))
  1004.             element.setAttribute("hostContainer", "true");
  1005.         }
  1006.         else if (aNode.itemId != -1) {
  1007.           if (PlacesUtils.nodeIsLivemarkContainer(aNode))
  1008.             element.setAttribute("livemark", "true");
  1009.         }
  1010.  
  1011.         var popup = document.createElement("menupopup");
  1012.         popup.setAttribute("placespopup", "true");
  1013.         popup._resultNode = asContainer(aNode);
  1014. //@line 1021 "/build/buildd/firefox-3.0-3.0.14+build2+nobinonly/build-tree/mozilla/browser/components/places/content/utils.js"
  1015.         // no context menu on mac
  1016.         popup.setAttribute("context", "placesContext");
  1017. //@line 1024 "/build/buildd/firefox-3.0-3.0.14+build2+nobinonly/build-tree/mozilla/browser/components/places/content/utils.js"
  1018.         element.appendChild(popup);
  1019.         if (aContainersMap)
  1020.           aContainersMap.push({ resultNode: aNode, domNode: popup });
  1021.         element.className = "menu-iconic bookmark-item";
  1022.       }
  1023.       else
  1024.         throw "Unexpected node";
  1025.  
  1026.       element.setAttribute("label", this.getBestTitle(aNode));
  1027.  
  1028.       if (iconURISpec)
  1029.         element.setAttribute("image", iconURISpec);
  1030.     }
  1031.     element.node = aNode;
  1032.     element.node.viewIndex = 0;
  1033.  
  1034.     return element;
  1035.   },
  1036.  
  1037.   cleanPlacesPopup: function PU_cleanPlacesPopup(aPopup) {
  1038.     // Find static menuitems at the start and at the end of the menupopup,
  1039.     // marked by builder="start" and builder="end" attributes, and set
  1040.     // markers to keep track of their indices.
  1041.     var items = [];
  1042.     aPopup._startMarker = -1;
  1043.     aPopup._endMarker = -1;
  1044.     for (var i = 0; i < aPopup.childNodes.length; ++i) {
  1045.       var item = aPopup.childNodes[i];
  1046.       if (item.getAttribute("builder") == "start") {
  1047.         aPopup._startMarker = i;
  1048.         continue;
  1049.       }
  1050.       if (item.getAttribute("builder") == "end") {
  1051.         aPopup._endMarker = i;
  1052.         continue;
  1053.       }
  1054.       if ((aPopup._startMarker != -1) && (aPopup._endMarker == -1))
  1055.         items.push(item);
  1056.     }
  1057.  
  1058.     // If static items at the beginning were found, remove all items between
  1059.     // them and the static content at the end.
  1060.     for (var i = 0; i < items.length; ++i) {
  1061.       // skip the empty menu item
  1062.       if (aPopup._emptyMenuItem != items[i]) {
  1063.         aPopup.removeChild(items[i]);
  1064.         if (aPopup._endMarker > 0)
  1065.           --aPopup._endMarker;
  1066.       }
  1067.     }
  1068.  
  1069.     // If no static items were found at the beginning, remove all items before
  1070.     // the static items at the end.
  1071.     if (aPopup._startMarker == -1) {
  1072.       var end = aPopup._endMarker == -1 ?
  1073.                 aPopup.childNodes.length - 1 : aPopup._endMarker - 1;
  1074.       for (var i = end; i >= 0; i--) {
  1075.         // skip the empty menu item
  1076.         if (aPopup._emptyMenuItem != aPopup.childNodes[i]) {
  1077.           aPopup.removeChild(aPopup.childNodes[i]);
  1078.           if (aPopup._endMarker > 0)
  1079.             --aPopup._endMarker;
  1080.         }
  1081.       }
  1082.     }
  1083.   },
  1084.  
  1085.   getBestTitle: function PU_getBestTitle(aNode) {
  1086.     var title;
  1087.     if (!aNode.title && PlacesUtils.uriTypes.indexOf(aNode.type) != -1) {
  1088.       // if node title is empty, try to set the label using host and filename
  1089.       // PlacesUtils._uri() will throw if aNode.uri is not a valid URI
  1090.       try {
  1091.         var uri = PlacesUtils._uri(aNode.uri);
  1092.         var host = uri.host;
  1093.         var fileName = uri.QueryInterface(Ci.nsIURL).fileName;
  1094.         // if fileName is empty, use path to distinguish labels
  1095.         title = host + (fileName ?
  1096.                         (host ? "/" + this.ellipsis + "/" : "") + fileName :
  1097.                         uri.path);
  1098.       }
  1099.       catch (e) {
  1100.         // Use (no title) for non-standard URIs (data:, javascript:, ...)
  1101.         title = "";
  1102.       }
  1103.     }
  1104.     else
  1105.       title = aNode.title;
  1106.  
  1107.     return title || this.getString("noTitle");
  1108.   },
  1109.  
  1110.   get leftPaneQueries() {    
  1111.     // build the map
  1112.     this.leftPaneFolderId;
  1113.     return this.leftPaneQueries;
  1114.   },
  1115.  
  1116.   // get the folder id for the organizer left-pane folder
  1117.   get leftPaneFolderId() {
  1118.     var leftPaneRoot = -1;
  1119.     var allBookmarksId;
  1120.     var items = PlacesUtils.annotations
  1121.                            .getItemsWithAnnotation(ORGANIZER_FOLDER_ANNO, {});
  1122.     if (items.length != 0 && items[0] != -1) {
  1123.       leftPaneRoot = items[0];
  1124.       // check organizer left pane version
  1125.       var version = PlacesUtils.annotations
  1126.                                .getItemAnnotation(leftPaneRoot, ORGANIZER_FOLDER_ANNO);
  1127.       if (version != ORGANIZER_LEFTPANE_VERSION) {
  1128.         // If version is not valid we must rebuild the left pane.
  1129.         PlacesUtils.bookmarks.removeFolder(leftPaneRoot);
  1130.         leftPaneRoot = -1;
  1131.       }
  1132.     }
  1133.  
  1134.     if (leftPaneRoot != -1) {
  1135.       // Build the leftPaneQueries Map
  1136.       delete this.leftPaneQueries;
  1137.       this.leftPaneQueries = {};
  1138.       var items = PlacesUtils.annotations
  1139.                              .getItemsWithAnnotation(ORGANIZER_QUERY_ANNO, {});
  1140.       for (var i=0; i < items.length; i++) {
  1141.         var queryName = PlacesUtils.annotations
  1142.                                    .getItemAnnotation(items[i], ORGANIZER_QUERY_ANNO);
  1143.         this.leftPaneQueries[queryName] = items[i];
  1144.       }
  1145.       delete this.leftPaneFolderId;
  1146.       return this.leftPaneFolderId = leftPaneRoot;
  1147.     }
  1148.  
  1149.     var self = this;
  1150.     const EXPIRE_NEVER = PlacesUtils.annotations.EXPIRE_NEVER;
  1151.     var callback = {
  1152.       runBatched: function(aUserData) {
  1153.         delete self.leftPaneQueries;
  1154.         self.leftPaneQueries = { };
  1155.  
  1156.         // Left Pane Root Folder
  1157.         leftPaneRoot = PlacesUtils.bookmarks.createFolder(PlacesUtils.placesRootId, "", -1);
  1158.         // ensure immediate children can't be removed
  1159.         PlacesUtils.bookmarks.setFolderReadonly(leftPaneRoot, true);
  1160.  
  1161.         // History Query
  1162.         let uri = PlacesUtils._uri("place:sort=4&");
  1163.         let title = self.getString("OrganizerQueryHistory");
  1164.         let itemId = PlacesUtils.bookmarks.insertBookmark(leftPaneRoot, uri, -1, title);
  1165.         PlacesUtils.annotations.setItemAnnotation(itemId, ORGANIZER_QUERY_ANNO,
  1166.                                                   "History", 0, EXPIRE_NEVER);
  1167.         self.leftPaneQueries["History"] = itemId;
  1168.  
  1169.         // XXX: Downloads
  1170.  
  1171.         // Tags Query
  1172.         uri = PlacesUtils._uri("place:type=" +
  1173.                           Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY +
  1174.                           "&sort=" +
  1175.                           Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING);
  1176.         title = PlacesUtils.bookmarks.getItemTitle(PlacesUtils.tagsFolderId);
  1177.         itemId = PlacesUtils.bookmarks.insertBookmark(leftPaneRoot, uri, -1, title);
  1178.         PlacesUtils.annotations.setItemAnnotation(itemId, ORGANIZER_QUERY_ANNO,
  1179.                                                   "Tags", 0, EXPIRE_NEVER);
  1180.         self.leftPaneQueries["Tags"] = itemId;
  1181.  
  1182.         // All Bookmarks Folder
  1183.         title = self.getString("OrganizerQueryAllBookmarks");
  1184.         itemId = PlacesUtils.bookmarks.createFolder(leftPaneRoot, title, -1);
  1185.         allBookmarksId = itemId;
  1186.         PlacesUtils.annotations.setItemAnnotation(itemId, ORGANIZER_QUERY_ANNO,
  1187.                                                   "AllBookmarks", 0, EXPIRE_NEVER);
  1188.         self.leftPaneQueries["AllBookmarks"] = itemId;
  1189.  
  1190.         // disallow manipulating this folder within the organizer UI
  1191.         PlacesUtils.bookmarks.setFolderReadonly(allBookmarksId, true);
  1192.  
  1193.         // All Bookmarks->Bookmarks Toolbar Query
  1194.         uri = PlacesUtils._uri("place:folder=TOOLBAR");
  1195.         itemId = PlacesUtils.bookmarks.insertBookmark(allBookmarksId, uri, -1, null);
  1196.         PlacesUtils.annotations.setItemAnnotation(itemId, ORGANIZER_QUERY_ANNO,
  1197.                                                   "BookmarksToolbar", 0, EXPIRE_NEVER);
  1198.         self.leftPaneQueries["BookmarksToolbar"] = itemId;
  1199.  
  1200.         // All Bookmarks->Bookmarks Menu Query
  1201.         uri = PlacesUtils._uri("place:folder=BOOKMARKS_MENU");
  1202.         itemId = PlacesUtils.bookmarks.insertBookmark(allBookmarksId, uri, -1, null);
  1203.         PlacesUtils.annotations.setItemAnnotation(itemId, ORGANIZER_QUERY_ANNO,
  1204.                                                   "BookmarksMenu", 0, EXPIRE_NEVER);
  1205.         self.leftPaneQueries["BookmarksMenu"] = itemId;
  1206.  
  1207.         // All Bookmarks->Unfiled bookmarks
  1208.         uri = PlacesUtils._uri("place:folder=UNFILED_BOOKMARKS");
  1209.         itemId = PlacesUtils.bookmarks.insertBookmark(allBookmarksId, uri, -1, null);
  1210.         PlacesUtils.annotations.setItemAnnotation(itemId, ORGANIZER_QUERY_ANNO,
  1211.                                                   "UnfiledBookmarks", 0,
  1212.                                                   EXPIRE_NEVER);
  1213.         self.leftPaneQueries["UnfiledBookmarks"] = itemId;
  1214.  
  1215.         // disallow manipulating this folder within the organizer UI
  1216.         PlacesUtils.bookmarks.setFolderReadonly(leftPaneRoot, true);
  1217.       }
  1218.     };
  1219.     PlacesUtils.bookmarks.runInBatchMode(callback, null);
  1220.     PlacesUtils.annotations.setItemAnnotation(leftPaneRoot,
  1221.                                               ORGANIZER_FOLDER_ANNO,
  1222.                                               ORGANIZER_LEFTPANE_VERSION,
  1223.                                               0, EXPIRE_NEVER);
  1224.     delete this.leftPaneFolderId;
  1225.     return this.leftPaneFolderId = leftPaneRoot;
  1226.   },
  1227.  
  1228.   get allBookmarksFolderId() {
  1229.     // ensure the left-pane root is initialized;
  1230.     this.leftPaneFolderId;
  1231.     delete this.allBookmarksFolderId;
  1232.     return this.allBookmarksFolderId = this.leftPaneQueries["AllBookmarks"];
  1233.   }
  1234. };
  1235.  
  1236. PlacesUIUtils.placesFlavors = [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
  1237.                              PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR,
  1238.                              PlacesUtils.TYPE_X_MOZ_PLACE];
  1239.  
  1240. PlacesUIUtils.GENERIC_VIEW_DROP_TYPES = [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
  1241.                                        PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR,
  1242.                                        PlacesUtils.TYPE_X_MOZ_PLACE,
  1243.                                        PlacesUtils.TYPE_X_MOZ_URL,
  1244.                                        PlacesUtils.TYPE_UNICODE];
  1245.