nsPlacesTransactionsService.js
Go to the documentation of this file.
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  * Marco Bonarco <mak77@bonardo.net>
26  *
27  * Alternatively, the contents of this file may be used under the terms of
28  * either the GNU General Public License Version 2 or later (the "GPL"), or
29  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30  * in which case the provisions of the GPL or the LGPL are applicable instead
31  * of those above. If you wish to allow use of your version of this file only
32  * under the terms of either the GPL or the LGPL, and not to allow others to
33  * use your version of this file under the terms of the MPL, indicate your
34  * decision by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL or the LGPL. If you do not delete
36  * the provisions above, a recipient may use your version of this file under
37  * the terms of any one of the MPL, the GPL or the LGPL.
38  *
39  * ***** END LICENSE BLOCK ***** */
40 
41 let Ci = Components.interfaces;
42 let Cc = Components.classes;
43 let Cr = Components.results;
44 
45 const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
46 const DESCRIPTION_ANNO = "bookmarkProperties/description";
47 const GUID_ANNO = "placesInternal/GUID";
48 
49 const CLASS_ID = Components.ID("c0844a84-5a12-4808-80a8-809cb002bb4f");
50 const CONTRACT_ID = "@mozilla.org/browser/placesTransactionsService;1";
51 
52 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
53 
54 __defineGetter__("PlacesUtils", function() {
55  delete this.PlacesUtils
56  var tmpScope = {};
57  Components.utils.import("resource://gre/modules/utils.js", tmpScope);
58  return this.PlacesUtils = tmpScope.PlacesUtils;
59 });
60 
61 // The minimum amount of transactions we should tell our observers to begin
62 // batching (rather than letting them do incremental drawing).
64 
66  this.mTransactionManager = Cc["@mozilla.org/transactionmanager;1"].
67  createInstance(Ci.nsITransactionManager);
68 }
69 
71  classDescription: "Places Transaction Manager",
74 
75  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPlacesTransactionsService,
76  Ci.nsITransactionManager]),
77 
78  aggregateTransactions:
79  function placesTxn_aggregateTransactions(aName, aTransactions) {
80  return new placesAggregateTransactions(aName, aTransactions);
81  },
82 
83  createFolder:
84  function placesTxn_createFolder(aName, aContainer, aIndex,
85  aAnnotations, aChildItemsTransactions) {
86  return new placesCreateFolderTransactions(aName, aContainer, aIndex,
87  aAnnotations, aChildItemsTransactions);
88  },
89 
90  createItem:
91  function placesTxn_createItem(aURI, aContainer, aIndex, aTitle,
92  aKeyword, aAnnotations, aChildTransactions) {
93  return new placesCreateItemTransactions(aURI, aContainer, aIndex, aTitle,
94  aKeyword, aAnnotations, aChildTransactions);
95  },
96 
97  createSeparator:
98  function placesTxn_createSeparator(aContainer, aIndex) {
99  return new placesCreateSeparatorTransactions(aContainer, aIndex);
100  },
101 
102  createLivemark:
103  function placesTxn_createLivemark(aFeedURI, aSiteURI, aName,
104  aContainer, aIndex, aAnnotations) {
105  return new placesCreateLivemarkTransactions(aFeedURI, aSiteURI, aName,
106  aContainer, aIndex, aAnnotations);
107  },
108 
109  moveItem:
110  function placesTxn_moveItem(aItemId, aNewContainer, aNewIndex) {
111  return new placesMoveItemTransactions(aItemId, aNewContainer, aNewIndex);
112  },
113 
114  removeItem:
115  function placesTxn_removeItem(aItemId) {
116  if (aItemId == PlacesUtils.tagsFolderId ||
117  aItemId == PlacesUtils.placesRootId ||
118  aItemId == PlacesUtils.bookmarksMenuFolderId ||
119  aItemId == PlacesUtils.toolbarFolderId)
120  throw Cr.NS_ERROR_INVALID_ARG;
121 
122  // if the item lives within a tag container, use the tagging transactions
123  var parent = PlacesUtils.bookmarks.getFolderIdForItem(aItemId);
124  var grandparent = PlacesUtils.bookmarks.getFolderIdForItem(parent);
125  if (grandparent == PlacesUtils.tagsFolderId) {
126  var uri = PlacesUtils.bookmarks.getBookmarkURI(aItemId);
127  return this.untagURI(uri, [parent]);
128  }
129 
130  // if the item is a livemark container we will not save its children and
131  // will use createLivemark to undo.
132  if (PlacesUtils.itemIsLivemark(aItemId))
133  return new placesRemoveLivemarkTransaction(aItemId);
134 
135  return new placesRemoveItemTransaction(aItemId);
136  },
137 
138  editItemTitle:
139  function placesTxn_editItemTitle(aItemId, aNewTitle) {
140  return new placesEditItemTitleTransactions(aItemId, aNewTitle);
141  },
142 
143  editBookmarkURI:
144  function placesTxn_editBookmarkURI(aItemId, aNewURI) {
145  return new placesEditBookmarkURITransactions(aItemId, aNewURI);
146  },
147 
148  setItemAnnotation:
149  function placesTxn_setItemAnnotation(aItemId, aAnnotationObject) {
150  return new placesSetItemAnnotationTransactions(aItemId, aAnnotationObject);
151  },
152 
153  setPageAnnotation:
154  function placesTxn_setPageAnnotation(aURI, aAnnotationObject) {
155  return new placesSetPageAnnotationTransactions(aURI, aAnnotationObject);
156  },
157 
158  setLoadInSidebar:
159  function placesTxn_setLoadInSidebar(aItemId, aLoadInSidebar) {
160  var annoObj = { name: LOAD_IN_SIDEBAR_ANNO,
161  type: Ci.nsIAnnotationService.TYPE_INT32,
162  flags: 0,
163  value: aLoadInSidebar,
164  expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
165  return this.setItemAnnotation(aItemId, annoObj);
166  },
167 
168  editItemDescription:
169  function placesTxn_editItemDescription(aItemId, aDescription) {
170  var annoObj = { name: DESCRIPTION_ANNO,
171  type: Ci.nsIAnnotationService.TYPE_STRING,
172  flags: 0,
173  value: aDescription,
174  expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
175  return this.setItemAnnotation(aItemId, annoObj);
176  },
177 
178  editBookmarkKeyword:
179  function placesTxn_editBookmarkKeyword(aItemId, aNewKeyword) {
180  return new placesEditBookmarkKeywordTransactions(aItemId, aNewKeyword);
181  },
182 
183  editBookmarkPostData:
184  function placesTxn_editBookmarkPostdata(aItemId, aPostData) {
185  return new placesEditBookmarkPostDataTransactions(aItemId, aPostData);
186  },
187 
188  editLivemarkSiteURI:
189  function placesTxn_editLivemarkSiteURI(aLivemarkId, aSiteURI) {
190  return new placesEditLivemarkSiteURITransactions(aLivemarkId, aSiteURI);
191  },
192 
193  editLivemarkFeedURI:
194  function placesTxn_editLivemarkFeedURI(aLivemarkId, aFeedURI) {
195  return new placesEditLivemarkFeedURITransactions(aLivemarkId, aFeedURI);
196  },
197 
198  editBookmarkMicrosummary:
199  function placesTxn_editBookmarkMicrosummary(aItemId, aNewMicrosummary) {
200  return new placesEditBookmarkMicrosummaryTransactions(aItemId, aNewMicrosummary);
201  },
202 
203  editItemDateAdded:
204  function placesTxn_editItemDateAdded(aItemId, aNewDateAdded) {
205  return new placesEditItemDateAddedTransaction(aItemId, aNewDateAdded);
206  },
207 
208  editItemLastModified:
209  function placesTxn_editItemLastModified(aItemId, aNewLastModified) {
210  return new placesEditItemLastModifiedTransaction(aItemId, aNewLastModified);
211  },
212 
213  sortFolderByName:
214  function placesTxn_sortFolderByName(aFolderId) {
215  return new placesSortFolderByNameTransactions(aFolderId);
216  },
217 
218  tagURI:
219  function placesTxn_tagURI(aURI, aTags) {
220  return new placesTagURITransaction(aURI, aTags);
221  },
222 
223  untagURI:
224  function placesTxn_untagURI(aURI, aTags) {
225  return new placesUntagURITransaction(aURI, aTags);
226  },
227 
228  // Update commands in the undo group of the active window
229  // commands in inactive windows will are updated on-focus
230  _updateCommands: function placesTxn__updateCommands() {
231  var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
232  getService(Ci.nsIWindowMediator);
233  var win = wm.getMostRecentWindow(null);
234  if (win)
235  win.updateCommands("undo");
236  },
237 
238  // nsITransactionManager
239  beginBatch: function() {
240  this.mTransactionManager.beginBatch();
241 
242  // A no-op transaction is pushed to the stack, in order to make safe and
243  // easy to implement "Undo" an unknown number of transactions (including 0),
244  // "above" beginBatch and endBatch. Otherwise,implementing Undo that way
245  // head to dataloss: for example, if no changes were done in the
246  // edit-item panel, the last transaction on the undo stack would be the
247  // initial createItem transaction, or even worse, the batched editing of
248  // some other item.
249  // DO NOT MOVE this to the window scope, that would leak (bug 490068)!
250  this.doTransaction({ doTransaction: function() { },
251  undoTransaction: function() { },
252  redoTransaction: function() { },
253  isTransient: false,
254  merge: function() { return false; } });
255  },
256 
257  endBatch: function() this.mTransactionManager.endBatch(),
258 
259  doTransaction: function placesTxn_doTransaction(txn) {
260  this.mTransactionManager.doTransaction(txn);
261  this._updateCommands();
262  },
263 
264  undoTransaction: function placesTxn_undoTransaction() {
265  this.mTransactionManager.undoTransaction();
266  this._updateCommands();
267  },
268 
269  redoTransaction: function placesTxn_redoTransaction() {
270  this.mTransactionManager.redoTransaction();
271  this._updateCommands();
272  },
273 
274  clear: function() this.mTransactionManager.clear(),
275 
276  get numberOfUndoItems() {
277  return this.mTransactionManager.numberOfUndoItems;
278  },
279 
280  get numberOfRedoItems() {
281  return this.mTransactionManager.numberOfRedoItems;
282  },
283 
284  get maxTransactionCount() {
285  return this.mTransactionManager.maxTransactionCount;
286  },
287  set maxTransactionCount(val) {
288  return this.mTransactionManager.maxTransactionCount = val;
289  },
290 
291  peekUndoStack: function() this.mTransactionManager.peekUndoStack(),
292  peekRedoStack: function() this.mTransactionManager.peekRedoStack(),
293  getUndoStack: function() this.mTransactionManager.getUndoStack(),
294  getRedoStack: function() this.mTransactionManager.getRedoStack(),
295  AddListener: function(l) this.mTransactionManager.AddListener(l),
296  RemoveListener: function(l) this.mTransactionManager.RemoveListener(l)
297 };
298 
303 }
304 
305 placesBaseTransaction.prototype = {
306  // for child-transactions
307  get wrappedJSObject() {
308  return this;
309  },
310 
311  // nsITransaction
312  redoTransaction: function PBT_redoTransaction() {
313  throw Cr.NS_ERROR_NOT_IMPLEMENTED;
314  },
315 
316  get isTransient() {
317  return false;
318  },
319 
320  merge: function mergeFunc(transaction) {
321  return false;
322  },
323 
324  // nsISupports
325  QueryInterface: XPCOMUtils.generateQI([Ci.nsITransaction]),
326 };
327 
328 function placesAggregateTransactions(name, transactions) {
329  this._transactions = transactions;
330  this._name = name;
331  this.container = -1;
332  this.redoTransaction = this.doTransaction;
333 
334  // Check child transactions number. We will batch if we have more than
335  // MIN_TRANSACTIONS_FOR_BATCH total number of transactions.
336  var countTransactions = function(aTransactions, aTxnCount) {
337  for (let i = 0;
338  i < aTransactions.length && aTxnCount < MIN_TRANSACTIONS_FOR_BATCH;
339  i++, aTxnCount++) {
340  let txn = aTransactions[i].wrappedJSObject;
341  if (txn && txn.childTransactions && txn.childTransactions.length)
342  aTxnCount = countTransactions(txn.childTransactions, aTxnCount);
343  }
344  return aTxnCount;
345  }
346 
347  var txnCount = countTransactions(transactions, 0);
348  this._useBatch = txnCount >= MIN_TRANSACTIONS_FOR_BATCH;
349 }
350 
351 placesAggregateTransactions.prototype = {
352  __proto__: placesBaseTransaction.prototype,
353 
354  doTransaction: function PAT_doTransaction() {
355  if (this._useBatch) {
356  var callback = {
357  _self: this,
358  runBatched: function() {
359  this._self.commit(false);
360  }
361  };
362  PlacesUtils.bookmarks.runInBatchMode(callback, null);
363  }
364  else
365  this.commit(false);
366  },
367 
368  undoTransaction: function PAT_undoTransaction() {
369  if (this._useBatch) {
370  var callback = {
371  _self: this,
372  runBatched: function() {
373  this._self.commit(true);
374  }
375  };
376  PlacesUtils.bookmarks.runInBatchMode(callback, null);
377  }
378  else
379  this.commit(true);
380  },
381 
382  commit: function PAT_commit(aUndo) {
383  // Use a copy of the transactions array, so we won't reverse the original
384  // one on undoing.
385  var transactions = this._transactions.slice(0);
386  if (aUndo)
387  transactions.reverse();
388  for (var i = 0; i < transactions.length; i++) {
389  var txn = transactions[i];
390  if (this.container > -1)
391  txn.wrappedJSObject.container = this.container;
392  if (aUndo)
393  txn.undoTransaction();
394  else
395  txn.doTransaction();
396  }
397  }
398 };
399 
400 function placesCreateFolderTransactions(aName, aContainer, aIndex,
401  aAnnotations,
402  aChildItemsTransactions) {
403  this._name = aName;
404  this._container = aContainer;
405  this._index = typeof(aIndex) == "number" ? aIndex : -1;
406  this._annotations = aAnnotations;
407  this._id = null;
408  this.childTransactions = aChildItemsTransactions || [];
409  this.redoTransaction = this.doTransaction;
410 }
411 
412 placesCreateFolderTransactions.prototype = {
413  __proto__: placesBaseTransaction.prototype,
414 
415  // childItemsTransaction support
416  get container() { return this._container; },
417  set container(val) { return this._container = val; },
418 
419  doTransaction: function PCFT_doTransaction() {
420  this._id = PlacesUtils.bookmarks.createFolder(this._container,
421  this._name, this._index);
422  if (this._annotations && this._annotations.length > 0)
423  PlacesUtils.setAnnotationsForItem(this._id, this._annotations);
424 
425  if (this.childTransactions.length) {
426  // Set the new container id into child transactions.
427  for (var i = 0; i < this.childTransactions.length; ++i) {
428  this.childTransactions[i].wrappedJSObject.container = this._id;
429  }
430 
431  let aggregateTxn = new placesAggregateTransactions("Create folder childTxn",
432  this.childTransactions);
433  aggregateTxn.doTransaction();
434  }
435 
436  if (this._GUID)
437  PlacesUtils.bookmarks.setItemGUID(this._id, this._GUID);
438  },
439 
440  undoTransaction: function PCFT_undoTransaction() {
441  if (this.childTransactions.length) {
442  let aggregateTxn = new placesAggregateTransactions("Create folder childTxn",
443  this.childTransactions);
444  aggregateTxn.undoTransaction();
445  }
446 
447  // If a GUID exists for this item, preserve it before removing the item.
448  if (PlacesUtils.annotations.itemHasAnnotation(this._id, GUID_ANNO))
449  this._GUID = PlacesUtils.bookmarks.getItemGUID(this._id);
450 
451  // Remove item only after all child transactions have been reverted.
452  PlacesUtils.bookmarks.removeItem(this._id);
453  }
454 };
455 
456 function placesCreateItemTransactions(aURI, aContainer, aIndex, aTitle,
457  aKeyword, aAnnotations,
458  aChildTransactions) {
459  this._uri = aURI;
460  this._container = aContainer;
461  this._index = typeof(aIndex) == "number" ? aIndex : -1;
462  this._title = aTitle;
463  this._keyword = aKeyword;
464  this._annotations = aAnnotations;
465  this.childTransactions = aChildTransactions || [];
466  this.redoTransaction = this.doTransaction;
467 }
468 
469 placesCreateItemTransactions.prototype = {
470  __proto__: placesBaseTransaction.prototype,
471 
472  // childItemsTransactions support for the create-folder transaction
473  get container() { return this._container; },
474  set container(val) { return this._container = val; },
475 
476  doTransaction: function PCIT_doTransaction() {
477  this._id = PlacesUtils.bookmarks.insertBookmark(this.container, this._uri,
478  this._index, this._title);
479  if (this._keyword)
480  PlacesUtils.bookmarks.setKeywordForBookmark(this._id, this._keyword);
481  if (this._annotations && this._annotations.length > 0)
482  PlacesUtils.setAnnotationsForItem(this._id, this._annotations);
483 
484  if (this.childTransactions.length) {
485  // Set the new item id into child transactions.
486  for (var i = 0; i < this.childTransactions.length; ++i) {
487  this.childTransactions[i].wrappedJSObject.id = this._id;
488  }
489  let aggregateTxn = new placesAggregateTransactions("Create item childTxn",
490  this.childTransactions);
491  aggregateTxn.doTransaction();
492  }
493  if (this._GUID)
494  PlacesUtils.bookmarks.setItemGUID(this._id, this._GUID);
495  },
496 
497  undoTransaction: function PCIT_undoTransaction() {
498  if (this.childTransactions.length) {
499  // Undo transactions should always be done in reverse order.
500  let aggregateTxn = new placesAggregateTransactions("Create item childTxn",
501  this.childTransactions);
502  aggregateTxn.undoTransaction();
503  }
504 
505  // If a GUID exists for this item, preserve it before removing the item.
506  if (PlacesUtils.annotations.itemHasAnnotation(this._id, GUID_ANNO))
507  this._GUID = PlacesUtils.bookmarks.getItemGUID(this._id);
508 
509  // Remove item only after all child transactions have been reverted.
510  PlacesUtils.bookmarks.removeItem(this._id);
511  }
512 };
513 
514 function placesCreateSeparatorTransactions(aContainer, aIndex) {
515  this._container = aContainer;
516  this._index = typeof(aIndex) == "number" ? aIndex : -1;
517  this._id = null;
518  this.redoTransaction = this.doTransaction;
519 }
520 
522  __proto__: placesBaseTransaction.prototype,
523 
524  // childItemsTransaction support
525  get container() { return this._container; },
526  set container(val) { return this._container = val; },
527 
528  doTransaction: function PCST_doTransaction() {
529  this._id = PlacesUtils.bookmarks
530  .insertSeparator(this.container, this._index);
531  if (this._GUID)
532  PlacesUtils.bookmarks.setItemGUID(this._id, this._GUID);
533  },
534 
535  undoTransaction: function PCST_undoTransaction() {
536  // If a GUID exists for this item, preserve it before removing the item.
537  if (PlacesUtils.annotations.itemHasAnnotation(this._id, GUID_ANNO))
538  this._GUID = PlacesUtils.bookmarks.getItemGUID(this._id);
539 
540  PlacesUtils.bookmarks.removeItem(this._id);
541  }
542 };
543 
544 function placesCreateLivemarkTransactions(aFeedURI, aSiteURI, aName,
545  aContainer, aIndex,
546  aAnnotations) {
547  this.redoTransaction = this.doTransaction;
548  this._feedURI = aFeedURI;
549  this._siteURI = aSiteURI;
550  this._name = aName;
551  this._container = aContainer;
552  this._index = typeof(aIndex) == "number" ? aIndex : -1;
553  this._annotations = aAnnotations;
554 }
555 
557  __proto__: placesBaseTransaction.prototype,
558 
559  // childItemsTransaction support
560  get container() { return this._container; },
561  set container(val) { return this._container = val; },
562 
563  doTransaction: function PCLT_doTransaction() {
564  this._id = PlacesUtils.livemarks.createLivemark(this._container, this._name,
565  this._siteURI, this._feedURI,
566  this._index);
567  if (this._annotations && this._annotations.length > 0)
568  PlacesUtils.setAnnotationsForItem(this._id, this._annotations);
569  if (this._GUID)
570  PlacesUtils.bookmarks.setItemGUID(this._id, this._GUID);
571  },
572 
573  undoTransaction: function PCLT_undoTransaction() {
574  // If a GUID exists for this item, preserve it before removing the item.
575  if (PlacesUtils.annotations.itemHasAnnotation(this._id, GUID_ANNO))
576  this._GUID = PlacesUtils.bookmarks.getItemGUID(this._id);
577 
578  PlacesUtils.bookmarks.removeItem(this._id);
579  }
580 };
581 
582 function placesRemoveLivemarkTransaction(aFolderId) {
583  this.redoTransaction = this.doTransaction;
584  this._id = aFolderId;
585  this._title = PlacesUtils.bookmarks.getItemTitle(this._id);
586  this._container = PlacesUtils.bookmarks.getFolderIdForItem(this._id);
587  var annos = PlacesUtils.getAnnotationsForItem(this._id);
588  // Exclude livemark service annotations, those will be recreated automatically
589  var annosToExclude = ["livemark/feedURI",
590  "livemark/siteURI",
591  "livemark/expiration",
592  "livemark/loadfailed",
593  "livemark/loading"];
594  this._annotations = annos.filter(function(aValue, aIndex, aArray) {
595  return annosToExclude.indexOf(aValue.name) == -1;
596  });
597  this._feedURI = PlacesUtils.livemarks.getFeedURI(this._id);
598  this._siteURI = PlacesUtils.livemarks.getSiteURI(this._id);
599  this._dateAdded = PlacesUtils.bookmarks.getItemDateAdded(this._id);
600  this._lastModified = PlacesUtils.bookmarks.getItemLastModified(this._id);
601 }
602 
604  __proto__: placesBaseTransaction.prototype,
605 
606  doTransaction: function PRLT_doTransaction() {
607  this._index = PlacesUtils.bookmarks.getItemIndex(this._id);
608  PlacesUtils.bookmarks.removeItem(this._id);
609  },
610 
611  undoTransaction: function PRLT_undoTransaction() {
612  this._id = PlacesUtils.livemarks.createLivemark(this._container,
613  this._title,
614  this._siteURI,
615  this._feedURI,
616  this._index);
617  PlacesUtils.bookmarks.setItemDateAdded(this._id, this._dateAdded);
618  PlacesUtils.bookmarks.setItemLastModified(this._id, this._lastModified);
619  // Restore annotations
620  PlacesUtils.setAnnotationsForItem(this._id, this._annotations);
621  }
622 };
623 
624 function placesMoveItemTransactions(aItemId, aNewContainer, aNewIndex) {
625  this._id = aItemId;
626  this._oldContainer = PlacesUtils.bookmarks.getFolderIdForItem(this._id);
627  this._newContainer = aNewContainer;
628  this._newIndex = aNewIndex;
629  this.redoTransaction = this.doTransaction;
630 }
631 
632 placesMoveItemTransactions.prototype = {
633  __proto__: placesBaseTransaction.prototype,
634 
635  doTransaction: function PMIT_doTransaction() {
636  this._oldIndex = PlacesUtils.bookmarks.getItemIndex(this._id);
637  PlacesUtils.bookmarks.moveItem(this._id, this._newContainer, this._newIndex);
638  this._undoIndex = PlacesUtils.bookmarks.getItemIndex(this._id);
639  },
640 
641  undoTransaction: function PMIT_undoTransaction() {
642  // moving down in the same container takes in count removal of the item
643  // so to revert positions we must move to oldIndex + 1
644  if (this._newContainer == this._oldContainer &&
645  this._oldIndex > this._undoIndex)
646  PlacesUtils.bookmarks.moveItem(this._id, this._oldContainer, this._oldIndex + 1);
647  else
648  PlacesUtils.bookmarks.moveItem(this._id, this._oldContainer, this._oldIndex);
649  }
650 };
651 
652 function placesRemoveItemTransaction(aItemId) {
653  this.redoTransaction = this.doTransaction;
654  this._id = aItemId;
655  this._itemType = PlacesUtils.bookmarks.getItemType(this._id);
656  if (this._itemType == Ci.nsINavBookmarksService.TYPE_FOLDER) {
657  this.childTransactions = this._getFolderContentsTransactions();
658  // Remove this folder itself.
659  let txn = PlacesUtils.bookmarks.getRemoveFolderTransaction(this._id);
660  this.childTransactions.push(txn);
661  }
662  else if (this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK) {
663  this._uri = PlacesUtils.bookmarks.getBookmarkURI(this._id);
664  this._keyword = PlacesUtils.bookmarks.getKeywordForBookmark(this._id);
665  }
666 
667  if (this._itemType != Ci.nsINavBookmarksService.TYPE_SEPARATOR)
668  this._title = PlacesUtils.bookmarks.getItemTitle(this._id);
669 
670  this._oldContainer = PlacesUtils.bookmarks.getFolderIdForItem(this._id);
671  this._annotations = PlacesUtils.getAnnotationsForItem(this._id);
672  this._dateAdded = PlacesUtils.bookmarks.getItemDateAdded(this._id);
673  this._lastModified = PlacesUtils.bookmarks.getItemLastModified(this._id);
674 }
675 
676 placesRemoveItemTransaction.prototype = {
677  __proto__: placesBaseTransaction.prototype,
678 
679  doTransaction: function PRIT_doTransaction() {
680  this._oldIndex = PlacesUtils.bookmarks.getItemIndex(this._id);
681 
682  if (this._itemType == Ci.nsINavBookmarksService.TYPE_FOLDER) {
683  let aggregateTxn = new placesAggregateTransactions("Remove item childTxn",
684  this.childTransactions);
685  aggregateTxn.doTransaction();
686  }
687  else {
688  PlacesUtils.bookmarks.removeItem(this._id);
689  if (this._uri) {
690  // if this was the last bookmark (excluding tag-items and livemark
691  // children, see getMostRecentBookmarkForURI) for the bookmark's url,
692  // remove the url from tag containers as well.
693  if (PlacesUtils.getMostRecentBookmarkForURI(this._uri) == -1) {
694  this._tags = PlacesUtils.tagging.getTagsForURI(this._uri, {});
695  PlacesUtils.tagging.untagURI(this._uri, this._tags);
696  }
697  }
698  }
699  },
700 
701  undoTransaction: function PRIT_undoTransaction() {
702  if (this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK) {
703  this._id = PlacesUtils.bookmarks.insertBookmark(this._oldContainer,
704  this._uri,
705  this._oldIndex,
706  this._title);
707  if (this._tags && this._tags.length > 0)
708  PlacesUtils.tagging.tagURI(this._uri, this._tags);
709  if (this._keyword)
710  PlacesUtils.bookmarks.setKeywordForBookmark(this._id, this._keyword);
711  }
712  else if (this._itemType == Ci.nsINavBookmarksService.TYPE_FOLDER) {
713  let aggregateTxn = new placesAggregateTransactions("Remove item childTxn",
714  this.childTransactions);
715  aggregateTxn.undoTransaction();
716  }
717  else // TYPE_SEPARATOR
718  this._id = PlacesUtils.bookmarks.insertSeparator(this._oldContainer, this._oldIndex);
719 
720  if (this._annotations.length > 0)
721  PlacesUtils.setAnnotationsForItem(this._id, this._annotations);
722 
723  PlacesUtils.bookmarks.setItemDateAdded(this._id, this._dateAdded);
724  PlacesUtils.bookmarks.setItemLastModified(this._id, this._lastModified);
725  },
726 
731  _getFolderContentsTransactions:
732  function PRIT__getFolderContentsTransactions() {
733  var transactions = [];
734  var contents =
735  PlacesUtils.getFolderContents(this._id, false, false).root;
736  for (var i = 0; i < contents.childCount; ++i) {
737  let txn = new placesRemoveItemTransaction(contents.getChild(i).itemId);
738  transactions.push(txn);
739  }
740  contents.containerOpen = false;
741  // Reverse transactions to preserve parent-child relationship.
742  return transactions.reverse();
743  }
744 };
745 
746 function placesEditItemTitleTransactions(id, newTitle) {
747  this._id = id;
748  this._newTitle = newTitle;
749  this._oldTitle = "";
750  this.redoTransaction = this.doTransaction;
751 }
752 
754  __proto__: placesBaseTransaction.prototype,
755 
756  doTransaction: function PEITT_doTransaction() {
757  this._oldTitle = PlacesUtils.bookmarks.getItemTitle(this._id);
758  PlacesUtils.bookmarks.setItemTitle(this._id, this._newTitle);
759  },
760 
761  undoTransaction: function PEITT_undoTransaction() {
762  PlacesUtils.bookmarks.setItemTitle(this._id, this._oldTitle);
763  }
764 };
765 
766 function placesEditBookmarkURITransactions(aBookmarkId, aNewURI) {
767  this._id = aBookmarkId;
768  this._newURI = aNewURI;
769  this.redoTransaction = this.doTransaction;
770 }
771 
773  __proto__: placesBaseTransaction.prototype,
774 
775  doTransaction: function PEBUT_doTransaction() {
776  this._oldURI = PlacesUtils.bookmarks.getBookmarkURI(this._id);
777  PlacesUtils.bookmarks.changeBookmarkURI(this._id, this._newURI);
778  // move tags from old URI to new URI
779  this._tags = PlacesUtils.tagging.getTagsForURI(this._oldURI, {});
780  if (this._tags.length != 0) {
781  // only untag the old URI if this is the only bookmark
782  if (PlacesUtils.getBookmarksForURI(this._oldURI, {}).length == 0)
783  PlacesUtils.tagging.untagURI(this._oldURI, this._tags);
784  PlacesUtils.tagging.tagURI(this._newURI, this._tags);
785  }
786  },
787 
788  undoTransaction: function PEBUT_undoTransaction() {
789  PlacesUtils.bookmarks.changeBookmarkURI(this._id, this._oldURI);
790  // move tags from new URI to old URI
791  if (this._tags.length != 0) {
792  // only untag the new URI if this is the only bookmark
793  if (PlacesUtils.getBookmarksForURI(this._newURI, {}).length == 0)
794  PlacesUtils.tagging.untagURI(this._newURI, this._tags);
795  PlacesUtils.tagging.tagURI(this._oldURI, this._tags);
796  }
797  }
798 };
799 
800 function placesSetItemAnnotationTransactions(aItemId, aAnnotationObject) {
801  this.id = aItemId;
802  this._anno = aAnnotationObject;
803  // create an empty old anno
804  this._oldAnno = { name: this._anno.name,
805  type: Ci.nsIAnnotationService.TYPE_STRING,
806  flags: 0,
807  value: null,
808  expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
809  this.redoTransaction = this.doTransaction;
810 }
811 
813  __proto__: placesBaseTransaction.prototype,
814 
815  doTransaction: function PSIAT_doTransaction() {
816  // Since this can be used as a child transaction this.id will be known
817  // only at this point, after the external caller has set it.
818  if (PlacesUtils.annotations.itemHasAnnotation(this.id, this._anno.name)) {
819  // Save the old annotation if it is set.
820  var flags = {}, expires = {}, mimeType = {}, type = {};
821  PlacesUtils.annotations.getItemAnnotationInfo(this.id, this._anno.name,
822  flags, expires, mimeType,
823  type);
824  this._oldAnno.flags = flags.value;
825  this._oldAnno.expires = expires.value;
826  this._oldAnno.mimeType = mimeType.value;
827  this._oldAnno.type = type.value;
828  this._oldAnno.value = PlacesUtils.annotations
829  .getItemAnnotation(this.id,
830  this._anno.name);
831  }
832 
833  PlacesUtils.setAnnotationsForItem(this.id, [this._anno]);
834  },
835 
836  undoTransaction: function PSIAT_undoTransaction() {
837  PlacesUtils.setAnnotationsForItem(this.id, [this._oldAnno]);
838  }
839 };
840 
841 function placesSetPageAnnotationTransactions(aURI, aAnnotationObject) {
842  this._uri = aURI;
843  this._anno = aAnnotationObject;
844  // create an empty old anno
845  this._oldAnno = { name: this._anno.name,
846  type: Ci.nsIAnnotationService.TYPE_STRING,
847  flags: 0,
848  value: null,
849  expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
850 
851  if (PlacesUtils.annotations.pageHasAnnotation(this._uri, this._anno.name)) {
852  // fill the old anno if it is set
853  var flags = {}, expires = {}, mimeType = {}, type = {};
854  PlacesUtils.annotations.getPageAnnotationInfo(this._uri, this._anno.name,
855  flags, expires, mimeType, type);
856  this._oldAnno.flags = flags.value;
857  this._oldAnno.expires = expires.value;
858  this._oldAnno.mimeType = mimeType.value;
859  this._oldAnno.type = type.value;
860  this._oldAnno.value = PlacesUtils.annotations
861  .getPageAnnotation(this._uri, this._anno.name);
862  }
863 
864  this.redoTransaction = this.doTransaction;
865 }
866 
868  __proto__: placesBaseTransaction.prototype,
869 
870  doTransaction: function PSPAT_doTransaction() {
871  PlacesUtils.setAnnotationsForURI(this._uri, [this._anno]);
872  },
873 
874  undoTransaction: function PSPAT_undoTransaction() {
875  PlacesUtils.setAnnotationsForURI(this._uri, [this._oldAnno]);
876  }
877 };
878 
879 function placesEditBookmarkKeywordTransactions(id, newKeyword) {
880  this.id = id;
881  this._newKeyword = newKeyword;
882  this._oldKeyword = "";
883  this.redoTransaction = this.doTransaction;
884 }
885 
887  __proto__: placesBaseTransaction.prototype,
888 
889  doTransaction: function PEBKT_doTransaction() {
890  this._oldKeyword = PlacesUtils.bookmarks.getKeywordForBookmark(this.id);
891  PlacesUtils.bookmarks.setKeywordForBookmark(this.id, this._newKeyword);
892  },
893 
894  undoTransaction: function PEBKT_undoTransaction() {
895  PlacesUtils.bookmarks.setKeywordForBookmark(this.id, this._oldKeyword);
896  }
897 };
898 
899 function placesEditBookmarkPostDataTransactions(aItemId, aPostData) {
900  this.id = aItemId;
901  this._newPostData = aPostData;
902  this._oldPostData = null;
903  this.redoTransaction = this.doTransaction;
904 }
905 
907  __proto__: placesBaseTransaction.prototype,
908 
909  doTransaction: function PEUPDT_doTransaction() {
910  this._oldPostData = PlacesUtils.getPostDataForBookmark(this.id);
911  PlacesUtils.setPostDataForBookmark(this.id, this._newPostData);
912  },
913 
914  undoTransaction: function PEUPDT_undoTransaction() {
915  PlacesUtils.setPostDataForBookmark(this.id, this._oldPostData);
916  }
917 };
918 
920  this._folderId = folderId;
921  this._newURI = uri;
922  this._oldURI = null;
923  this.redoTransaction = this.doTransaction;
924 }
925 
927  __proto__: placesBaseTransaction.prototype,
928 
929  doTransaction: function PELSUT_doTransaction() {
930  this._oldURI = PlacesUtils.livemarks.getSiteURI(this._folderId);
931  PlacesUtils.livemarks.setSiteURI(this._folderId, this._newURI);
932  },
933 
934  undoTransaction: function PELSUT_undoTransaction() {
935  PlacesUtils.livemarks.setSiteURI(this._folderId, this._oldURI);
936  }
937 };
938 
940  this._folderId = folderId;
941  this._newURI = uri;
942  this._oldURI = null;
943  this.redoTransaction = this.doTransaction;
944 }
945 
947  __proto__: placesBaseTransaction.prototype,
948 
949  doTransaction: function PELFUT_doTransaction() {
950  this._oldURI = PlacesUtils.livemarks.getFeedURI(this._folderId);
951  PlacesUtils.livemarks.setFeedURI(this._folderId, this._newURI);
952  PlacesUtils.livemarks.reloadLivemarkFolder(this._folderId);
953  },
954 
955  undoTransaction: function PELFUT_undoTransaction() {
956  PlacesUtils.livemarks.setFeedURI(this._folderId, this._oldURI);
957  PlacesUtils.livemarks.reloadLivemarkFolder(this._folderId);
958  }
959 };
960 
961 function placesEditBookmarkMicrosummaryTransactions(aItemId, newMicrosummary) {
962  this.id = aItemId;
963  this._mss = Cc["@mozilla.org/microsummary/service;1"].
964  getService(Ci.nsIMicrosummaryService);
965  this._newMicrosummary = newMicrosummary;
966  this._oldMicrosummary = null;
967  this.redoTransaction = this.doTransaction;
968 }
969 
971  __proto__: placesBaseTransaction.prototype,
972 
973  doTransaction: function PEBMT_doTransaction() {
974  this._oldMicrosummary = this._mss.getMicrosummary(this.id);
975  if (this._newMicrosummary)
976  this._mss.setMicrosummary(this.id, this._newMicrosummary);
977  else
978  this._mss.removeMicrosummary(this.id);
979  },
980 
981  undoTransaction: function PEBMT_undoTransaction() {
982  if (this._oldMicrosummary)
983  this._mss.setMicrosummary(this.id, this._oldMicrosummary);
984  else
985  this._mss.removeMicrosummary(this.id);
986  }
987 };
988 
989 function placesEditItemDateAddedTransaction(id, newDateAdded) {
990  this.id = id;
991  this._newDateAdded = newDateAdded;
992  this._oldDateAdded = null;
993  this.redoTransaction = this.doTransaction;
994 }
995 
997  __proto__: placesBaseTransaction.prototype,
998 
999  // to support folders as well
1000  get container() { return this.id; },
1001  set container(val) { return this.id = val; },
1002 
1003  doTransaction: function PEIDA_doTransaction() {
1004  this._oldDateAdded = PlacesUtils.bookmarks.getItemDateAdded(this.id);
1005  PlacesUtils.bookmarks.setItemDateAdded(this.id, this._newDateAdded);
1006  },
1007 
1008  undoTransaction: function PEIDA_undoTransaction() {
1009  PlacesUtils.bookmarks.setItemDateAdded(this.id, this._oldDateAdded);
1010  }
1011 };
1012 
1013 function placesEditItemLastModifiedTransaction(id, newLastModified) {
1014  this.id = id;
1015  this._newLastModified = newLastModified;
1016  this._oldLastModified = null;
1017  this.redoTransaction = this.doTransaction;
1018 }
1019 
1021  __proto__: placesBaseTransaction.prototype,
1022 
1023  // to support folders as well
1024  get container() { return this.id; },
1025  set container(val) { return this.id = val; },
1026 
1027  doTransaction: function PEILM_doTransaction() {
1028  this._oldLastModified = PlacesUtils.bookmarks.getItemLastModified(this.id);
1029  PlacesUtils.bookmarks.setItemLastModified(this.id, this._newLastModified);
1030  },
1031 
1032  undoTransaction: function PEILM_undoTransaction() {
1033  PlacesUtils.bookmarks.setItemLastModified(this.id, this._oldLastModified);
1034  }
1035 };
1036 
1038  this._folderId = aFolderId;
1039  this._oldOrder = null,
1040  this.redoTransaction = this.doTransaction;
1041 }
1042 
1044  __proto__: placesBaseTransaction.prototype,
1045 
1046  doTransaction: function PSSFBN_doTransaction() {
1047  this._oldOrder = [];
1048 
1049  var contents =
1050  PlacesUtils.getFolderContents(this._folderId, false, false).root;
1051  var count = contents.childCount;
1052 
1053  // sort between separators
1054  var newOrder = [];
1055  var preSep = []; // temporary array for sorting each group of items
1056  var sortingMethod =
1057  function (a, b) {
1058  if (PlacesUtils.nodeIsContainer(a) && !PlacesUtils.nodeIsContainer(b))
1059  return -1;
1060  if (!PlacesUtils.nodeIsContainer(a) && PlacesUtils.nodeIsContainer(b))
1061  return 1;
1062  return a.title.localeCompare(b.title);
1063  };
1064 
1065  for (var i = 0; i < count; ++i) {
1066  var item = contents.getChild(i);
1067  this._oldOrder[item.itemId] = i;
1068  if (PlacesUtils.nodeIsSeparator(item)) {
1069  if (preSep.length > 0) {
1070  preSep.sort(sortingMethod);
1071  newOrder = newOrder.concat(preSep);
1072  preSep.splice(0);
1073  }
1074  newOrder.push(item);
1075  }
1076  else
1077  preSep.push(item);
1078  }
1079  contents.containerOpen = false;
1080 
1081  if (preSep.length > 0) {
1082  preSep.sort(sortingMethod);
1083  newOrder = newOrder.concat(preSep);
1084  }
1085 
1086  // set the nex indexes
1087  var callback = {
1088  runBatched: function() {
1089  for (var i = 0; i < newOrder.length; ++i) {
1090  PlacesUtils.bookmarks.setItemIndex(newOrder[i].itemId, i);
1091  }
1092  }
1093  };
1094  PlacesUtils.bookmarks.runInBatchMode(callback, null);
1095  },
1096 
1097  undoTransaction: function PSSFBN_undoTransaction() {
1098  var callback = {
1099  _self: this,
1100  runBatched: function() {
1101  for (item in this._self._oldOrder)
1102  PlacesUtils.bookmarks.setItemIndex(item, this._self._oldOrder[item]);
1103  }
1104  };
1105  PlacesUtils.bookmarks.runInBatchMode(callback, null);
1106  }
1107 };
1108 
1109 function placesTagURITransaction(aURI, aTags) {
1110  this._uri = aURI;
1111  this._tags = aTags;
1112  this._unfiledItemId = -1;
1113  this.redoTransaction = this.doTransaction;
1114 }
1115 
1116 placesTagURITransaction.prototype = {
1117  __proto__: placesBaseTransaction.prototype,
1118 
1119  doTransaction: function PTU_doTransaction() {
1120  if (PlacesUtils.getMostRecentBookmarkForURI(this._uri) == -1) {
1121  // Force an unfiled bookmark first
1122  this._unfiledItemId =
1123  PlacesUtils.bookmarks
1124  .insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
1125  this._uri,
1126  PlacesUtils.bookmarks.DEFAULT_INDEX,
1127  PlacesUtils.history.getPageTitle(this._uri));
1128  if (this._GUID)
1129  PlacesUtils.bookmarks.setItemGUID(this._unfiledItemId, this._GUID);
1130  }
1131  PlacesUtils.tagging.tagURI(this._uri, this._tags);
1132  },
1133 
1134  undoTransaction: function PTU_undoTransaction() {
1135  if (this._unfiledItemId != -1) {
1136  // If a GUID exists for this item, preserve it before removing the item.
1137  if (PlacesUtils.annotations.itemHasAnnotation(this._unfiledItemId, GUID_ANNO)) {
1138  this._GUID = PlacesUtils.bookmarks.getItemGUID(this._unfiledItemId);
1139  }
1140  PlacesUtils.bookmarks.removeItem(this._unfiledItemId);
1141  this._unfiledItemId = -1;
1142  }
1143  PlacesUtils.tagging.untagURI(this._uri, this._tags);
1144  }
1145 };
1146 
1147 function placesUntagURITransaction(aURI, aTags) {
1148  this._uri = aURI;
1149  if (aTags) {
1150  // Within this transaction, we cannot rely on tags given by itemId
1151  // since the tag containers may be gone after we call untagURI.
1152  // Thus, we convert each tag given by its itemId to name.
1153  this._tags = aTags;
1154  for (var i=0; i < aTags.length; i++) {
1155  if (typeof(this._tags[i]) == "number")
1156  this._tags[i] = PlacesUtils.bookmarks.getItemTitle(this._tags[i]);
1157  }
1158  }
1159  else
1160  this._tags = PlacesUtils.tagging.getTagsForURI(this._uri, {});
1161 
1162  this.redoTransaction = this.doTransaction;
1163 }
1164 
1165 placesUntagURITransaction.prototype = {
1166  __proto__: placesBaseTransaction.prototype,
1167 
1168  doTransaction: function PUTU_doTransaction() {
1169  PlacesUtils.tagging.untagURI(this._uri, this._tags);
1170  },
1171 
1172  undoTransaction: function PUTU_undoTransaction() {
1173  PlacesUtils.tagging.tagURI(this._uri, this._tags);
1174  }
1175 };
1176 
1177 
1178 function NSGetModule(aCompMgr, aFileSpec) {
1179  return XPCOMUtils.generateModule([placesTransactionsService]);
1180 }
const DESCRIPTION_ANNO
function placesSortFolderByNameTransactions(aFolderId)
const LOAD_IN_SIDEBAR_ANNO
function placesEditItemDateAddedTransaction(id, newDateAdded)
function createItem(library, url)
__defineGetter__("PlacesUtils", function(){delete this.PlacesUtils var tmpScope={};Components.utils.import("resource://gre/modules/utils.js", tmpScope);return this.PlacesUtils=tmpScope.PlacesUtils;})
menuItem id
Definition: FeedWriter.js:971
onPageChanged aValue
Definition: FeedWriter.js:1395
function NSGetModule(aCompMgr, aFileSpec)
function clear(dbq)
sbDeviceFirmwareAutoCheckForUpdate prototype flags
function placesEditBookmarkKeywordTransactions(id, newKeyword)
sbDeviceFirmwareAutoCheckForUpdate prototype contractID
function placesCreateSeparatorTransactions(aContainer, aIndex)
sidebarFactory createInstance
Definition: nsSidebar.js:351
sbOSDControlService prototype QueryInterface
sbDeviceFirmwareAutoCheckForUpdate prototype classDescription
function placesMoveItemTransactions(aItemId, aNewContainer, aNewIndex)
getService(Ci.sbIFaceplateManager)
function placesTagURITransaction(aURI, aTags)
function placesEditLivemarkSiteURITransactions(folderId, uri)
var count
Definition: test_bug7406.js:32
function placesRemoveItemTransaction(aItemId)
function placesSetItemAnnotationTransactions(aItemId, aAnnotationObject)
this _dialogInput val(dateText)
function placesEditBookmarkPostDataTransactions(aItemId, aPostData)
function placesEditItemTitleTransactions(id, newTitle)
grep callback
function placesBaseTransaction()
return null
Definition: FeedWriter.js:1143
function placesCreateItemTransactions(aURI, aContainer, aIndex, aTitle, aKeyword, aAnnotations, aChildTransactions)
function placesEditLivemarkFeedURITransactions(folderId, uri)
function placesSetPageAnnotationTransactions(aURI, aAnnotationObject)
function placesUntagURITransaction(aURI, aTags)
_updateCookies aName
var uri
Definition: FeedWriter.js:1135
countRef value
Definition: FeedWriter.js:1423
function placesCreateLivemarkTransactions(aFeedURI, aSiteURI, aName, aContainer, aIndex, aAnnotations)
function placesRemoveLivemarkTransaction(aFolderId)
function placesTransactionsService()
sbDeviceFirmwareAutoCheckForUpdate prototype classID
_window _feedURI
Definition: FeedWriter.js:1144
function placesEditBookmarkURITransactions(aBookmarkId, aNewURI)
function placesCreateFolderTransactions(aName, aContainer, aIndex, aAnnotations, aChildItemsTransactions)
function placesEditItemLastModifiedTransaction(id, newLastModified)
function placesAggregateTransactions(name, transactions)
_getSelectedPageStyle s i
const MIN_TRANSACTIONS_FOR_BATCH
function placesEditBookmarkMicrosummaryTransactions(aItemId, newMicrosummary)