Places:Transactions

From MozillaWiki
Jump to: navigation, search

Introduction

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);

The 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.

Basic Transactions

All Places transactions inherit from PlacesBaseTransaction.

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);

Grouping Transactions

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.

Creating Folders

Normally, creating folders is no big hassle. You just use PlacesCreateFolderTransaction:

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.

The 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, PlacesCreateFolderTransaction objects' 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 Folders

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.