The Places:Controller performs all edit operations (node creation, removal, modification) on Bookmark items using Transactions. Transactions support the execution of commands, as well as reverting (undoing) and repeating (redoing) them. Transactions thus support the Undo/Redo feature.
This document talks about how the Transactions are used, and what you should know as a programmer.
All Edits Are Transaction-Based
Any edit you perform to a bookmark item in a Places View is generally undo-able using the undo command (and then subsequently re-doable). To take advantage of this in your code, you need to use one of the
Places*Transactions defined in
controller.js, rather than just using the data APIs directly.
Constructing a Transaction
To create a new folder called "Foo" at the end of the Bookmarks Toolbar, you might write code that looks like this:
var bms = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. getService(Ci.nsINavBookmarksService); var txn = new PlacesCreateFolderTransaction("Foo", bms.toolbarRoot, -1); PlacesController.tm.doTransaction(txn);
PlacesController maintains an active transaction manager for each instance of itself (e.g. one for the Places Organizer, one for each browser window, etc. This object implements nsITransactionManager.
All Places transactions inherit from
Here are some of the basic transactions provided:
function PlacesCreateItemTransaction(uri, container, index); function PlacesCreateSeparatorTransaction(container, index); function PlacesMoveFolderTransaction(id, oldContainer, oldIndex, newContainer, newIndex); function PlacesMoveItemTransaction(uri, oldContainer, oldIndex, newContainer, newIndex); function PlacesRemoveItemTransaction(uri, oldContainer, oldIndex); function PlacesRemoveSeparatorTransaction(oldContainer, oldIndex); function PlacesEditItemTitleTransaction(uri, newTitle); function PlacesEditFolderTitleTransaction(id, newTitle); function PlacesEditBookmarkKeywordTransaction(uri, newKeyword);
Several transactions can be aggregated together into a single atomic unit of work using
PlacesAggregateTransaction. For example, consider creating and naming a new Bookmark:
var ios = Cc["@mozilla.org/network/io-service"]. getService(Ci.nsIIOService); var uri = ios.newURI("http://www.mozilla.org/", null, null); var bms = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. getService(Ci.nsINavBookmarksService); var createTxn = new PlacesCreateItemTransaction(uri, bms.toolbarRoot, -1); var nameTxn = new PlacesEditItemTitleTransaction(uri, "Mozilla.org"); var aggTxn = new PlacesAggregateTransaction("Create Bookmark", [createTxn, nameTxn]); PlacesController.tm.doTransaction(aggTxn);
The aggregator does/undoes/redoes each of the transactions in the set provided.
Normally, creating folders is no big hassle. You just use
var bms = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. getService(Ci.nsINavBookmarksService); var createTxn = new PlacesCreateFolderTransaction("Something", bms.toolbarRoot, -1); PlacesController.tm.doTransaction(createTxn);
For pasting, the
PlacesController does something a little special. Since the user may be pasting in a nested folder structure, when it creates the set of CreateTransactions for the insert operation, there is a problem: the insertion is two-pass. The first pass generates a list of transactions for the insertion, and the second pass is the execution of the transactions using
doTransaction. The problem is that all the CreateTransactions take the insertion container as a parameter to the constructor, but the folder the items are being inserted into hasn't been created yet for nested contents!
The solution is that on CreateTransactions, the
container property is a public field, and that every pasted folder creates its own list of CreateTransactions to create child items.
PlacesCreateFolderTransaction method has a
childTransactions field which is an array of CreateTransactions taht are used to construct child nodes.
During the first phase of the paste,
childTransactions fields are recursively populated with these CreateTransactions.
During the second phase of the paste, these
childTransactions lists are walked after the containing folder is created, and for each child CreateTransaction, the
container property is set to the id of the newly created folder, so that the item is created into the right place.
Removing a folder is pretty straight forward:
var txn = new PlacesRemoveFolderTransaction(folderId); PlacesController.tm.doTransaction(txn);
The implementation is a little more complex. Some of this functionality is implemented by the bookmarks service itself. For a complete description of how this is implemented, read this posting to dev-apps-firefox.