home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2009 May / maximum-cd-2009-05.iso / DiscContents / Firefox Setup 3.0.6.exe / nonlocalized / components / nsPlacesTransactionsService.js < prev    next >
Encoding:
Text File  |  2009-01-19  |  33.0 KB  |  977 lines

  1. /* -*- Mode: IDL; tab-width: 2; 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.  *
  19.  * Portions created by the Initial Developer are Copyright (C) 2005
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *   Sungjoon Steve Won <stevewon@gmail.com> (Original Author)
  24.  *   Asaf Romano <mano@mozilla.com>
  25.  *
  26.  * Alternatively, the contents of this file may be used under the terms of
  27.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  28.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29.  * in which case the provisions of the GPL or the LGPL are applicable instead
  30.  * of those above. If you wish to allow use of your version of this file only
  31.  * under the terms of either the GPL or the LGPL, and not to allow others to
  32.  * use your version of this file under the terms of the MPL, indicate your
  33.  * decision by deleting the provisions above and replace them with the notice
  34.  * and other provisions required by the GPL or the LGPL. If you do not delete
  35.  * the provisions above, a recipient may use your version of this file under
  36.  * the terms of any one of the MPL, the GPL or the LGPL.
  37.  *
  38.  * ***** END LICENSE BLOCK ***** */
  39.  
  40. let Ci = Components.interfaces;
  41. let Cc = Components.classes;
  42. let Cr = Components.results;
  43.  
  44. const loadInSidebarAnno = "bookmarkProperties/loadInSidebar";
  45. const descriptionAnno = "bookmarkProperties/description";
  46. const CLASS_ID = Components.ID("c0844a84-5a12-4808-80a8-809cb002bb4f");
  47. const CONTRACT_ID = "@mozilla.org/browser/placesTransactionsService;1";
  48.  
  49. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  50.  
  51. __defineGetter__("PlacesUtils", function() {
  52.   delete this.PlacesUtils
  53.   var tmpScope = {};
  54.   Components.utils.import("resource://gre/modules/utils.js", tmpScope);
  55.   return this.PlacesUtils = tmpScope.PlacesUtils;
  56. });
  57.  
  58. // The minimum amount of transactions we should tell our observers to begin
  59. // batching (rather than letting them do incremental drawing).
  60. const MIN_TRANSACTIONS_FOR_BATCH = 5;  
  61.  
  62. function placesTransactionsService() {
  63.   this.mTransactionManager = Cc["@mozilla.org/transactionmanager;1"].
  64.                              createInstance(Ci.nsITransactionManager);
  65. }
  66.  
  67. placesTransactionsService.prototype = {
  68.   classDescription: "Places Transaction Manager",
  69.   classID: CLASS_ID,
  70.   contractID: CONTRACT_ID,
  71.  
  72.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPlacesTransactionsService,
  73.                                          Ci.nsITransactionManager]),
  74.  
  75.   aggregateTransactions: function placesAggrTransactions(name, transactions) {
  76.     return new placesAggregateTransactions(name, transactions);
  77.   },
  78.  
  79.   createFolder: function placesCrtFldr(aName, aContainer, aIndex,
  80.                                        aAnnotations, aChildItemsTransactions) {
  81.      return new placesCreateFolderTransactions(aName, aContainer, aIndex,
  82.                                                aAnnotations, aChildItemsTransactions);
  83.   },
  84.  
  85.   createItem: function placesCrtItem(aURI, aContainer, aIndex, aTitle,
  86.                                      aKeyword, aAnnotations, aChildTransactions) {
  87.     return new placesCreateItemTransactions(aURI, aContainer, aIndex, aTitle,
  88.                                             aKeyword, aAnnotations, aChildTransactions);
  89.   },
  90.  
  91.   createSeparator: function placesCrtSpr(aContainer, aIndex) {
  92.     return new placesCreateSeparatorTransactions(aContainer, aIndex);
  93.   },
  94.  
  95.   createLivemark: function placesCrtLivemark(aFeedURI, aSiteURI, aName,
  96.                                              aContainer, aIndex, aAnnotations) {
  97.     return new placesCreateLivemarkTransactions(aFeedURI, aSiteURI, aName,
  98.                                                 aContainer, aIndex, aAnnotations);
  99.   },
  100.  
  101.   moveItem: function placesMvItem(aItemId, aNewContainer, aNewIndex) {
  102.     return new placesMoveItemTransactions(aItemId, aNewContainer, aNewIndex);
  103.   },
  104.  
  105.   removeItem: function placesRmItem(id) {
  106.     if (id == PlacesUtils.tagsFolderId || id == PlacesUtils.placesRootId ||
  107.         id == PlacesUtils.bookmarksMenuFolderId ||
  108.         id == PlacesUtils.toolbarFolderId)
  109.       throw Cr.NS_ERROR_INVALID_ARG;
  110.  
  111.     // if the item lives within a tag container, use the tagging transactions
  112.     var parent = PlacesUtils.bookmarks.getFolderIdForItem(id);
  113.     var grandparent = PlacesUtils.bookmarks.getFolderIdForItem(parent);
  114.     if (grandparent == PlacesUtils.tagsFolderId) {
  115.       var uri = PlacesUtils.bookmarks.getBookmarkURI(id);
  116.       return this.untagURI(uri, [parent]);
  117.     }
  118.  
  119.     return new placesRemoveItemTransaction(id);
  120.   },
  121.  
  122.   editItemTitle: function placesEditItmTitle(id, newTitle) {
  123.     return new placesEditItemTitleTransactions(id, newTitle);
  124.   },
  125.  
  126.   editBookmarkURI: function placesEditBkmkURI(aBookmarkId, aNewURI) {
  127.     return new placesEditBookmarkURITransactions(aBookmarkId, aNewURI);
  128.   },
  129.  
  130.   setLoadInSidebar: function placesSetLdInSdbar(aBookmarkId, aLoadInSidebar) {
  131.     return new placesSetLoadInSidebarTransactions(aBookmarkId, aLoadInSidebar);
  132.   },
  133.  
  134.   editItemDescription: function placesEditItmDesc(aItemId, aDescription) {
  135.     return new placesEditItemDescriptionTransactions(aItemId, aDescription);
  136.   },
  137.  
  138.   editBookmarkKeyword: function placesEditBkmkKwd(aItemId, newKeyword) {
  139.     return new placesEditBookmarkKeywordTransactions(aItemId, newKeyword);
  140.   },
  141.  
  142.   editBookmarkPostData: function placesEditBookmarkPostdata(aItemId, aPostData) {
  143.     return new placesEditBookmarkPostDataTransactions(aItemId, aPostData);
  144.   },
  145.  
  146.   editLivemarkSiteURI: function placesEditLvmkSiteURI(folderId, uri) {
  147.     return new placesEditLivemarkSiteURITransactions(folderId, uri);
  148.   },
  149.  
  150.   editLivemarkFeedURI: function placesEditLvmkFeedURI(folderId, uri) {
  151.     return new placesEditLivemarkFeedURITransactions(folderId, uri);
  152.   },
  153.  
  154.   editBookmarkMicrosummary: function placesEditBkmkMicrosummary(aID, newMicrosummary) {
  155.     return new placesEditBookmarkMicrosummaryTransactions(aID, newMicrosummary);
  156.   },
  157.  
  158.   editItemDateAdded: function placesEditItemDateAdded(aID, aNewDateAdded) {
  159.     return new placesEditItemDateAddedTransaction(aID, aNewDateAdded);
  160.   },
  161.  
  162.   editItemLastModified: function placesEditItemLastModified(aID, aNewLastModified) {
  163.     return new placesEditItemLastModifiedTransaction(aID, aNewLastModified);
  164.   },
  165.  
  166.   sortFolderByName: function placesSortFldrByName(aFolderId) {
  167.     return new placesSortFolderByNameTransactions(aFolderId);
  168.   },
  169.  
  170.   tagURI: function placesTagURI(aURI, aTags) {
  171.     return new placesTagURITransaction(aURI, aTags);
  172.   },
  173.  
  174.   untagURI: function placesUntagURI(aURI, aTags) {
  175.     return new placesUntagURITransaction(aURI, aTags);
  176.   },
  177.  
  178.   // Update commands in the undo group of the active window
  179.   // commands in inactive windows will are updated on-focus
  180.   _updateCommands: function() {
  181.     var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
  182.              getService(Ci.nsIWindowMediator);
  183.     var win = wm.getMostRecentWindow(null);
  184.     if (win)
  185.       win.updateCommands("undo");
  186.   },
  187.  
  188.   // nsITransactionManager
  189.   doTransaction: function doTransaction(txn) {
  190.     this.mTransactionManager.doTransaction(txn);
  191.     this._updateCommands();
  192.   },
  193.  
  194.   undoTransaction: function undoTransaction() {
  195.     this.mTransactionManager.undoTransaction();
  196.     this._updateCommands();
  197.   },
  198.  
  199.   redoTransaction: function redoTransaction() {
  200.     this.mTransactionManager.redoTransaction();
  201.     this._updateCommands();
  202.   },
  203.  
  204.   clear: function() this.mTransactionManager.clear(),
  205.   beginBatch: function() this.mTransactionManager.beginBatch(),
  206.   endBatch: function() this.mTransactionManager.endBatch(),
  207.  
  208.   get numberOfUndoItems() {
  209.     return this.mTransactionManager.numberOfUndoItems;
  210.   },
  211.  
  212.   get numberOfRedoItems() {
  213.     return this.mTransactionManager.numberOfRedoItems;
  214.   },
  215.  
  216.   get maxTransactionCount() {
  217.     return this.mTransactionManager.maxTransactionCount;
  218.   },
  219.   set maxTransactionCount(val) {
  220.     return this.mTransactionManager.maxTransactionCount = val;
  221.   },
  222.  
  223.   peekUndoStack: function() this.mTransactionManager.peekUndoStack(),
  224.   peekRedoStack: function() this.mTransactionManager.peekRedoStack(),
  225.   getUndoStack: function() this.mTransactionManager.getUndoStack(),
  226.   getRedoStack: function() this.mTransactionManager.getRedoStack(),
  227.   AddListener: function(l) this.mTransactionManager.AddListener(l),
  228.   RemoveListener: function(l) this.mTransactionManager.RemoveListener(l)
  229. };
  230.  
  231. /**
  232.  * Method and utility stubs for Places Edit Transactions
  233.  */
  234. function placesBaseTransaction() {
  235. }
  236.  
  237. placesBaseTransaction.prototype = {
  238.   // for child-transactions
  239.   get wrappedJSObject() {
  240.     return this;
  241.   },
  242.  
  243.   // nsITransaction
  244.   redoTransaction: function PBT_redoTransaction() {
  245.     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  246.   },
  247.  
  248.   get isTransient() {
  249.     return false;
  250.   },
  251.  
  252.   merge: function mergeFunc(transaction) {
  253.     return false;
  254.   },
  255.  
  256.   // nsISupports
  257.   QueryInterface: XPCOMUtils.generateQI([Ci.nsITransaction]),
  258. };
  259.  
  260. function placesAggregateTransactions(name, transactions) {
  261.   this._transactions = transactions;
  262.   this._name = name;
  263.   this.container = -1;
  264.   this.redoTransaction = this.doTransaction;
  265. }
  266.  
  267. placesAggregateTransactions.prototype = {
  268.   __proto__: placesBaseTransaction.prototype,
  269.  
  270.   doTransaction: function PAT_doTransaction() {
  271.     if (this._transactions.length >= MIN_TRANSACTIONS_FOR_BATCH) {
  272.       var callback = {
  273.         _self: this,
  274.         runBatched: function() {
  275.           this._self.commit(false);
  276.         }
  277.       };
  278.       PlacesUtils.bookmarks.runInBatchMode(callback, null);
  279.     }
  280.     else
  281.       this.commit(false);
  282.   },
  283.  
  284.   undoTransaction: function PAT_undoTransaction() {
  285.     if (this._transactions.length >= MIN_TRANSACTIONS_FOR_BATCH) {
  286.       var callback = {
  287.         _self: this,
  288.         runBatched: function() {
  289.           this._self.commit(true);
  290.         }
  291.       };
  292.       PlacesUtils.bookmarks.runInBatchMode(callback, null);
  293.     }
  294.     else
  295.       this.commit(true);
  296.   },
  297.  
  298.   commit: function PAT_commit(aUndo) {
  299.     for (var i=0; i < this._transactions.length; ++i) {
  300.       var txn = this._transactions[i];
  301.       if (this.container > -1) 
  302.         txn.wrappedJSObject.container = this.container;
  303.       if (aUndo)
  304.         txn.undoTransaction();
  305.       else
  306.         txn.doTransaction();
  307.     }
  308.   }
  309. };
  310.  
  311. function placesCreateFolderTransactions(aName, aContainer, aIndex,
  312.                                         aAnnotations,
  313.                                         aChildItemsTransactions) {
  314.   this._name = aName;
  315.   this._container = aContainer;
  316.   this._index = typeof(aIndex) == "number" ? aIndex : -1;
  317.   this._annotations = aAnnotations;
  318.   this._id = null;
  319.   this._childItemsTransactions = aChildItemsTransactions || [];
  320.   this.redoTransaction = this.doTransaction;
  321. }
  322.  
  323. placesCreateFolderTransactions.prototype = {
  324.   __proto__: placesBaseTransaction.prototype,
  325.  
  326.   // childItemsTransaction support
  327.   get container() { return this._container; },
  328.   set container(val) { return this._container = val; },
  329.  
  330.   doTransaction: function PCFT_doTransaction() {
  331.     this._id = PlacesUtils.bookmarks.createFolder(this._container, 
  332.                                                   this._name, this._index);
  333.     if (this._annotations && this._annotations.length > 0)
  334.       PlacesUtils.setAnnotationsForItem(this._id, this._annotations);
  335.  
  336.     for (var i = 0; i < this._childItemsTransactions.length; ++i) {
  337.       var txn = this._childItemsTransactions[i];
  338.       txn.wrappedJSObject.container = this._id;
  339.       txn.doTransaction();
  340.     }
  341.   },
  342.  
  343.   undoTransaction: function PCFT_undoTransaction() {
  344.     for (var i = 0; i < this._childItemsTransactions.length; ++i) {
  345.       var txn = this._childItemsTransactions[i];
  346.       txn.undoTransaction();
  347.     }
  348.     PlacesUtils.bookmarks.removeFolder(this._id);
  349.   }
  350. };
  351.  
  352. function placesCreateItemTransactions(aURI, aContainer, aIndex, aTitle,
  353.                                       aKeyword, aAnnotations,
  354.                                       aChildTransactions) {
  355.   this._uri = aURI;
  356.   this._container = aContainer;
  357.   this._index = typeof(aIndex) == "number" ? aIndex : -1;
  358.   this._title = aTitle;
  359.   this._keyword = aKeyword;
  360.   this._annotations = aAnnotations;
  361.   this._childTransactions = aChildTransactions || [];
  362.   this.redoTransaction = this.doTransaction;
  363. }
  364.  
  365. placesCreateItemTransactions.prototype = {
  366.   __proto__: placesBaseTransaction.prototype,
  367.  
  368.   // childItemsTransactions support for the create-folder transaction
  369.   get container() { return this._container; },
  370.   set container(val) { return this._container = val; },
  371.  
  372.   doTransaction: function PCIT_doTransaction() {
  373.     this._id = PlacesUtils.bookmarks.insertBookmark(this.container, this._uri,
  374.                                                     this._index, this._title);
  375.     if (this._keyword)
  376.       PlacesUtils.bookmarks.setKeywordForBookmark(this._id, this._keyword);
  377.     if (this._annotations && this._annotations.length > 0)
  378.       PlacesUtils.setAnnotationsForItem(this._id, this._annotations);
  379.  
  380.     for (var i = 0; i < this._childTransactions.length; ++i) {
  381.       var txn = this._childTransactions[i];
  382.       txn.wrappedJSObject.id = this._id;
  383.       txn.doTransaction();
  384.     }
  385.   },
  386.  
  387.   undoTransaction: function PCIT_undoTransaction() {
  388.     PlacesUtils.bookmarks.removeItem(this._id);
  389.     for (var i = 0; i < this._childTransactions.length; ++i) {
  390.       var txn = this._childTransactions[i];
  391.       txn.undoTransaction();
  392.     }
  393.   }
  394. };
  395.  
  396. function placesCreateSeparatorTransactions(aContainer, aIndex) {
  397.   this._container = aContainer;
  398.   this._index = typeof(aIndex) == "number" ? aIndex : -1;
  399.   this._id = null;
  400. }
  401.  
  402. placesCreateSeparatorTransactions.prototype = {
  403.   __proto__: placesBaseTransaction.prototype,
  404.  
  405.   // childItemsTransaction support
  406.   get container() { return this._container; },
  407.   set container(val) { return this._container = val; },
  408.  
  409.   doTransaction: function PCST_doTransaction() {
  410.     this._id = PlacesUtils.bookmarks
  411.                           .insertSeparator(this.container, this._index);
  412.   },
  413.  
  414.   undoTransaction: function PCST_undoTransaction() {
  415.     PlacesUtils.bookmarks.removeItem(this._id);
  416.   }
  417. };
  418.  
  419. function placesCreateLivemarkTransactions(aFeedURI, aSiteURI, aName,
  420.                                           aContainer, aIndex,
  421.                                           aAnnotations) {
  422.   this._feedURI = aFeedURI;
  423.   this._siteURI = aSiteURI;
  424.   this._name = aName;
  425.   this._container = aContainer;
  426.   this._index = typeof(aIndex) == "number" ? aIndex : -1;
  427.   this._annotations = aAnnotations;
  428. }
  429.  
  430. placesCreateLivemarkTransactions.prototype = {
  431.   __proto__: placesBaseTransaction.prototype,
  432.  
  433.   // childItemsTransaction support
  434.   get container() { return this._container; },
  435.   set container(val) { return this._container = val; },
  436.  
  437.   doTransaction: function PCLT_doTransaction() {
  438.     this._id = PlacesUtils.livemarks.createLivemark(this._container, this._name,
  439.                                                     this._siteURI, this._feedURI,
  440.                                                     this._index);
  441.     if (this._annotations && this._annotations.length > 0)
  442.       PlacesUtils.setAnnotationsForItem(this._id, this._annotations);
  443.   },
  444.  
  445.   undoTransaction: function PCLT_undoTransaction() {
  446.     PlacesUtils.bookmarks.removeFolder(this._id);
  447.   }
  448. };
  449.  
  450. function placesMoveItemTransactions(aItemId, aNewContainer, aNewIndex) {
  451.   this._id = aItemId;
  452.   this._oldContainer = PlacesUtils.bookmarks.getFolderIdForItem(this._id);
  453.   this._oldIndex = PlacesUtils.bookmarks.getItemIndex(this._id);
  454.   this._newContainer = aNewContainer;
  455.   this._newIndex = aNewIndex;
  456.   this.redoTransaction = this.doTransaction;
  457. }
  458.  
  459. placesMoveItemTransactions.prototype = {
  460.   __proto__: placesBaseTransaction.prototype,
  461.  
  462.   doTransaction: function PMIT_doTransaction() {
  463.     PlacesUtils.bookmarks.moveItem(this._id, this._newContainer, this._newIndex);
  464.     // if newIndex == DEFAULT_INDEX we append, so get correct index for undo
  465.     if (this._newIndex == PlacesUtils.bookmarks.DEFAULT_INDEX)
  466.       this._newIndex = PlacesUtils.bookmarks.getItemIndex(this._id);
  467.   },
  468.  
  469.   undoTransaction: function PMIT_undoTransaction() {
  470.     // moving down in the same container takes in count removal of the item
  471.     // so to revert positions we must move to oldIndex + 1
  472.     if (this._newContainer == this._oldContainer &&
  473.         this._oldIndex > this._newIndex)
  474.       PlacesUtils.bookmarks.moveItem(this._id, this._oldContainer, this._oldIndex + 1);
  475.     else
  476.       PlacesUtils.bookmarks.moveItem(this._id, this._oldContainer, this._oldIndex);
  477.   }
  478. };
  479.  
  480. function placesRemoveItemTransaction(aItemId) {
  481.   this.redoTransaction = this.doTransaction;
  482.   this._id = aItemId;
  483.   this._itemType = PlacesUtils.bookmarks.getItemType(this._id);
  484.   if (this._itemType == Ci.nsINavBookmarksService.TYPE_FOLDER) {
  485.     this._transactions = [];
  486.     this._removeTxn = PlacesUtils.bookmarks
  487.                                  .getRemoveFolderTransaction(this._id);
  488.   }
  489. }
  490.  
  491. placesRemoveItemTransaction.prototype = {
  492.   __proto__: placesBaseTransaction.prototype,
  493.  
  494.   doTransaction: function PRIT_doTransaction() {
  495.     this._oldContainer = PlacesUtils.bookmarks.getFolderIdForItem(this._id);
  496.     this._oldIndex = PlacesUtils.bookmarks.getItemIndex(this._id);
  497.     this._title = PlacesUtils.bookmarks.getItemTitle(this._id);
  498.     this._annotations = PlacesUtils.getAnnotationsForItem(this._id);
  499.     this._dateAdded = PlacesUtils.bookmarks.getItemDateAdded(this._id);
  500.     this._lastModified = PlacesUtils.bookmarks.getItemLastModified(this._id);
  501.  
  502.     if (this._itemType == Ci.nsINavBookmarksService.TYPE_FOLDER) {
  503.       this._saveFolderContents();
  504.  
  505.       // Remove children backwards to preserve parent-child relationships.
  506.       for (var i = this._transactions.length - 1; i >= 0; --i)
  507.         this._transactions[i].doTransaction();
  508.     
  509.       // Remove this folder itself. 
  510.       this._removeTxn.doTransaction();
  511.     }
  512.     else {
  513.       if (this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK)
  514.         this._uri = PlacesUtils.bookmarks.getBookmarkURI(this._id);
  515.       PlacesUtils.bookmarks.removeItem(this._id);
  516.       if (this._uri) {
  517.         // if this was the last bookmark (excluding tag-items and livemark
  518.         // children, see getMostRecentBookmarkForURI) for the bookmark's url,
  519.         // remove the url from tag containers as well.
  520.         if (PlacesUtils.getMostRecentBookmarkForURI(this._uri) == -1) {
  521.           this._tags = PlacesUtils.tagging.getTagsForURI(this._uri, {});
  522.           PlacesUtils.tagging.untagURI(this._uri, this._tags);
  523.         }
  524.       }
  525.     }
  526.   },
  527.  
  528.   undoTransaction: function PRIT_undoTransaction() {
  529.     if (this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK) {
  530.       this._id = PlacesUtils.bookmarks.insertBookmark(this._oldContainer,
  531.                                                       this._uri,
  532.                                                       this._oldIndex,
  533.                                                       this._title);
  534.       if (this._tags && this._tags.length > 0)
  535.         PlacesUtils.tagging.tagURI(this._uri, this._tags);
  536.     }
  537.     else if (this._itemType == Ci.nsINavBookmarksService.TYPE_FOLDER) {
  538.       this._removeTxn.undoTransaction();
  539.       // Create children forwards to preserve parent-child relationships.
  540.       for (var i = 0; i < this._transactions.length; ++i)
  541.         this._transactions[i].undoTransaction();
  542.     }
  543.     else // TYPE_SEPARATOR
  544.       PlacesUtils.bookmarks.insertSeparator(this._oldContainer, this._oldIndex);
  545.  
  546.     if (this._annotations.length > 0)
  547.       PlacesUtils.setAnnotationsForItem(this._id, this._annotations);
  548.  
  549.     PlacesUtils.bookmarks.setItemDateAdded(this._id, this._dateAdded);
  550.     PlacesUtils.bookmarks.setItemLastModified(this._id, this._lastModified);
  551.   },
  552.  
  553.   /**
  554.   * Create a flat, ordered list of transactions for a depth-first recreation
  555.   * of items within this folder.
  556.   */
  557.   _saveFolderContents: function PRIT__saveFolderContents() {
  558.     this._transactions = [];
  559.     var contents =
  560.       PlacesUtils.getFolderContents(this._id, false, false).root;
  561.     for (var i = 0; i < contents.childCount; ++i) {
  562.       this._transactions
  563.           .push(new placesRemoveItemTransaction(contents.getChild(i).itemId));
  564.     }
  565.   }
  566. };
  567.  
  568. function placesEditItemTitleTransactions(id, newTitle) {
  569.   this._id = id;
  570.   this._newTitle = newTitle;
  571.   this._oldTitle = "";
  572.   this.redoTransaction = this.doTransaction;
  573. }
  574.  
  575. placesEditItemTitleTransactions.prototype = {
  576.   __proto__: placesBaseTransaction.prototype,
  577.  
  578.   doTransaction: function PEITT_doTransaction() {
  579.     this._oldTitle = PlacesUtils.bookmarks.getItemTitle(this._id);
  580.     PlacesUtils.bookmarks.setItemTitle(this._id, this._newTitle);
  581.   },
  582.  
  583.   undoTransaction: function PEITT_undoTransaction() {
  584.     PlacesUtils.bookmarks.setItemTitle(this._id, this._oldTitle);
  585.   }
  586. };
  587.  
  588. function placesEditBookmarkURITransactions(aBookmarkId, aNewURI) {
  589.   this._id = aBookmarkId;
  590.   this._newURI = aNewURI;
  591.   this.redoTransaction = this.doTransaction;
  592. }
  593.  
  594. placesEditBookmarkURITransactions.prototype = {
  595.   __proto__: placesBaseTransaction.prototype,
  596.  
  597.   doTransaction: function PEBUT_doTransaction() {
  598.     this._oldURI = PlacesUtils.bookmarks.getBookmarkURI(this._id);
  599.     PlacesUtils.bookmarks.changeBookmarkURI(this._id, this._newURI);
  600.     // move tags from old URI to new URI
  601.     this._tags = PlacesUtils.tagging.getTagsForURI(this._oldURI, {});
  602.     if (this._tags.length != 0) {
  603.       // only untag the old URI if this is the only bookmark
  604.       if (PlacesUtils.getBookmarksForURI(this._oldURI, {}).length == 0)
  605.         PlacesUtils.tagging.untagURI(this._oldURI, this._tags);
  606.       PlacesUtils.tagging.tagURI(this._newURI, this._tags);
  607.     }
  608.   },
  609.  
  610.   undoTransaction: function PEBUT_undoTransaction() {
  611.     PlacesUtils.bookmarks.changeBookmarkURI(this._id, this._oldURI);
  612.     // move tags from new URI to old URI 
  613.     if (this._tags.length != 0) {
  614.       // only untag the new URI if this is the only bookmark
  615.       if (PlacesUtils.getBookmarksForURI(this._newURI, {}).length == 0)
  616.         PlacesUtils.tagging.untagURI(this._newURI, this._tags);
  617.       PlacesUtils.tagging.tagURI(this._oldURI, this._tags);
  618.     }
  619.   }
  620. };
  621.  
  622. function placesSetLoadInSidebarTransactions(aBookmarkId, aLoadInSidebar) {
  623.   this.id = aBookmarkId;
  624.   this._loadInSidebar = aLoadInSidebar;
  625.   this.redoTransaction = this.doTransaction;
  626. }
  627.  
  628. placesSetLoadInSidebarTransactions.prototype = {
  629.   __proto__: placesBaseTransaction.prototype,
  630.  
  631.   _anno: {
  632.     name: loadInSidebarAnno,
  633.     type: Ci.nsIAnnotationService.TYPE_INT32,
  634.     value: 1,
  635.     flags: 0,
  636.     expires: Ci.nsIAnnotationService.EXPIRE_NEVER
  637.   },
  638.  
  639.   doTransaction: function PSLIST_doTransaction() {
  640.     this._wasSet = PlacesUtils.annotations.itemHasAnnotation(this.id, this._anno.name);
  641.     if (this._loadInSidebar) {
  642.       PlacesUtils.setAnnotationsForItem(this.id, [this._anno]);
  643.     }
  644.     else {
  645.       try {
  646.         PlacesUtils.annotations.removeItemAnnotation(this.id, this._anno.name);
  647.       } catch(ex) { }
  648.     }
  649.   },
  650.  
  651.   undoTransaction: function PSLIST_undoTransaction() {
  652.     if (this._wasSet != this._loadInSidebar) {
  653.       this._loadInSidebar = !this._loadInSidebar;
  654.       this.doTransaction();
  655.     }
  656.   }
  657. };
  658.  
  659. function placesEditItemDescriptionTransactions(aItemId, aDescription) {
  660.   this.id = aItemId;
  661.   this._newDescription = aDescription;
  662.   this.redoTransaction = this.doTransaction;
  663. }
  664.  
  665. placesEditItemDescriptionTransactions.prototype = {
  666.   __proto__: placesBaseTransaction.prototype,
  667.  
  668.   _oldDescription: "",
  669.  
  670.   doTransaction: function PSLIST_doTransaction() {
  671.     const annos = PlacesUtils.annotations;
  672.     if (annos.itemHasAnnotation(this.id, descriptionAnno))
  673.       this._oldDescription = annos.getItemAnnotation(this.id, descriptionAnno);
  674.  
  675.     if (this._newDescription) {
  676.       annos.setItemAnnotation(this.id, descriptionAnno,
  677.                               this._newDescription, 0,
  678.                               annos.EXPIRE_NEVER);
  679.     }
  680.     else if (this._oldDescription)
  681.       annos.removeItemAnnotation(this.id, descriptionAnno);
  682.   },
  683.  
  684.   undoTransaction: function PSLIST_undoTransaction() {
  685.     const annos = PlacesUtils.annotations;
  686.     if (this._oldDescription) {
  687.       annos.setItemAnnotationString(this.id, descriptionAnno,
  688.                                     this._oldDescription, 0,
  689.                                     annos.EXPIRE_NEVER);
  690.     }
  691.     else if (annos.itemHasAnnotation(this.id, descriptionAnno))
  692.       annos.removeItemAnnotation(this.id, descriptionAnno);
  693.   }
  694. };
  695.  
  696. function placesEditBookmarkKeywordTransactions(id, newKeyword) {
  697.   this.id = id;
  698.   this._newKeyword = newKeyword;
  699.   this._oldKeyword = "";
  700.   this.redoTransaction = this.doTransaction;
  701. }
  702.  
  703. placesEditBookmarkKeywordTransactions.prototype = {
  704.   __proto__: placesBaseTransaction.prototype,
  705.  
  706.   doTransaction: function PEBKT_doTransaction() {
  707.     this._oldKeyword = PlacesUtils.bookmarks.getKeywordForBookmark(this.id);
  708.     PlacesUtils.bookmarks.setKeywordForBookmark(this.id, this._newKeyword);
  709.   },
  710.  
  711.   undoTransaction: function PEBKT_undoTransaction() {
  712.     PlacesUtils.bookmarks.setKeywordForBookmark(this.id, this._oldKeyword);
  713.   }
  714. };
  715.  
  716. function placesEditBookmarkPostDataTransactions(aItemId, aPostData) {
  717.   this.id = aItemId;
  718.   this._newPostData = aPostData;
  719.   this._oldPostData = null;
  720.   this.redoTransaction = this.doTransaction;
  721. }
  722.  
  723. placesEditBookmarkPostDataTransactions.prototype = {
  724.   __proto__: placesBaseTransaction.prototype,
  725.  
  726.   doTransaction: function PEUPDT_doTransaction() {
  727.     this._oldPostData = PlacesUtils.getPostDataForBookmark(this._id);
  728.     PlacesUtils.setPostDataForBookmark(this.id, this._newPostData);
  729.   },
  730.  
  731.   undoTransaction: function PEUPDT_undoTransaction() {
  732.     PlacesUtils.setPostDataForBookmark(this.id, this._oldPostData);
  733.   }
  734. };
  735.  
  736. function placesEditLivemarkSiteURITransactions(folderId, uri) {
  737.   this._folderId = folderId;
  738.   this._newURI = uri;
  739.   this._oldURI = null;
  740.   this.redoTransaction = this.doTransaction;
  741. }
  742.  
  743. placesEditLivemarkSiteURITransactions.prototype = {
  744.   __proto__: placesBaseTransaction.prototype,
  745.  
  746.   doTransaction: function PELSUT_doTransaction() {
  747.     this._oldURI = PlacesUtils.livemarks.getSiteURI(this._folderId);
  748.     PlacesUtils.livemarks.setSiteURI(this._folderId, this._newURI);
  749.   },
  750.  
  751.   undoTransaction: function PELSUT_undoTransaction() {
  752.     PlacesUtils.livemarks.setSiteURI(this._folderId, this._oldURI);
  753.   }
  754. };
  755.  
  756. function placesEditLivemarkFeedURITransactions(folderId, uri) {
  757.   this._folderId = folderId;
  758.   this._newURI = uri;
  759.   this._oldURI = null;
  760.   this.redoTransaction = this.doTransaction;
  761. }
  762.  
  763. placesEditLivemarkFeedURITransactions.prototype = {
  764.   __proto__: placesBaseTransaction.prototype,
  765.  
  766.   doTransaction: function PELFUT_doTransaction() {
  767.     this._oldURI = PlacesUtils.livemarks.getFeedURI(this._folderId);
  768.     PlacesUtils.livemarks.setFeedURI(this._folderId, this._newURI);
  769.     PlacesUtils.livemarks.reloadLivemarkFolder(this._folderId);
  770.   },
  771.  
  772.   undoTransaction: function PELFUT_undoTransaction() {
  773.     PlacesUtils.livemarks.setFeedURI(this._folderId, this._oldURI);
  774.     PlacesUtils.livemarks.reloadLivemarkFolder(this._folderId);
  775.   }
  776. };
  777.  
  778. function placesEditBookmarkMicrosummaryTransactions(aID, newMicrosummary) {
  779.   this.id = aID;
  780.   this._mss = Cc["@mozilla.org/microsummary/service;1"].
  781.               getService(Ci.nsIMicrosummaryService);
  782.   this._newMicrosummary = newMicrosummary;
  783.   this._oldMicrosummary = null;
  784.   this.redoTransaction = this.doTransaction;
  785. }
  786.  
  787. placesEditBookmarkMicrosummaryTransactions.prototype = {
  788.   __proto__: placesBaseTransaction.prototype,
  789.  
  790.   doTransaction: function PEBMT_doTransaction() {
  791.     this._oldMicrosummary = this._mss.getMicrosummary(this.id);
  792.     if (this._newMicrosummary)
  793.       this._mss.setMicrosummary(this.id, this._newMicrosummary);
  794.     else
  795.       this._mss.removeMicrosummary(this.id);
  796.   },
  797.  
  798.   undoTransaction: function PEBMT_undoTransaction() {
  799.     if (this._oldMicrosummary)
  800.       this._mss.setMicrosummary(this.id, this._oldMicrosummary);
  801.     else
  802.       this._mss.removeMicrosummary(this.id);
  803.   }
  804. };
  805.  
  806. function placesEditItemDateAddedTransaction(id, newDateAdded) {
  807.   this.id = id;
  808.   this._newDateAdded = newDateAdded;
  809.   this._oldDateAdded = null;
  810.   this.redoTransaction = this.doTransaction;
  811. }
  812.  
  813. placesEditItemDateAddedTransaction.prototype = {
  814.   __proto__: placesBaseTransaction.prototype,
  815.  
  816.   // to support folders as well
  817.   get container() { return this.id; },
  818.   set container(val) { return this.id = val; },
  819.  
  820.   doTransaction: function PEIDA_doTransaction() {
  821.     this._oldDateAdded = PlacesUtils.bookmarks.getItemDateAdded(this.id);
  822.     PlacesUtils.bookmarks.setItemDateAdded(this.id, this._newDateAdded);
  823.   },
  824.  
  825.   undoTransaction: function PEIDA_undoTransaction() {
  826.     PlacesUtils.bookmarks.setItemDateAdded(this.id, this._oldDateAdded);
  827.   }
  828. };
  829.  
  830. function placesEditItemLastModifiedTransaction(id, newLastModified) {
  831.   this.id = id;
  832.   this._newLastModified = newLastModified;
  833.   this._oldLastModified = null;
  834.   this.redoTransaction = this.doTransaction;
  835. }
  836.  
  837. placesEditItemLastModifiedTransaction.prototype = {
  838.   __proto__: placesBaseTransaction.prototype,
  839.  
  840.   // to support folders as well
  841.   get container() { return this.id; },
  842.   set container(val) { return this.id = val; },
  843.  
  844.   doTransaction: function PEILM_doTransaction() {
  845.     this._oldLastModified = PlacesUtils.bookmarks.getItemLastModified(this.id);
  846.     PlacesUtils.bookmarks.setItemLastModified(this.id, this._newLastModified);
  847.   },
  848.  
  849.   undoTransaction: function PEILM_undoTransaction() {
  850.     PlacesUtils.bookmarks.setItemLastModified(this.id, this._oldLastModified);
  851.   }
  852. };
  853.  
  854. function placesSortFolderByNameTransactions(aFolderId) {
  855.   this._folderId = aFolderId;
  856.   this._oldOrder = null,
  857.   this.redoTransaction = this.doTransaction;
  858. }
  859.  
  860. placesSortFolderByNameTransactions.prototype = {
  861.   __proto__: placesBaseTransaction.prototype,
  862.  
  863.   doTransaction: function PSSFBN_doTransaction() {
  864.     this._oldOrder = [];
  865.  
  866.     var contents = PlacesUtils.getFolderContents(this._folderId, false, false).root;
  867.     var count = contents.childCount;
  868.  
  869.     // sort between separators
  870.     var newOrder = []; 
  871.     var preSep = []; // temporary array for sorting each group of items
  872.     var sortingMethod =
  873.       function (a, b) {
  874.         if (PlacesUtils.nodeIsContainer(a) && !PlacesUtils.nodeIsContainer(b))
  875.           return -1;
  876.         if (!PlacesUtils.nodeIsContainer(a) && PlacesUtils.nodeIsContainer(b))
  877.           return 1;
  878.         return a.title.localeCompare(b.title);
  879.       };
  880.  
  881.     for (var i = 0; i < count; ++i) {
  882.       var item = contents.getChild(i);
  883.       this._oldOrder[item.itemId] = i;
  884.       if (PlacesUtils.nodeIsSeparator(item)) {
  885.         if (preSep.length > 0) {
  886.           preSep.sort(sortingMethod);
  887.           newOrder = newOrder.concat(preSep);
  888.           preSep.splice(0);
  889.         }
  890.         newOrder.push(item);
  891.       }
  892.       else
  893.         preSep.push(item);
  894.     }
  895.     if (preSep.length > 0) {
  896.       preSep.sort(sortingMethod);
  897.       newOrder = newOrder.concat(preSep);
  898.     }
  899.  
  900.     // set the nex indexs
  901.     for (var i = 0; i < count; ++i)
  902.       PlacesUtils.bookmarks.setItemIndex(newOrder[i].itemId, i);
  903.   },
  904.  
  905.   undoTransaction: function PSSFBN_undoTransaction() {
  906.     for (item in this._oldOrder)
  907.       PlacesUtils.bookmarks.setItemIndex(item, this._oldOrder[item]);
  908.   }
  909. };
  910.  
  911. function placesTagURITransaction(aURI, aTags) {
  912.   this._uri = aURI;
  913.   this._tags = aTags;
  914.   this._unfiledItemId = -1;
  915.   this.redoTransaction = this.doTransaction;
  916. }
  917.  
  918. placesTagURITransaction.prototype = {
  919.   __proto__: placesBaseTransaction.prototype,
  920.  
  921.   doTransaction: function PTU_doTransaction() {
  922.     if (PlacesUtils.getMostRecentBookmarkForURI(this._uri) == -1) {
  923.       // Force an unfiled bookmark first
  924.       this._unfiledItemId =
  925.         PlacesUtils.bookmarks
  926.                    .insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
  927.                                    this._uri,
  928.                                    PlacesUtils.bookmarks.DEFAULT_INDEX,
  929.                                    PlacesUtils.history.getPageTitle(this._uri));
  930.     }
  931.     PlacesUtils.tagging.tagURI(this._uri, this._tags);
  932.   },
  933.  
  934.   undoTransaction: function PTU_undoTransaction() {
  935.     if (this._unfiledItemId != -1) {
  936.       PlacesUtils.bookmarks.removeItem(this._unfiledItemId);
  937.       this._unfiledItemId = -1;
  938.     }
  939.     PlacesUtils.tagging.untagURI(this._uri, this._tags);
  940.   }
  941. };
  942.  
  943. function placesUntagURITransaction(aURI, aTags) {
  944.   this._uri = aURI;
  945.   if (aTags) {    
  946.     // Within this transaction, we cannot rely on tags given by itemId
  947.     // since the tag containers may be gone after we call untagURI.
  948.     // Thus, we convert each tag given by its itemId to name.
  949.     this._tags = aTags;
  950.     for (var i=0; i < aTags.length; i++) {
  951.       if (typeof(this._tags[i]) == "number")
  952.         this._tags[i] = PlacesUtils.bookmarks.getItemTitle(this._tags[i]);
  953.     }
  954.   }
  955.   else
  956.     this._tags = PlacesUtils.tagging.getTagsForURI(this._uri, {});
  957.  
  958.   this.redoTransaction = this.doTransaction;
  959. }
  960.  
  961. placesUntagURITransaction.prototype = {
  962.   __proto__: placesBaseTransaction.prototype,
  963.  
  964.   doTransaction: function PUTU_doTransaction() {
  965.     PlacesUtils.tagging.untagURI(this._uri, this._tags);
  966.   },
  967.  
  968.   undoTransaction: function PUTU_undoTransaction() {
  969.     PlacesUtils.tagging.tagURI(this._uri, this._tags);
  970.   }
  971. };
  972.  
  973.  
  974. function NSGetModule(aCompMgr, aFileSpec) {
  975.   return XPCOMUtils.generateModule([placesTransactionsService]);
  976. }
  977.