diff --git a/browser/components/places/content/bookmarkProperties.js b/browser/components/places/content/bookmarkProperties.js index b90b1a8d4c2..1e9015a80b8 100755 --- a/browser/components/places/content/bookmarkProperties.js +++ b/browser/components/places/content/bookmarkProperties.js @@ -710,7 +710,7 @@ var BookmarkPropertiesPanel = { */ _getEditTitleTransaction: function BPP__getEditTitleTransaction(aItemId, aNewTitle) { - return new PlacesEditItemTitleTransaction(aItemId, aNewTitle); + return PlacesUtils.ptm.editItemTitle(aItemId, aNewTitle); }, /** @@ -806,13 +806,13 @@ var BookmarkPropertiesPanel = { // location var url = PlacesUtils._uri(this._element("editURLBar").value); if (!this._bookmarkURI.equals(url)) - transactions.push(new PlacesEditBookmarkURITransaction(itemId, url)); + transactions.push(PlacesUtils.ptm.editBookmarkURI(itemId, url)); // keyword transactions var newKeyword = this._element("keywordTextfield").value; if (newKeyword != this._bookmarkKeyword) { - transactions.push( - new PlacesEditBookmarkKeywordTransaction(itemId, newKeyword)); + transactions.push(PlacesUtils.ptm. + editBookmarkKeyword(itemId, newKeyword)); } // microsummaries @@ -828,15 +828,14 @@ var BookmarkPropertiesPanel = { (newMicrosummary != null && !this._mss.isMicrosummary(itemId, newMicrosummary))) { transactions.push( - new PlacesEditBookmarkMicrosummaryTransaction(itemId, - newMicrosummary)); + PlacesUtils.ptm.editBookmarkMicrosummary(itemId, newMicrosummary)); } // load in sidebar var loadInSidebarChecked = this._element("loadInSidebarCheckbox").checked; if (loadInSidebarChecked != this._loadBookmarkInSidebar) { transactions.push( - new PlacesSetLoadInSidebarTransaction(itemId, loadInSidebarChecked)); + PlacesUtils.ptm.setLoadInSidebar(itemId, loadInSidebarChecked)); } } else if (this._itemType == LIVEMARK_CONTAINER) { @@ -844,7 +843,7 @@ var BookmarkPropertiesPanel = { var feedURI = PlacesUtils._uri(feedURIString); if (!this._feedURI.equals(feedURI)) { transactions.push( - new PlacesEditLivemarkFeedURITransaction(this._folderId, feedURI)); + PlacesUtils.ptm.editLivemarkFeedURI(this._folderId, feedURI)); } // Site Location is empty, we can set its URI to null @@ -856,7 +855,7 @@ var BookmarkPropertiesPanel = { if ((!newSiteURI && this._siteURI) || (newSiteURI && (!this._siteURI || !this._siteURI.equals(newSiteURI)))) { transactions.push( - new PlacesEditLivemarkSiteURITransaction(this._folderId, newSiteURI)); + PlacesUtils.ptm.editLivemarkSiteURI(this._folderId, newSiteURI)); } } @@ -865,7 +864,7 @@ var BookmarkPropertiesPanel = { if (transactions.length > 0) { window.arguments[0].performed = true; var aggregate = - new PlacesAggregateTransaction(this._getDialogTitle(), transactions); + PlacesUtils.ptm.aggregateTransactions(this._getDialogTitle(), transactions); this._tm.doTransaction(aggregate); } }, @@ -911,20 +910,20 @@ var BookmarkPropertiesPanel = { var microsummary = this._element("namePicker").selectedItem.microsummary; if (microsummary) { childTransactions.push( - new PlacesEditBookmarkMicrosummaryTransaction(-1, microsummary)); + PlacesUtils.ptm.editBookmarkMicrosummary(-1, microsummary)); } - var transactions = [new PlacesCreateItemTransaction(uri, aContainer, aIndex, - title, keyword, - annotations, - childTransactions)]; + var transactions = [PlacesUtils.ptm.createItem(uri, aContainer, aIndex, + title, keyword, + annotations, + childTransactions)]; if (this._postData) { - transactions.push(new PlacesEditURIPostDataTransaction(uri, - this._postData)); + transactions.push( + PlacesUtils.ptm.editURIPostData(uri, this._postData)); } - return new PlacesAggregateTransaction(this._getDialogTitle(), - transactions); + + return PlacesUtils.ptm.aggregateTransactions(this._getDialogTitle(), transactions); }, /** @@ -936,7 +935,7 @@ var BookmarkPropertiesPanel = { for (var i = 0; i < this._URIList.length; ++i) { var uri = this._URIList[i]; var title = this._getURITitleFromHistory(uri); - transactions.push(new PlacesCreateItemTransaction(uri, -1, -1, title)); + transactions.push(PlacesUtils.ptm.createItem(uri, -1, -1, title)); } return transactions; }, @@ -956,9 +955,8 @@ var BookmarkPropertiesPanel = { if (description) annotations.push(this._getDescriptionAnnotation(description)); - return new PlacesCreateFolderTransaction(folderName, aContainer, aIndex, - annotations, - childItemsTransactions); + return PlacesUtils.ptm.createFolder(folderName, aContainer, aIndex, + annotations, childItemsTransactions); }, /** @@ -976,9 +974,8 @@ var BookmarkPropertiesPanel = { siteURI = PlacesUtils._uri(siteURIString); var name = this._element("namePicker").value; - return new PlacesCreateLivemarkTransaction(feedURI, siteURI, - name, aContainer, - aIndex); + return PlacesUtils.ptm.createLivemark(feedURI, siteURI, name, + aContainer, aIndex); }, /** diff --git a/browser/components/places/content/controller.js b/browser/components/places/content/controller.js index 9eb5ac008cc..2f639cedc85 100755 --- a/browser/components/places/content/controller.js +++ b/browser/components/places/content/controller.js @@ -993,8 +993,8 @@ PlacesController.prototype = { var ip = this._view.insertionPoint; if (!ip) throw Cr.NS_ERROR_NOT_AVAILABLE; - var txn = new PlacesCreateSeparatorTransaction(ip.itemId, ip.index); - PlacesUtils.tm.doTransaction(txn); + var txn = PlacesUtils.ptm.createSeparator(ip.itemId, ip.index); + PlacesUtils.ptm.commitTransaction(txn); }, /** @@ -1011,9 +1011,9 @@ PlacesController.prototype = { */ sortFolderByName: function PC_sortFolderByName() { var selectedNode = this._view.selectedNode; - var txn = new PlacesSortFolderByNameTransaction(selectedNode.itemId, - selectedNode.bookmarkIndex); - PlacesUtils.tm.doTransaction(txn); + var txn = PlacesUtils.ptm.sortFolderByName(selectedNode.itemId, + selectedNode.bookmarkIndex); + PlacesUtils.ptm.commitTransaction(txn); }, /** @@ -1021,11 +1021,11 @@ PlacesController.prototype = { */ setBookmarksToolbarFolder: function PC_setBookmarksToolbarFolder() { if (!this._view.hasSingleSelection) - return false; + return; + var selectedNode = this._view.selectedNode; - var txn = new PlacesSetBookmarksToolbarTransaction(selectedNode.itemId); - PlacesUtils.tm.doTransaction(txn); - return true; + var txn = PlacesUtils.ptm.setBookmarksToolbar(selectedNode.itemId); + PlacesUtils.ptm.commitTransaction(txn); }, @@ -1068,14 +1068,13 @@ PlacesController.prototype = { /** * Creates a set of transactions for the removal of a range of items. * A range is an array of adjacent nodes in a view. - * @param range + * @param [in] range * An array of nodes to remove. Should all be adjacent. - * @param transactions + * @param [out] transactions * An array of transactions. */ _removeRange: function PC__removeRange(range, transactions) { NS_ASSERT(transactions instanceof Array, "Must pass a transactions array"); - var index = PlacesUtils.getIndexOfNode(range[0]); var removedFolders = []; @@ -1084,28 +1083,10 @@ PlacesController.prototype = { if (this._shouldSkipNode(node, removedFolders)) continue; - if (PlacesUtils.nodeIsFolder(node)) { - // TODO -- node.parent might be a query and not a folder. See bug 324948 - var folder = node; - removedFolders.push(folder); - transactions.push(new PlacesRemoveFolderTransaction(folder.itemId)); - } - else if (PlacesUtils.nodeIsSeparator(node)) { - // A Bookmark separator. - transactions.push(new PlacesRemoveSeparatorTransaction(node.itemId, - node.parent.itemId, index)); - } - else if (PlacesUtils.nodeIsFolder(node.parent)) { - // A Bookmark in a Bookmark Folder. - transactions.push(new PlacesRemoveItemTransaction(node.itemId, - PlacesUtils._uri(node.uri), node.parent.itemId, index)); - } - else if (PlacesUtils.nodeIsBookmark(node)) { - // A Bookmark in a query. - var folderId = PlacesUtils.bookmarks.getFolderIdForItem(node.itemId); - transactions.push(new PlacesRemoveItemTransaction(node.itemId, - PlacesUtils._uri(node.uri), folderId, index)); - } + if (PlacesUtils.nodeIsFolder(node)) + removedFolders.push(node); + + transactions.push(PlacesUtils.ptm.removeItem(node.itemId)); } }, @@ -1122,8 +1103,8 @@ PlacesController.prototype = { for (var i = ranges.length - 1; i >= 0 ; --i) this._removeRange(ranges[i], transactions); if (transactions.length > 0) { - var txn = new PlacesAggregateTransaction(txnName, transactions); - PlacesUtils.tm.doTransaction(txn); + var txn = PlacesUtils.ptm.aggregateTransactions(txnName, transactions); + PlacesUtils.ptm.commitTransaction(txn); } }, @@ -1379,8 +1360,8 @@ PlacesController.prototype = { var transactions = getTransactions([PlacesUtils.TYPE_X_MOZ_PLACE, PlacesUtils.TYPE_X_MOZ_URL, PlacesUtils.TYPE_UNICODE]); - var txn = new PlacesAggregateTransaction("Paste", transactions); - PlacesUtils.tm.doTransaction(txn); + var txn = PlacesUtils.ptm.aggregateTransactions("Paste", transactions); + PlacesUtils.ptm.commitTransaction(txn); } }; @@ -1513,781 +1494,8 @@ var PlacesControllerDragHelper = { insertionPoint.index, copy)); } - var txn = new PlacesAggregateTransaction("DropItems", transactions); - PlacesUtils.tm.doTransaction(txn); - } -}; - -/** - * Method and utility stubs for Place Edit Transactions - */ -function PlacesBaseTransaction() { -} -PlacesBaseTransaction.prototype = { - utils: PlacesUtils, - - bookmarks: Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. - getService(Ci.nsINavBookmarksService), - _livemarks: null, - get livemarks() { - if (!this._livemarks) { - this._livemarks = - Cc["@mozilla.org/browser/livemark-service;2"]. - getService(Ci.nsILivemarkService); - } - return this._livemarks; - }, - - // The minimum amount of transactions we should tell our observers to begin - // batching (rather than letting them do incremental drawing). - MIN_TRANSACTIONS_FOR_BATCH: 5, - - LOG: LOG, - redoTransaction: function PIT_redoTransaction() { - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - get isTransient() { - return false; - }, - - merge: function PIT_merge(transaction) { - return false; - } -}; - -/** - * Performs several Places Transactions in a single batch. - */ -function PlacesAggregateTransaction(name, transactions) { - this._transactions = transactions; - this._name = name; - this.container = -1; - this.redoTransaction = this.doTransaction; -} -PlacesAggregateTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - doTransaction: function PAT_doTransaction() { - this.LOG("== " + this._name + " (Aggregate) =============="); - if (this._transactions.length >= this.MIN_TRANSACTIONS_FOR_BATCH) { - var callback = { - _self: this, - runBatched: function() { - this._self.commit(false); - } - }; - this.utils.bookmarks.runInBatchMode(callback, null); - } - else - this.commit(false); - - this.LOG("== " + this._name + " (Aggregate Ends) ========="); - }, - - undoTransaction: function PAT_undoTransaction() { - this.LOG("== UN" + this._name + " (UNAggregate) ============"); - if (this._transactions.length >= this.MIN_TRANSACTIONS_FOR_BATCH) { - var callback = { - _self: this, - runBatched: function() { - this._self.commit(true); - } - }; - this.utils.bookmarks.runInBatchMode(callback, null); - } - else - this.commit(true); - - this.LOG("== UN" + this._name + " (UNAggregate Ends) ======="); - }, - - commit: function PAT_commit(aUndo) { - for (var i = 0; i < this._transactions.length; ++i) { - var txn = this._transactions[i]; - if (this.container > -1) - txn.container = this.container; - if (aUndo) - txn.undoTransaction(); - else - txn.doTransaction(); - } - } -}; - - -/** - * Transaction for creating a new folder item. - * - * @param aName - * the name of the new folder - * @param aContainer - * the identifier of the folder in which the new folder should be - * added. - * @param [optional] aIndex - * the index of the item in aContainer, pass -1 or nothing to create - * the item at the end of aContainer. - * @param [optional] aAnnotations - * the annotations to set for the new folder. - * @param [optional] aChildItemsTransactions - * array of transactions for items to be created under the new folder. - */ -function PlacesCreateFolderTransaction(aName, aContainer, aIndex, - aAnnotations, aChildItemsTransactions) { - this._name = aName; - this._container = aContainer; - this._index = typeof(aIndex) == "number" ? aIndex : -1; - this._annotations = aAnnotations; - this._id = null; - this._childItemsTransactions = aChildItemsTransactions || []; - this.redoTransaction = this.doTransaction; -} -PlacesCreateFolderTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - // childItemsTransaction support - get container() { return this._container; }, - set container(val) { return this._container = val; }, - - doTransaction: function PCFT_doTransaction() { - var bookmarks = this.utils.bookmarks; - this._id = bookmarks.createFolder(this._container, this._name, this._index); - if (this._annotations.length > 0) - this.utils.setAnnotationsForItem(this._id, this._annotations); - - for (var i = 0; i < this._childItemsTransactions.length; ++i) { - var txn = this._childItemsTransactions[i]; - txn.container = this._id; - txn.doTransaction(); - } - }, - - undoTransaction: function PCFT_undoTransaction() { - this.bookmarks.removeFolder(this._id); - for (var i = 0; i < this._childItemsTransactions.length; ++i) { - var txn = this.childItemsTransactions[i]; - txn.undoTransaction(); - } - } -}; - -/** - * Transaction for creating a new bookmark item - * - * @param aURI - * the uri of the new bookmark (nsIURI) - * @param aContainer - * the identifier of the folder in which the bookmark should be added. - * @param [optional] aIndex - * the index of the item in aContainer, pass -1 or nothing to create - * the item at the end of aContainer. - * @param [optional] aTitle - * the title of the new bookmark. - * @param [optional] aKeyword - * the keyword of the new bookmark. - * @param [optional] aAnnotations - * the annotations to set for the new bookmark. - * @param [optional] aChildTransactions - * child transactions to commit after creating the bookmark. Prefer - * using any of the arguments above if possible. In general, a child - * transations should be used only if the change it does has to be - * reverted manually when removing the bookmark item. - * a child transaction must support setting its bookmark-item - * identifier via an "id" js setter. - */ -function PlacesCreateItemTransaction(aURI, aContainer, aIndex, aTitle, - aKeyword, aAnnotations, - aChildTransactions) { - this._uri = aURI; - this._container = aContainer; - this._index = typeof(aIndex) == "number" ? aIndex : -1; - this._title = aTitle; - this._keyword = aKeyword; - this._annotations = aAnnotations; - this._childTransactions = aChildTransactions || []; - this.redoTransaction = this.doTransaction; -} -PlacesCreateItemTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - // childItemsTransactions support for the create-folder transaction - get container() { return this._container; }, - set container(val) { return this._container = val; }, - - doTransaction: function PCIT_doTransaction() { - var bookmarks = this.utils.bookmarks; - this._id = bookmarks.insertBookmark(this.container, this._uri, this._index, - this._title); - if (this._keyword) - bookmarks.setKeywordForBookmark(this._id, this._keyword); - if (this._annotations && this._annotations.length > 0) - this.utils.setAnnotationsForItem(this._id, this._annotations); - - for (var i = 0; i < this._childTransactions.length; ++i) { - var txn = this._childTransactions[i]; - txn.id = this._id; - txn.doTransaction(); - } - }, - - undoTransaction: function PCIT_undoTransaction() { - this.utils.bookmarks.removeItem(this._id); - for (var i = 0; i < this._childTransactions.length; ++i) { - var txn = this._childTransactions[i]; - txn.undoTransaction(); - } - } -}; - -/** - * Transaction for creating a new separator item - * - * @param aContainer - * the identifier of the folder in which the separator should be - * added. - * @param [optional] aIndex - * the index of the item in aContainer, pass -1 or nothing to create - * the separator at the end of aContainer. - */ -function PlacesCreateSeparatorTransaction(aContainer, aIndex) { - this._container = aContainer; - this._index = typeof(aIndex) == "number" ? aIndex : -1; - this._id = null; -} -PlacesCreateSeparatorTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - // childItemsTransaction support - get container() { return this._container; }, - set container(val) { return this._container = val; }, - - doTransaction: function PCST_doTransaction() { - this.LOG("Create separator in: " + this.container + "," + this._index); - this._id = this.bookmarks.insertSeparator(this.container, this._index); - }, - - undoTransaction: function PCST_undoTransaction() { - this.LOG("UNCreate separator from: " + this.container + "," + this._index); - this.bookmarks.removeChildAt(this.container, this._index); - } -}; - -/** - * Transaction for creating a new live-bookmark item. - * - * @see nsILivemarksService::createLivemark for documentation regarding the - * first three arguments. - * - * @param aContainer - * the identifier of the folder in which the live-bookmark should be - * added. - * @param [optional] aIndex - * the index of the item in aContainer, pass -1 or nothing to create - * the item at the end of aContainer. - * @param [optional] aAnnotations - * the annotations to set for the new live-bookmark. - */ -function PlacesCreateLivemarkTransaction(aFeedURI, aSiteURI, aName, - aContainer, aIndex, aAnnotations) { - this._feedURI = aFeedURI; - this._siteURI = aSiteURI; - this._name = aName; - this._container = aContainer; - this._index = typeof(aIndex) == "number" ? aIndex : -1; - this._annotations = aAnnotations; -} -PlacesCreateLivemarkTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - // childItemsTransaction support - get container() { return this._container; }, - set container(val) { return this._container = val; }, - - doTransaction: function PCLT_doTransaction() { - this._id = this.utils.livemarks - .createLivemark(this._container, this._name, this._siteURI, - this._feedURI, this._index); - if (this._annotations) - this.utils.setAnnotationsForItem(this._id, this._annotations); - }, - - undoTransaction: function PCLT_undoTransaction() { - this.bookmarks.removeFolder(this._id); - } -}; - -/** - * Move an Item - */ -function PlacesMoveItemTransaction(aItemId, aNewContainer, aNewIndex) { - NS_ASSERT(!isNaN(aItemId + aNewContainer + aNewIndex), "Parameter is NaN!"); - NS_ASSERT(aNewIndex >= -1, "invalid insertion index"); - this._id = aItemId; - this._oldContainer = this.utils.bookmarks.getFolderIdForItem(this._id); - this._oldIndex = this.utils.bookmarks.getItemIndex(this._id); - NS_ASSERT(this._oldContainer > 0 && this._oldIndex >= 0, "invalid item"); - this._newContainer = aNewContainer; - this._newIndex = aNewIndex; - this.redoTransaction = this.doTransaction; -} -PlacesMoveItemTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - doTransaction: function PMIT_doTransaction() { - this.bookmarks.moveItem(this._id, this._newContainer, this._newIndex); - }, - - undoTransaction: function PMIT_undoTransaction() { - this.bookmarks.moveItem(this._id, this._oldContainer, this._oldIndex); - } -}; - -/** - * Remove a Folder - * This is a little complicated. When we remove a container we need to remove - * all of its children. We can't just repurpose our existing transactions for - * this since they cache their parent container id. Since the folder structure - * is being removed, this id is being destroyed and when it is re-created will - * likely have a different id. - */ - -function PlacesRemoveFolderTransaction(id) { - this._removeTxn = this.bookmarks.getRemoveFolderTransaction(id); - this._id = id; - this._transactions = []; // A set of transactions to remove content. - this.redoTransaction = this.doTransaction; -} -PlacesRemoveFolderTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - /** - * Create a flat, ordered list of transactions for a depth-first recreation - * of items within this folder. - */ - _saveFolderContents: function PRFT__saveFolderContents() { - this._transactions = []; - var contents = this.utils.getFolderContents(this._id, false, false).root; - var ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - for (var i = 0; i < contents.childCount; ++i) { - var child = contents.getChild(i); - var txn; - if (child.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER) { - txn = new PlacesRemoveFolderTransaction(child.itemId); - } - else if (child.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) { - txn = new PlacesRemoveSeparatorTransaction(child.itemId, this._id, i); - } - else { - txn = new PlacesRemoveItemTransaction(child.itemId, - ios.newURI(child.uri, null, null), - this._id, i); - } - this._transactions.push(txn); - } - }, - - doTransaction: function PRFT_doTransaction() { - this._title = this.bookmarks.getItemTitle(this._id); - this._annotations = this.utils.getAnnotationsForItem(this._id); - - this.LOG("Remove Folder: " + this._title); - - this._saveFolderContents(); - - // Remove children backwards to preserve parent-child relationships. - for (var i = this._transactions.length - 1; i >= 0; --i) - this._transactions[i].doTransaction(); - - // Remove this folder itself. - this._removeTxn.doTransaction(); - }, - - undoTransaction: function PRFT_undoTransaction() { - this._removeTxn.undoTransaction(); - - this.LOG("UNRemove Folder: " + this._title); - - // Repopulate annotations - if (this._annotations && this._annotations.length > 0) - this.utils.setAnnotationsForItem(this._id, this._annotations); - - // Create children forwards to preserve parent-child relationships. - for (var i = 0; i < this._transactions.length; ++i) - this._transactions[i].undoTransaction(); - } -}; - -/** - * Remove an Item - */ -function PlacesRemoveItemTransaction(id, uri, oldContainer, oldIndex) { - this._id = id; - this._uri = uri; - this._oldContainer = oldContainer; - this._oldIndex = oldIndex; - this._annotations = []; - this.redoTransaction = this.doTransaction; -} -PlacesRemoveItemTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - doTransaction: function PRIT_doTransaction() { - this.LOG("Remove Item: " + this._uri.spec + " from: " + this._oldContainer + "," + this._oldIndex); - this._title = this.bookmarks.getItemTitle(this._id); - this._annotations = this.utils.getAnnotationsForItem(this._id); - this.utils.bookmarks.removeItem(this._id); - }, - - undoTransaction: function PRIT_undoTransaction() { - this.LOG("UNRemove Item: " + this._uri.spec + " from: " + this._oldContainer + "," + this._oldIndex); - this._id = this.bookmarks.insertBookmark(this._oldContainer, this._uri, - this._oldIndex, this._title); - this.utils.setAnnotationsForItem(this._id, this._annotations); - } -}; - -/** - * Remove a separator - */ -function PlacesRemoveSeparatorTransaction(id, oldContainer, oldIndex) { - this._id = id; - this._oldContainer = oldContainer; - this._oldIndex = oldIndex; - this.redoTransaction = this.doTransaction; -} -PlacesRemoveSeparatorTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - doTransaction: function PRST_doTransaction() { - this.LOG("Remove Separator from: " + this._oldContainer + "," + this._oldIndex); - this.utils.bookmarks.removeItem(this._id); - }, - - undoTransaction: function PRST_undoTransaction() { - this.LOG("UNRemove Separator from: " + this._oldContainer + "," + this._oldIndex); - this.bookmarks.insertSeparator(this._oldContainer, this._oldIndex); - } -}; - -/** - * Edit a bookmark's title. - */ -function PlacesEditItemTitleTransaction(id, newTitle) { - this._id = id; - this._newTitle = newTitle; - this._oldTitle = ""; - this.redoTransaction = this.doTransaction; -} -PlacesEditItemTitleTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - doTransaction: function PEITT_doTransaction() { - this._oldTitle = this.bookmarks.getItemTitle(this._id); - this.bookmarks.setItemTitle(this._id, this._newTitle); - }, - - undoTransaction: function PEITT_undoTransaction() { - this.bookmarks.setItemTitle(this._id, this._oldTitle); - } -}; - -/** - * Edit a bookmark's uri. - */ -function PlacesEditBookmarkURITransaction(aBookmarkId, aNewURI) { - this._id = aBookmarkId; - this._newURI = aNewURI; - this.redoTransaction = this.doTransaction; -} -PlacesEditBookmarkURITransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - doTransaction: function PEBUT_doTransaction() { - this._oldURI = this.bookmarks.getBookmarkURI(this._id); - this.bookmarks.changeBookmarkURI(this._id, this._newURI); - }, - - undoTransaction: function PEBUT_undoTransaction() { - this.bookmarks.changeBookmarkURI(this._id, this._oldURI); - } -}; - -/** - * Set/Unset Load-in-sidebar annotation - */ -function PlacesSetLoadInSidebarTransaction(aBookmarkId, aLoadInSidebar) { - this.id = aBookmarkId; - this._loadInSidebar = aLoadInSidebar; - this.redoTransaction = this.doTransaction; -} -PlacesSetLoadInSidebarTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - _anno: { - name: LOAD_IN_SIDEBAR_ANNO, - type: Ci.nsIAnnotationService.TYPE_INT32, - value: 1, - flags: 0, - expires: Ci.nsIAnnotationService.EXPIRE_NEVER - }, - - doTransaction: function PSLIST_doTransaction() { - this._wasSet = this.utils.annotations - .itemHasAnnotation(this.id, this._anno.name); - if (this._loadInSidebar) { - this.utils.setAnnotationsForItem(this.id, [this._anno]); - } - else { - try { - this.utils.annotations.removeItemAnnotation(this.id, this._anno.name); - } catch(ex) { } - } - }, - - undoTransaction: function PSLIST_undoTransaction() { - if (this._wasSet != this._loadInSidebar) { - this._loadInSidebar = !this._loadInSidebar; - this.doTransaction(); - } - } -}; - -/** - * Edit a the description of a bookmark or a folder - * - */ -function PlacesEditItemDescriptionTransaction(aItemId, aDescription) { - this.id = aItemId; - this._newDescription = aDescription; - this.redoTransaction = this.doTransaction; -} -PlacesEditItemDescriptionTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - _oldDescription: "", - DESCRIPTION_ANNO: DESCRIPTION_ANNO, - nsIAnnotationService: Components.interfaces.nsIAnnotationService, - - doTransaction: function PSLIST_doTransaction() { - const annos = this.utils.annotations; - - if (annos.itemHasAnnotation(this.id, this.DESCRIPTION_ANNO)) { - this._oldDescription = - annos.getItemAnnotation(this.id, this.DESCRIPTION_ANNO); - } - - if (this._newDescription) { - annos.setItemAnnotation(this.id, this.DESCRIPTION_ANNO, - this._newDescription, 0, - this.nsIAnnotationService.EXPIRE_NEVER); - } - else if (this._oldDescription) - annos.removeItemAnnotation(this.id, this.DESCRIPTION_ANNO); - }, - - undoTransaction: function PSLIST_undoTransaction() { - const annos = this.utils.annotations; - - if (this._oldDescription) { - annos.setItemAnnotation(this.id, this.DESCRIPTION_ANNO, - this._oldDescription, 0, - this.nsIAnnotationService.EXPIRE_NEVER); - } - else if (annos.itemHasAnnotation(this.id, this.DESCRIPTION_ANNO)) - annos.removeItemAnnotation(this.id, this.DESCRIPTION_ANNO); - } -}; - -/** - * Edit a bookmark's keyword. - */ -function PlacesEditBookmarkKeywordTransaction(id, newKeyword) { - this.id = id; - this._newKeyword = newKeyword; - this._oldKeyword = ""; - this.redoTransaction = this.doTransaction; -} -PlacesEditBookmarkKeywordTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - doTransaction: function PEBKT_doTransaction() { - this._oldKeyword = this.bookmarks.getKeywordForBookmark(this.id); - this.bookmarks.setKeywordForBookmark(this.id, this._newKeyword); - }, - - undoTransaction: function PEBKT_undoTransaction() { - this.bookmarks.setKeywordForBookmark(this.id, this._oldKeyword); - } -}; - -/** - * Edit the post data associated with a URI - */ -function PlacesEditURIPostDataTransaction(aURI, aPostData) { - this._uri = aURI; - this._newPostData = aPostData; - this._oldPostData = null; - this.redoTransaction = this.doTransaction; -} -PlacesEditURIPostDataTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - doTransaction: function PEUPDT_doTransaction() { - this._oldPostData = this.utils.getPostDataForURI(this._uri); - this.utils.setPostDataForURI(this._uri, this._newPostData); - }, - - undoTransaction: function PEUPDT_undoTransaction() { - this.utils.setPostDataForURI(this._uri, this._oldPostData); - } -}; - -/** - * Edit a live bookmark's site URI. - */ -function PlacesEditLivemarkSiteURITransaction(folderId, uri) { - this._folderId = folderId; - this._newURI = uri; - this._oldURI = null; - this.redoTransaction = this.doTransaction; -} -PlacesEditLivemarkSiteURITransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - doTransaction: function PELSUT_doTransaction() { - this._oldURI = this.livemarks.getSiteURI(this._folderId); - this.livemarks.setSiteURI(this._folderId, this._newURI); - }, - - undoTransaction: function PELSUT_undoTransaction() { - this.livemarks.setSiteURI(this._folderId, this._oldURI); - } -}; - -/** - * Edit a live bookmark's feed URI. - */ -function PlacesEditLivemarkFeedURITransaction(folderId, uri) { - this._folderId = folderId; - this._newURI = uri; - this._oldURI = null; - this.redoTransaction = this.doTransaction; -} -PlacesEditLivemarkFeedURITransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - doTransaction: function PELFUT_doTransaction() { - this._oldURI = this.livemarks.getFeedURI(this._folderId); - this.livemarks.setFeedURI(this._folderId, this._newURI); - this.livemarks.reloadLivemarkFolder(this._folderId); - }, - - undoTransaction: function PELFUT_undoTransaction() { - this.livemarks.setFeedURI(this._folderId, this._oldURI); - this.livemarks.reloadLivemarkFolder(this._folderId); - } -}; - -/** - * Edit a bookmark's microsummary. - */ -function PlacesEditBookmarkMicrosummaryTransaction(aID, newMicrosummary) { - this.id = aID; - this._newMicrosummary = newMicrosummary; - this._oldMicrosummary = null; - this.redoTransaction = this.doTransaction; -} -PlacesEditBookmarkMicrosummaryTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - mss: PlacesUtils.microsummaries, - - doTransaction: function PEBMT_doTransaction() { - this._oldMicrosummary = this.mss.getMicrosummary(this.id); - if (this._newMicrosummary) - this.mss.setMicrosummary(this.id, this._newMicrosummary); - else - this.mss.removeMicrosummary(this.id); - }, - - undoTransaction: function PEBMT_undoTransaction() { - if (this._oldMicrosummary) - this.mss.setMicrosummary(this.id, this._oldMicrosummary); - else - this.mss.removeMicrosummary(this.id); - } -}; - -/** - * Sort a folder by name - */ -function PlacesSortFolderByNameTransaction(aFolderId, aFolderIndex) { - this._folderId = aFolderId; - this._folderIndex = aFolderIndex; - this._oldOrder = null, - this.redoTransaction = this.doTransaction; -} -PlacesSortFolderByNameTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - doTransaction: function PSSFBN_doTransaction() { - this._oldOrder = []; - - var contents = this.utils.getFolderContents(this._folderId, false, false).root; - var count = contents.childCount; - - // sort between separators - var newOrder = []; - var preSep = []; // temporary array for sorting each group of items - var sortingMethod = - function (a, b) { return a.title.localeCompare(b.title); }; - - for (var i = 0; i < count; ++i) { - var item = contents.getChild(i); - this._oldOrder[item.itemId] = i; - if (this.utils.nodeIsSeparator(item)) { - if (preSep.length > 0) { - preSep.sort(sortingMethod); - newOrder = newOrder.concat(preSep); - preSep.splice(0); - } - newOrder.push(item); - } - else - preSep.push(item); - } - if (preSep.length > 0) { - preSep.sort(sortingMethod); - newOrder = newOrder.concat(preSep); - } - - // set the nex indexs - for (var i = 0; i < count; ++i) { - this.bookmarks.setItemIndex(newOrder[i].itemId, i); - } - }, - - undoTransaction: function PSSFBN_undoTransaction() { - for (item in this._oldOrder) - this.bookmarks.setItemIndex(item, this._oldOrder[item]); - } -}; - -/** - * Set the bookmarks toolbar folder. - */ -function PlacesSetBookmarksToolbarTransaction(aFolderId) { - this._folderId = aFolderId; - this._oldFolderId = this.utils.toolbarFolder; - this.redoTransaction = this.doTransaction; -} -PlacesSetBookmarksToolbarTransaction.prototype = { - __proto__: PlacesBaseTransaction.prototype, - - doTransaction: function PSBTT_doTransaction() { - this.utils.bookmarks.toolbarFolder = this._folderId; - }, - - undoTransaction: function PSBTT_undoTransaction() { - this.utils.bookmarks.toolbarFolder = this._oldFolderId; + var txn = PlacesUtils.ptm.aggregateTransactions("DropItems", transactions); + PlacesUtils.ptm.commitTransaction(txn); } }; diff --git a/browser/components/places/content/moveBookmarks.js b/browser/components/places/content/moveBookmarks.js index 7d778da8772..04a05538b09 100644 --- a/browser/components/places/content/moveBookmarks.js +++ b/browser/components/places/content/moveBookmarks.js @@ -20,6 +20,7 @@ * * Contributor(s): * Asaf Romano + * Sungjoon Steve Won * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -72,11 +73,11 @@ var gMoveBookmarksDialog = { continue; transactions.push(new - PlacesMoveItemTransaction(this._nodes[i].itemId, selectedFolderID, -1)); + PlacesUtils.txn.moveItem(this._nodes[i].itemId, selectedFolderId, -1)); } if (transactions.length != 0) { - var txn = new PlacesAggregateTransaction("Move Items", transactions); + var txn = PlacesUtils.ptm.aggregateTransactions("Move Items", transactions); this._tm.doTransaction(txn); } }, diff --git a/browser/components/places/content/utils.js b/browser/components/places/content/utils.js index f2a71473f8a..f6e3e0259cf 100644 --- a/browser/components/places/content/utils.js +++ b/browser/components/places/content/utils.js @@ -22,6 +22,7 @@ * Ben Goodger * Myk Melez * Asaf Romano + * Sungjoon Steve Won * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -164,16 +165,17 @@ var PlacesUtils = { return this._localStore; }, - /** - * The Transaction Manager for this window. - */ - _tm: null, get tm() { - if (!this._tm) { - this._tm = Cc["@mozilla.org/transactionmanager;1"]. - createInstance(Ci.nsITransactionManager); + return this.ptm.transactionManager; + }, + + _ptm: null, + get ptm() { + if (!this._ptm) { + this._ptm = Cc["@mozilla.org/browser/placesTransactionsService;1"]. + getService(Components.interfaces.nsIPlacesTransactionsService); } - return this._tm; + return this._ptm; }, /** @@ -628,9 +630,8 @@ var PlacesUtils = { * @returns A nsITransaction object that performs the copy. */ _getURIItemCopyTransaction: function (aData, aContainer, aIndex) { - var itemURL = this._uri(aData.uri); - return new PlacesCreateItemTransaction(itemURL, aContainer, - aIndex, aData.title); + return this.ptm.createItem(this._uri(aData.uri), aContainer, aIndex, + aData.title, ""); }, /** @@ -661,10 +662,8 @@ var PlacesUtils = { }); } - var createTxn = - new PlacesCreateItemTransaction(itemURL, aContainer, aIndex, itemTitle, - keyword, annos); - return createTxn; + return this.ptm.createItem(itemURL, aContainer, aIndex, itemTitle, keyword, + annos); }, /** @@ -701,18 +700,18 @@ var PlacesUtils = { var annos = node.folder.annos; var folderItemsTransactions = getChildItemsTransactions(node.children); - txn = new PlacesCreateFolderTransaction(title, -1, index, annos, - folderItemsTransactions); + txn = this.ptm.createFolder(title, -1, aIndex, annos, + folderItemsTransactions); } else { // node is a livemark var feedURI = self._uri(node.uri.feed); var siteURI = self._uri(node.uri.site); - txn = new PlacesCreateLivemarkTransaction(feedURI, siteURI, - node.title, aContainer, index, node.annos); + txn = this.ptm.createLivemark(feedURI, siteURI, node.title, + aContainer, index, node.annos); } } else if (node.type == self.TYPE_X_MOZ_PLACE_SEPARATOR) - txn = new PlacesCreateSeparatorTransaction(-1, index); + txn = this.ptm.createSeparator(-1, aIndex); else if (node.type == self.TYPE_X_MOZ_PLACE) txn = self._getBookmarkItemCopyTransaction(node, -1, index); @@ -722,14 +721,12 @@ var PlacesUtils = { } return childItemsTransactions; } - + var title = aData.folder.title; var annos = aData.folder.annos; - var createTxn = - new PlacesCreateFolderTransaction(title, aContainer, aIndex, annos, - getChildItemsTransactions(aData.children)); - return createTxn; + return this.ptm.createFolder(title, aContainer, aIndex, annos, + getChildItemsTransactions(aData.children)); }, /** @@ -811,8 +808,8 @@ var PlacesUtils = { // Place is a Livemark Container, should be reinstantiated var feedURI = this._uri(data.uri.feed); var siteURI = this._uri(data.uri.site); - return new PlacesCreateLivemarkTransaction(feedURI, siteURI, - data.title, container, index, data.annos); + return this.ptm.createLivemark(feedURI, siteURI, data.title, container, + index, data.annos); } break; case this.TYPE_X_MOZ_PLACE: @@ -832,27 +829,23 @@ var PlacesUtils = { if (copy) { // There is no data in a separator, so copying it just amounts to // inserting a new separator. - return new PlacesCreateSeparatorTransaction(container, index); + return this.ptm.createSeparator(container, index); } break; default: if (type == this.TYPE_X_MOZ_URL || type == this.TYPE_UNICODE) { var title = (type == this.TYPE_X_MOZ_URL) ? data.title : data.uri; - var url = this._uri(data.uri); - var createTxn = - new PlacesCreateItemTransaction(url, container, index, title); - return createTxn; - } - else { - return null; + return this.ptm.createItem(this._uri(data.uri), container, index, + title); } + return null; } if (data.id <= 0) return null; // Move the item otherwise var id = data.folder ? data.folder.id : data.id; - return new PlacesMoveItemTransaction(id, container, index); + return this.ptm.moveItem(id, container, index); }, /** diff --git a/browser/components/places/public/Makefile.in b/browser/components/places/public/Makefile.in index 2b563e6bbe6..dc16061cbc6 100644 --- a/browser/components/places/public/Makefile.in +++ b/browser/components/places/public/Makefile.in @@ -46,6 +46,8 @@ include $(DEPTH)/config/autoconf.mk MODULE = browserplaces XPIDL_MODULE = browserplaces -XPIDLSRCS = nsIPlacesImportExportService.idl +XPIDLSRCS = nsIPlacesImportExportService.idl \ + nsIPlacesTransactionsService.idl \ + $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/browser/components/places/public/nsIPlacesTransactionsService.idl b/browser/components/places/public/nsIPlacesTransactionsService.idl new file mode 100644 index 00000000000..db16273dc50 --- /dev/null +++ b/browser/components/places/public/nsIPlacesTransactionsService.idl @@ -0,0 +1,323 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Places. + * + * The Initial Developer of the Original Code is Mozilla Corporation + * + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sungjoon Steve Won (Original Author) + * Asaf Romano + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" + +interface nsIVariant; +interface nsIURI; +interface nsIInputStream; +interface nsIMicrosummary; +interface nsITransactionManager; +interface nsITransaction; + +/** + * nsIPlacesTransactionService is a service designed to handle + * nsITransactions that correspond to changes in Places. It is here as a + * service so that we can keep the transactions around without holding onto + * the global scope of a js window. + */ + +[scriptable, uuid(224f6833-762b-43ba-8911-020b589b2da3)] +interface nsIPlacesTransactionsService : nsISupports +{ + /** + * Performs a transaction. + * + * @param aTransaction + * a transaction object for a transaction + */ + void commitTransaction(in nsITransaction aTransaction); + + /** + * Transaction for performing several Places Transactions in a single batch. + * + * @param aName + * title of the aggregate transactions + * @param aTransactions + * an array of transactions to perform + * @returns nsITransaction object + */ + nsITransaction aggregateTransactions(in AString aName, + in nsIVariant aTransactions); + + /** + * Transaction for creating a new folder item. + * + * @param aName + * the name of the new folder + * @param aContainer + * the identifier of the folder in which the new folder should be + * added. + * @param [optional] aIndex + * the index of the item in aContainer, pass -1 or nothing to create + * the item at the end of aContainer. + * @param [optional] aAnnotations + * the annotations to set for the new folder. + * @param [optional] aChildItemsTransactions + * array of transactions for items to be created under the new folder. + * @returns nsITransaction object + */ + nsITransaction createFolder(in AString aName, in long long aContainer, + [optional] in long long aIndex, + [optional] in nsIVariant aAnnotations, + [optional] in nsIVariant aChildItemsTransactions); + + /** + * Transaction for creating a new bookmark item + * + * @param aURI + * the uri of the new bookmark (nsIURI) + * @param aContainer + * the identifier of the folder in which the bookmark should be added. + * @param [optional] aIndex + * the index of the item in aContainer, pass -1 or nothing to create + * the item at the end of aContainer. + * @param [optional] aTitle + * the title of the new bookmark. + * @param [optional] aKeyword + * the keyword of the new bookmark. + * @param [optional] aAnnotations + * the annotations to set for the new bookmark. + * @param [optional] aChildTransactions + * child transactions to commit after creating the bookmark. Prefer + * using any of the arguments above if possible. In general, a child + * transations should be used only if the change it does has to be + * reverted manually when removing the bookmark item. + * a child transaction must support setting its bookmark-item + * identifier via an "id" js setter. + * @returns nsITransaction object + */ + nsITransaction createItem(in nsIURI aURI, in long long aContainer, + [optional] in long long aIndex, + [optional] in AString aTitle, + [optional] in AString aKeyword, + [optional] in nsIVariant aAnnotations, + [optional] in nsIVariant aChildTransactions); + + /** + * Transaction for creating a new separator item + * + * @param aContainer + * the identifier of the folder in which the separator should be + * added. + * @param [optional] aIndex + * the index of the item in aContainer, pass -1 or nothing to create + * the separator at the end of aContainer. + * @returns nsITransaction object + */ + nsITransaction createSeparator(in long long aContainer, + [optional] in long long aIndex); + + /** + * Transaction for creating a new live-bookmark item. + * + * @see nsILivemarksService::createLivemark for documentation regarding the + * first three arguments. + * + * @param aContainer + * the identifier of the folder in which the live-bookmark should be + * added. + * @param [optional] aIndex + * the index of the item in aContainer, pass -1 or nothing to create + * the item at the end of aContainer. + * @param [optional] aAnnotations + * the annotations to set for the new live-bookmark. + * @returns nsITransaction object + */ + nsITransaction createLivemark(in nsIURI aFeedURI, + in nsIURI aSiteURI, + in AString aName, + in long long aContainer, + [optional] in long long aIndex, + [optional] in nsIVariant aAnnotations); + + /** + * Transaction for moving an Item. + * + * @param aItemId + * the id of the item to move + * @param aNewContainer + * id of the new container to move to + * @param aNewIndex + * index of the new position to move to + * @returns nsITransaction object + */ + nsITransaction moveItem(in long long aItemId, + in long long aNewContainer, + in long long aNewIndex); + + /** + * Transaction for removing an Item + * + * @param aItemId + * id of the item to remove + * @returns nsITransaction object + */ + nsITransaction removeItem(in long long aItemId); + + /** + * Transaction for editting a bookmark's title. + * + * @param id + * id of the item to edit + * @param newTitle + * new title for the item to edit + * @returns nsITransaction object + */ + nsITransaction editItemTitle(in long long id, in AString newTitle); + + /** + * Transaction for editting a bookmark's uri. + * + * @param aBookmarkId + * id of the bookmark to edit + * @param aNewURI + * new uri for the bookmark + * @returns nsITransaction object + */ + nsITransaction editBookmarkURI(in long long aBookmarkId, in nsIURI aNewURI); + + /** + * Transaction for setting/unsetting Load-in-sidebar annotation + * + * @param aBookmarkId + * id of the selected bookmark + * @param aLoadInSidebar + * boolean value + * @returns nsITransaction object + */ + nsITransaction setLoadInSidebar(in long long aBookmarkId, + in boolean aLoadInSidebar); + + /** + * Transaction for editting a the description of a bookmark or a folder + * + * @param aItemId + * id of the item to edit + * @param aDescription + * new description + * @returns nsITransaction object + */ + nsITransaction editItemDescription(in long long aItemId, + in AString aDescription); + + /** + * Transaction for editting a bookmark's keyword. + * + * @param id + * id of the bookmark to edit + * @param newKeyword + * new keyword for the bookmark + * @returns nsITransaction object + */ + nsITransaction editBookmarkKeyword(in long long id, + in AString newKeyword); + + /** + * Transaction for editting the post data associated with a URI + * + * @param aURI + * uri to edit + * @param aPostData + * post data + * @returns nsITransaction object + */ + nsITransaction editURIPostData(in nsIURI aURI, + in nsIInputStream aPostData); + + /** + * Transaction for editting a live bookmark's site URI. + * + * @param aFolderId + * id of the livemark + * @param aURI + * new site uri + * @returns nsITransaction object + */ + nsITransaction editLivemarkSiteURI(in long long aFolderId, in nsIURI aURI); + + /** + * Transaction for editting a live bookmark's feed URI. + * + * @param folderId + * id of the livemark + * @param uri + * new feed uri + * @returns nsITransaction object + */ + nsITransaction editLivemarkFeedURI(in long long folderId, in nsIURI uri); + + /** + * Transaction for editting a bookmark's microsummary. + * + * @param aItemId + * id of the bookmark to edit + * @param aNewMicrosummary + * new microsummary for the bookmark + * @returns nsITransaction object + */ + nsITransaction editBookmarkMicrosummary(in long long aItemId, + in nsIMicrosummary aNewMicrosummary); + + /** + * Transaction for sorting a folder by name + * + * @param aFolderId + * id of the folder to sort + * @oaram aFolderIndex + * index of the folder to sort + * @returns nsITransaction object + */ + nsITransaction sortFolderByName(in long long aFolderId, + in long long aFolderIndex); + + /** + * Transaction for set the bookmarks toolbar folder. + * + * @param aFolderId + * id of the folder to set as toolbar folder + * @returns nsITransaction object + */ + nsITransaction setBookmarksToolbar(in long long aFolderId); + + /** + * A reference to the transaction manager + */ + readonly attribute nsITransactionManager transactionManager; +}; diff --git a/browser/components/places/src/Makefile.in b/browser/components/places/src/Makefile.in index 73d08db2320..850c1801136 100644 --- a/browser/components/places/src/Makefile.in +++ b/browser/components/places/src/Makefile.in @@ -55,15 +55,17 @@ REQUIRES = \ string \ docshell \ necko \ - browsercomps \ - toolkitcomps \ + browsercomps \ + toolkitcomps \ unicharutil \ - htmlparser \ - content \ - places \ - microsummaries \ + htmlparser \ + content \ + places \ + microsummaries \ $(NULL) CPPSRCS = nsPlacesImportExportService.cpp +EXTRA_COMPONENTS = nsPlacesTransactionsService.js + include $(topsrcdir)/config/rules.mk diff --git a/browser/components/places/src/nsPlacesTransactionsService.js b/browser/components/places/src/nsPlacesTransactionsService.js new file mode 100644 index 00000000000..ad83ad19a08 --- /dev/null +++ b/browser/components/places/src/nsPlacesTransactionsService.js @@ -0,0 +1,770 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Places Command Controller. + * + * The Initial Developer of the Original Code is Google Inc. + * + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sungjoon Steve Won (Original Author) + * Asaf Romano + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +const Cu = Components.utils; +const loadInSidebarAnno = "bookmarkProperties/loadInSidebar"; +const descriptionAnno = "bookmarkProperties/description"; +const CLASS_ID = Components.ID("bec866cc-9dd0-42a0-a196-6fdaa16021c4"); +const CONTRACT_ID = "@mozilla.org/browser/placesTransactionsService;1"; + +var toolbarFolder = null; +var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]. + getService(Components.interfaces.mozIJSSubScriptLoader); +loader.loadSubScript("chrome://global/content/debug.js"); +loader.loadSubScript("chrome://browser/content/places/utils.js"); + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +function LOG(str) { + dump("*** " + str + "\n"); +}; + +// The minimum amount of transactions we should tell our observers to begin +// batching (rather than letting them do incremental drawing). +const MIN_TRANSACTIONS_FOR_BATCH = 5; + +function placesTransactionsService() { + this.mTransactionManager = Cc["@mozilla.org/transactionmanager;1"]. + createInstance(Ci.nsITransactionManager); +} + +placesTransactionsService.prototype = { + classDescription: "Places Transaction Manager", + classID: CLASS_ID, + contractID: CONTRACT_ID, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIPlacesTransactionsService, + Ci.nsISupports]), + + aggregateTransactions: function placesAggrTransactions(name, transactions) { + return new placesAggregateTransactions(name, transactions); + }, + + createFolder: function placesCrtFldr(aName, aContainer, aIndex, + aAnnotations, aChildItemsTransactions) { + return new placesCreateFolderTransactions(aName, aContainer, aIndex, + aAnnotations, aChildItemsTransactions); + }, + + createItem: function placesCrtItem(aURI, aContainer, aIndex, aTitle, + aKeyword, aAnnotations, aChildTransactions) { + return new placesCreateItemTransactions(aURI, aContainer, aIndex, aTitle, + aKeyword, aAnnotations, aChildTransactions); + }, + + createSeparator: function placesCrtSpr(aContainer, aIndex) { + return new placesCreateSeparatorTransactions(aContainer, aIndex); + }, + + createLivemark: function placesCrtLivemark(aFeedURI, aSiteURI, aName, + aContainer, aIndex, aAnnotations) { + return new placesCreateLivemarkTransactions(aFeedURI, aSiteURI, aName, + aContainer, aIndex, aAnnotations); + }, + + moveItem: function placesMvItem(aItemId, aNewContainer, aNewIndex) { + return new placesMoveItemTransactions(aItemId, aNewContainer, aNewIndex); + }, + + removeItem: function placesRmItem(id) { + return new placesRemoveItemTransaction(id); + }, + + editItemTitle: function placesEditItmTitle(id, newTitle) { + return new placesEditItemTitleTransactions(id, newTitle); + }, + + editBookmarkURI: function placesEditBkmkURI(aBookmarkId, aNewURI) { + return new placesEditBookmarkURITransactions(aBookmarkId, aNewURI); + }, + + setLoadInSidebar: function placesSetLdInSdbar(aBookmarkId, aLoadInSidebar) { + return new placesSetLoadInSidebarTransactions(aBookmarkId, aLoadInSidebar); + }, + + editItemDescription: function placesEditItmDesc(aItemId, aDescription) { + return new placesEditItemDescriptionTransactions(aItemId, aDescription); + }, + + editBookmarkKeyword: function placesEditBkmkKwd(id, newKeyword) { + return new placesEditBookmarkKeywordTransactions(id, newKeyword); + }, + + editURIPostData: function placesEditURIPdata(aURI, aPostData) { + return new placesEditURIPostDataTransactions(aURI, aPostData); + }, + + editLivemarkSiteURI: function placesEditLvmkSiteURI(folderId, uri) { + return new placesEditLivemarkSiteURITransactions(folderId, uri); + }, + + editLivemarkFeedURI: function placesEditLvmkFeedURI(folderId, uri) { + return new placesEditLivemarkFeedURITransactions(folderId, uri); + }, + + editBookmarkMicrosummary: function placesEditBkmkMicrosummary(aID, newMicrosummary) { + return new placesEditBookmarkMicrosummaryTransactions(aID, newMicrosummary); + }, + + sortFolderByName: function placesSortFldrByName(aFolderId, aFolderIndex) { + return new placesSortFolderByNameTransactions(aFolderId, aFolderIndex); + }, + + setBookmarksToolbar: function placesSetBkmkToolbar(aFolderId) { + return new placesSetBookmarksToolbarTransactions(aFolderId); + }, + + commitTransaction: function placesCommitTxn(txn) { + this.mTransactionManager.doTransaction(txn); + }, + + get transactionManager() { + return this.mTransactionManager; + } +}; + +/** + * Method and utility stubs for Places Edit Transactions + */ +function placesBaseTransaction() { +} + +placesBaseTransaction.prototype = { + redoTransaction: function PIT_redoTransaction() { + throw Cr.NS_ERROR_NOT_IMPLEMENTED; + }, + + get isTransient() { + return false; + }, + + merge: function mergeFunc(transaction) { + return false; + }, + + _ifaces: [Ci.nsITransaction, Ci.nsIClassInfo, Ci.nsISupports], + + // nsIClassInfo, allows setting expando properties on transactions + flags: Ci.nsIClassInfo.DOM_OBJECT, + classDescription: "Places Transaction", + getInterface: function(aCount) { + aCount.value = this._ifaces.length; + return this._ifaces; + }, + + QueryInterface: XPCOMUtils.generateQI(this._ifaces), +}; + +function placesAggregateTransactions(name, transactions) { + this._transactions = transactions; + this._name = name; + this.container = -1; + this.redoTransaction = this.doTransaction; +} + +placesAggregateTransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + + doTransaction: function PAT_doTransaction() { + if (this._transactions.length >= MIN_TRANSACTIONS_FOR_BATCH) { + var callback = { + _self: this, + runBatched: function() { + this._self.commit(false); + } + }; + PlacesUtils.bookmarks.runInBatchMode(callback, null); + } + else + this.commit(false); + }, + + undoTransaction: function PAT_undoTransaction() { + if (this._transactions.length >= MIN_TRANSACTIONS_FOR_BATCH) { + var callback = { + _self: this, + runBatched: function() { + this._self.commit(true); + } + }; + PlacesUtils.bookmarks.runInBatchMode(callback, null); + } + else + this.commit(true); + }, + + commit: function PAT_commit(aUndo) { + for (var i = this._transactions.length - 1; i >= 0; --i) { + var txn = this._transactions[i]; + if (this.container > -1) + txn.container = this.container; + if (aUndo) + txn.undoTransaction(); + else + txn.doTransaction(); + } + } +}; + +function placesCreateFolderTransactions(aName, aContainer, aIndex, + aAnnotations, + aChildItemsTransactions) { + this._name = aName; + this._container = aContainer; + this._index = typeof(aIndex) == "number" ? aIndex : -1; + this._annotations = aAnnotations; + this._id = null; + this._childItemsTransactions = aChildItemsTransactions || []; + this.redoTransaction = this.doTransaction; +} + +placesCreateFolderTransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + + // childItemsTransaction support + get container() { return this._container; }, + set container(val) { return this._container = val; }, + + doTransaction: function PCFT_doTransaction() { + this._id = PlacesUtils.bookmarks.createFolder(this._container, + this._name, this._index); + if ((this._annotations != null) && (this._annotations.length > 0)) + PlacesUtils.setAnnotationsForItem(this.id, this._annotations); + + for (var i = 0; i < this._childItemsTransactions.length; ++i) { + var txn = this._childItemsTransactions[i]; + txn.container = this._id; + txn.doTransaction(); + } + }, + + undoTransaction: function PCFT_undoTransaction() { + PlacesUtils.bookmarks.removeFolder(this._id); + for (var i = 0; i < this._childItemsTransactions.length; ++i) { + var txn = this.childItemsTransactions[i]; + txn.undoTransaction(); + } + } +}; + +function placesCreateItemTransactions(aURI, aContainer, aIndex, aTitle, + aKeyword, aAnnotations, + aChildTransactions) { + this._uri = aURI; + this._container = aContainer; + this._index = typeof(aIndex) == "number" ? aIndex : -1; + this._title = aTitle; + this._keyword = aKeyword; + this._annotations = aAnnotations; + this._childTransactions = aChildTransactions || []; + this.redoTransaction = this.doTransaction; +} + +placesCreateItemTransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + + // childItemsTransactions support for the create-folder transaction + get container() { return this._container; }, + set container(val) { return this._container = val; }, + + doTransaction: function PCIT_doTransaction() { + this._id = PlacesUtils.bookmarks.insertBookmark(this.container, this._uri, + this._index, this._title); + if (this._keyword) + PlacesUtils.bookmarks.setKeywordForBookmark(this._id, this._keyword); + if (this._annotations && this._annotations.length > 0) + PlacesUtils.setAnnotationsForItem(this._id, this._annotations); + + for (var i = 0; i < this._childTransactions.length; ++i) { + var txn = this._childTransactions[i]; + txn.id = this._id; + txn.doTransaction(); + } + }, + + undoTransaction: function PCIT_undoTransaction() { + PlacesUtils.bookmarks.removeItem(this._id); + for (var i = 0; i < this._childTransactions.length; ++i) { + var txn = this._childTransactions[i]; + txn.undoTransaction(); + } + } +}; + +function placesCreateSeparatorTransactions(aContainer, aIndex) { + this._container = aContainer; + this._index = typeof(aIndex) == "number" ? aIndex : -1; + this._id = null; +} + +placesCreateSeparatorTransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + + // childItemsTransaction support + get container() { return this._container; }, + set container(val) { return this._container = val;clear }, + + doTransaction: function PCST_doTransaction() { + this._id = PlacesUtils.bookmarks + .insertSeparator(this.container, this._index); + }, + + undoTransaction: function PCST_undoTransaction() { + PlacesUtils.bookmarks.removeChildAt(this.container, this._index); + } +}; + +function placesCreateLivemarkTransactions(aFeedURI, aSiteURI, aName, + aContainer, aIndex, + aAnnotations) { + this._feedURI = aFeedURI; + this._siteURI = aSiteURI; + this._name = aName; + this._container = aContainer; + this._index = typeof(aIndex) == "number" ? aIndex : -1; + this._annotations = aAnnotations; +} + +placesCreateLivemarkTransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + + // childItemsTransaction support + get container() { return this._container; }, + set container(val) { return this._container = val; }, + + doTransaction: function PCLT_doTransaction() { + this._id = PlacesUtils.livemarks.createLivemark(this._container, this._name, + this._siteURI, this._feedURI, + this._index); + if (this._annotations) + PlacesUtils.setAnnotationsForItem(this._id, this._annotations); + }, + + undoTransaction: function PCLT_undoTransaction() { + PlacesUtils.bookmarks.removeFolder(this._id); + } +}; + +function placesMoveItemTransactions(aItemId, aNewContainer, aNewIndex) { + NS_ASSERT(aNewIndex >= -1, "invalid insertion index"); + this._id = aItemId; + this._oldContainer = PlacesUtils.bookmarks.getFolderIdForItem(this._id); + this._oldIndex = PlacesUtils.bookmarks.getItemIndex(this._id); + NS_ASSERT(this._oldContainer > 0 && this._oldIndex >= 0, "invalid item"); + this._newContainer = aNewContainer; + this._newIndex = aNewIndex; + this.redoTransaction = this.doTransaction; +} + +placesMoveItemTransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + + doTransaction: function PMIT_doTransaction() { + PlacesUtils.bookmarks.moveItem(this._id, this._newContainer, this._newIndex); + }, + + undoTransaction: function PMIT_undoTransaction() { + PlacesUtils.bookmarks.moveItem(this._id, this._oldContainer, this._oldIndex); + } +}; + +function placesRemoveItemTransaction(aItemId) { + this.redoTransaction = this.doTransaction; + this._id = aItemId; + this._itemType = PlacesUtils.bookmarks.getItemType(this._id); + if (this._itemType == Ci.nsINavBookmarksService.TYPE_FOLDER) { + this._transactions = []; + this._removeTxn = PlacesUtils.bookmarks + .getRemoveFolderTransaction(this._id); + } +} + +placesRemoveItemTransaction.prototype = { + __proto__: placesBaseTransaction.prototype, + + doTransaction: function PRIT_doTransaction() { + this._oldContainer = PlacesUtils.bookmarks.getFolderIdForItem(this._id); + this._oldIndex = PlacesUtils.bookmarks.getItemIndex(this._id); + this._title = PlacesUtils.bookmarks.getItemTitle(this._id); + this._annotations = PlacesUtils.getAnnotationsForItem(this._id); + + if (this._itemType == Ci.nsINavBookmarksService.TYPE_FOLDER) { + this._saveFolderContents(); + + // Remove children backwards to preserve parent-child relationships. + for (var i = this._transactions.length - 1; i >= 0; --i) + this._transactions[i].doTransaction(); + + // Remove this folder itself. + this._removeTxn.doTransaction(); + } + else { + if (this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK) + this._uri = PlacesUtils.bookmarks.getBookmarkURI(this._id); + PlacesUtils.bookmarks.removeItem(this._id); + } + }, + + undoTransaction: function PRIT_undoTransaction() { + if (this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK) { + this._id = PlacesUtils.bookmarks.insertBookmark(this._oldContainer, + this._uri, + this._oldIndex, + this._title); + } + else if (this._itemType == Ci.nsINavBookmarksService.TYPE_FOLDER) { + this._removeTxn.undoTransaction(); + // Create children forwards to preserve parent-child relationships. + for (var i = 0; i < this._transactions.length; ++i) + this._transactions[i].undoTransaction(); + } + else // TYPE_SEPARATOR + PlacesUtils.bookmarks.insertSeparator(this._oldContainer, this._oldIndex); + + PlacesUtils.setAnnotationsForItem(this._id, this._annotations); + }, + + /** + * Create a flat, ordered list of transactions for a depth-first recreation + * of items within this folder. + */ + _saveFolderContents: function PRIT__saveFolderContents() { + this._transactions = []; + var contents = PlacesUtils.getFolderContents(this._id, false, false).root; + for (var i = 0; i < contents.childCount; ++i) { + this._transactions + .push(new placesRemoveItemTransaction(contents.getChild(i).itemId)); + } + } +}; + +function placesEditItemTitleTransactions(id, newTitle) { + this._id = id; + this._newTitle = newTitle; + this._oldTitle = ""; + this.redoTransaction = this.doTransaction; +} + +placesEditItemTitleTransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + + doTransaction: function PEITT_doTransaction() { + this._oldTitle = PlacesUtils.bookmarks.getItemTitle(this._id); + PlacesUtils.bookmarks.setItemTitle(this._id, this._newTitle); + }, + + undoTransaction: function PEITT_undoTransaction() { + PlacesUtils.bookmarks.setItemTitle(this._id, this._oldTitle); + } +}; + +function placesEditBookmarkURITransactions(aBookmarkId, aNewURI) { + this._id = aBookmarkId; + this._newURI = aNewURI; + this.redoTransaction = this.doTransaction; +} + +placesEditBookmarkURITransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + + doTransaction: function PEBUT_doTransaction() { + this._oldURI = PlacesUtils.bookmarks.getBookmarkURI(this._id); + PlacesUtils.bookmarks.changeBookmarkURI(this._id, this._newURI); + }, + + undoTransaction: function PEBUT_undoTransaction() { + PlacesUtils.bookmarks.changeBookmarkURI(this._id, this._oldURI); + } +}; + +function placesSetLoadInSidebarTransactions(aBookmarkId, aLoadInSidebar) { + this.id = aBookmarkId; + this._loadInSidebar = aLoadInSidebar; + this.redoTransaction = this.doTransaction; +} + +placesSetLoadInSidebarTransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + + _anno: { + name: loadInSidebarAnno, + type: Ci.nsIAnnotationService.TYPE_INT32, + value: 1, + flags: 0, + expires: Ci.nsIAnnotationService.EXPIRE_NEVER + }, + + doTransaction: function PSLIST_doTransaction() { + this._wasSet = PlacesUtils.annotations.itemHasAnnotation(this.id, this._anno.name); + if (this._loadInSidebar) { + PlacesUtils.setAnnotationsForItem(this.id, [this._anno]); + } + else { + try { + PlacesUtils.annotations.removeItemAnnotation(this.id, this._anno.name); + } catch(ex) { } + } + }, + + undoTransaction: function PSLIST_undoTransaction() { + if (this._wasSet != this._loadInSidebar) { + this._loadInSidebar = !this._loadInSidebar; + this.doTransaction(); + } + } +}; + +function placesEditItemDescriptionTransactions(aItemId, aDescription) { + this.id = aItemId; + this._newDescription = aDescription; + this.redoTransaction = this.doTransaction; +} + +placesEditItemDescriptionTransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + + _oldDescription: "", + + doTransaction: function PSLIST_doTransaction() { + const annos = PlacesUtils.annotations; + if (annos.itemHasAnnotation(this.id, descriptionAnno)) + this._oldDescription = annos.getItemAnnotation(this.id, descriptionAnno); + + if (this._newDescription) { + annos.setItemAnnotation(this.id, descriptionAnno, + this._newDescription, 0, + annos.EXPIRE_NEVER); + } + else if (this._oldDescription) + annos.removeItemAnnotation(this.id, descriptionAnno); + }, + + undoTransaction: function PSLIST_undoTransaction() { + const annos = PlacesUtils.annotations; + if (this._oldDescription) { + annos.setItemAnnotationString(this.id, descriptionAnno, + this._oldDescription, 0, + annos.EXPIRE_NEVER); + } + else if (annos.itemHasAnnotation(this.id, descriptionAnno)) + annos.removeItemAnnotation(this.id, descriptionAnno); + } +}; + +function placesEditBookmarkKeywordTransactions(id, newKeyword) { + this.id = id; + this._newKeyword = newKeyword; + this._oldKeyword = ""; + this.redoTransaction = this.doTransaction; +} + +placesEditBookmarkKeywordTransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + + doTransaction: function PEBKT_doTransaction() { + this._oldKeyword = PlacesUtils.bookmarks.getKeywordForBookmark(this.id); + PlacesUtils.bookmarks.setKeywordForBookmark(this.id, this._newKeyword); + }, + + undoTransaction: function PEBKT_undoTransaction() { + PlacesUtils.bookmarks.setKeywordForBookmark(this.id, this._oldKeyword); + } +}; + +function placesEditURIPostDataTransactions(aURI, aPostData) { + this._uri = aURI; + this._newPostData = aPostData; + this._oldPostData = null; + this.redoTransaction = this.doTransaction; +} + +placesEditURIPostDataTransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + + doTransaction: function PEUPDT_doTransaction() { + this._oldPostData = PlacesUtils.getPostDataForURI(this._uri); + PlacesUtils.setPostDataForURI(this._uri, this._newPostData); + }, + + undoTransaction: function PEUPDT_undoTransaction() { + PlacesUtils.setPostDataForURI(this._uri, this._oldPostData); + } +}; + +function placesEditLivemarkSiteURITransactions(folderId, uri) { + this._folderId = folderId; + this._newURI = uri; + this._oldURI = null; + this.redoTransaction = this.doTransaction; +} + +placesEditLivemarkSiteURITransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + + doTransaction: function PELSUT_doTransaction() { + this._oldURI = PlacesUtils.livemarks.getSiteURI(this._folderId); + PlacesUtils.livemarks.setSiteURI(this._folderId, this._newURI); + }, + + undoTransaction: function PELSUT_undoTransaction() { + PlacesUtils.livemarks.setSiteURI(this._folderId, this._oldURI); + } +}; + +function placesEditLivemarkFeedURITransactions(folderId, uri) { + this._folderId = folderId; + this._newURI = uri; + this._oldURI = null; + this.redoTransaction = this.doTransaction; +} + +placesEditLivemarkFeedURITransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + + doTransaction: function PELFUT_doTransaction() { + this._oldURI = PlacesUtils.livemarks.getFeedURI(this._folderId); + PlacesUtils.livemarks.setFeedURI(this._folderId, this._newURI); + PlacesUtils.livemarks.reloadLivemarkFolder(this._folderId); + }, + + undoTransaction: function PELFUT_undoTransaction() { + PlacesUtils.livemarks.setFeedURI(this._folderId, this._oldURI); + PlacesUtils.livemarks.reloadLivemarkFolder(this._folderId); + } +}; + +function placesEditBookmarkMicrosummaryTransactions(aID, newMicrosummary) { + this.id = aID; + this._mss = Cc["@mozilla.org/microsummary/service;1"]. + getService(Ci.nsIMicrosummaryService); + this._newMicrosummary = newMicrosummary; + this._oldMicrosummary = null; + this.redoTransaction = this.doTransaction; +} + +placesEditBookmarkMicrosummaryTransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + + doTransaction: function PEBMT_doTransaction() { + this._oldMicrosummary = this._mss.getMicrosummary(this.id); + if (this._newMicrosummary) + this._mss.setMicrosummary(this.id, this._newMicrosummary); + else + this._mss.removeMicrosummary(this.id); + }, + + undoTransaction: function PEBMT_undoTransaction() { + if (this._oldMicrosummary) + this._mss.setMicrosummary(this.id, this._oldMicrosummary); + else + this._mss.removeMicrosummary(this.id); + } +}; + +function placesSortFolderByNameTransactions(aFolderId, aFolderIndex) { + this._folderId = aFolderId; + this._folderIndex = aFolderIndex; + this._oldOrder = null, + this.redoTransaction = this.doTransaction; +} + +placesSortFolderByNameTransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + + doTransaction: function PSSFBN_doTransaction() { + this._oldOrder = []; + + var contents = PlacesUtils.getFolderContents(this._folderId, false, false).root; + var count = contents.childCount; + + // sort between separators + var newOrder = []; + var preSep = []; // temporary array for sorting each group of items + var sortingMethod = + function (a, b) { return a.title.localeCompare(b.title); }; + + for (var i = 0; i < count; ++i) { + var item = contents.getChild(i); + this._oldOrder[item.itemId] = i; + if (PlacesUtils.nodeIsSeparator(item)) { + if (preSep.length > 0) { + preSep.sort(sortingMethod); + newOrder = newOrder.concat(preSep); + preSep.splice(0); + } + newOrder.push(item); + } + else + preSep.push(item); + } + if (preSep.length > 0) { + preSep.sort(sortingMethod); + newOrder = newOrder.concat(preSep); + } + + // set the nex indexs + for (var i = 0; i < count; ++i) + PlacesUtils.bookmarks.setItemIndex(newOrder[i].itemId, i); + }, + + undoTransaction: function PSSFBN_undoTransaction() { + for (item in this._oldOrder) + PlacesUtils.bookmarks.setItemIndex(item, this._oldOrder[item]); + } +}; + +function placesSetBookmarksToolbarTransactions(aFolderId) { + this._folderId = aFolderId; + this.redoTransaction = this.doTransaction; + this._oldFolderId = PlacesUtils.bookmarks.toolbarFolder; +} + +placesSetBookmarksToolbarTransactions.prototype = { + __proto__: placesBaseTransaction.prototype, + doTransaction: function PSBTT_doTransaction() { + PlacesUtils.bookmarks.toolbarFolder = this._folderId; + }, + + undoTransaction: function PSBTT_undoTransaction() { + PlacesUtils.bookmarks.toolbarFolder = this._oldFolderId; + } +}; + +function NSGetModule(aCompMgr, aFileSpec) { + return XPCOMUtils.generateModule([placesTransactionsService]); +} diff --git a/browser/components/places/tests/unit/test_placesTxn.js b/browser/components/places/tests/unit/test_placesTxn.js new file mode 100644 index 00000000000..e131d2b6955 --- /dev/null +++ b/browser/components/places/tests/unit/test_placesTxn.js @@ -0,0 +1,384 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is example Inc. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sungjoon Steve Won (Original Author) + * Asaf Romano + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// Get bookmark service +try { + var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService); +} catch(ex) { + do_throw("Could not get nav-bookmarks-service\n"); +} + +// Get livemark service +try { + var lmsvc = Cc["@mozilla.org/browser/livemark-service;2"].getService(Ci.nsILivemarkService); +} catch(ex) { + do_throw("Could not get livemark-service\n"); +} + +// Get microsummary service +try { + var mss = Cc["@mozilla.org/microsummary/service;1"].getService(Ci.nsIMicrosummaryService); +} catch(ex) { + do_throw("Could not get microsummary-service\n"); +} + +// Get Places Transaction Manager Service +try { + var ptSvc = + Cc["@mozilla.org/browser/placesTransactionsService;1"]. + getService(Ci.nsIPlacesTransactionsService); +} catch(ex) { + do_throw("Could not get Places Transactions Service\n"); +} + +// create and add bookmarks observer +var observer = { + onBeginUpdateBatch: function() { + this._beginUpdateBatch = true; + }, + onEndUpdateBatch: function() { + this._endUpdateBatch = true; + }, + onItemAdded: function(id, folder, index) { + this._itemAddedId = id; + this._itemAddedParent = folder; + this._itemAddedIndex = index; + }, + onItemRemoved: function(id, folder, index) { + this._itemRemovedId = id; + this._itemRemovedFolder = folder; + this._itemRemovedIndex = index; + }, + onItemChanged: function(id, property, isAnnotationProperty, value) { + this._itemChangedId = id; + this._itemChangedProperty = property; + this._itemChanged_isAnnotationProperty = isAnnotationProperty; + this._itemChangedValue = value; + }, + onItemVisited: function(id, visitID, time) { + this._itemVisitedId = id; + this._itemVisitedVistId = visitID; + this._itemVisitedTime = time; + }, + onItemMoved: function(id, oldParent, oldIndex, newParent, newIndex) { + this._itemMovedId = id + this._itemMovedOldParent = oldParent; + this._itemMovedOldIndex = oldIndex; + this._itemMovedNewParent = newParent; + this._itemMovedNewIndex = newIndex; + }, + QueryInterface: function(iid) { + if (iid.equals(Ci.nsINavBookmarkObserver) || + iid.equals(Ci.nsISupports)) { + return this; + } + throw Cr.NS_ERROR_NO_INTERFACE; + }, +}; +bmsvc.addObserver(observer, false); + +// get bookmarks root index +var root = bmsvc.bookmarksRoot; + +// index at which items should begin +var bmStartIndex = 1; + +// main +function run_test() { + + //Test creating a folder + var txn1 = ptSvc.createFolder("Testing folder", root, bmStartIndex); + txn1.doTransaction(); + var folderId = bmsvc.getChildFolder(root, "Testing folder"); + do_check_eq(observer._itemAddedIndex, bmStartIndex); + do_check_eq(observer._itemAddedParent, root); + do_check_eq(observer._itemAddedId, folderId); + txn1.undoTransaction(); + do_check_eq(observer._itemRemovedId, folderId); + do_check_eq(observer._itemRemovedFolder, root); + do_check_eq(observer._itemRemovedIndex, bmStartIndex); + + // Test creating an item + // Create to Root + var txn2 = ptSvc.createItem(uri("http://www.example.com"), root, bmStartIndex, "Testing1"); + ptSvc.commitTransaction(txn2); //Also testing commitTransaction + var b = (bmsvc.getBookmarkIdsForURI(uri("http://www.example.com"), {}))[0]; + do_check_eq(observer._itemAddedId, b); + do_check_eq(observer._itemAddedIndex, bmStartIndex); + do_check_true(bmsvc.isBookmarked(uri("http://www.example.com"))); + txn2.undoTransaction(); + do_check_eq(observer._itemRemovedId, b); + do_check_eq(observer._itemRemovedIndex, bmStartIndex); + + // Create to a folder + var txn2a = ptSvc.createFolder("Folder", root, bmStartIndex); + var fldrId = bmsvc.getChildFolder(root, "Folder"); + var txn2b = ptSvc.createItem(uri("http://www.example.com"), fldrId, bmStartIndex, "Testing1b"); + ptSvc.commitTransaction(txn2); + var b2 = (bmsvc.getBookmarkIdsForURI(uri("http://www.example.com"), {}))[0]; + do_check_eq(observer._itemAddedId, b2); + do_check_eq(observer._itemAddedIndex, bmStartIndex); + do_check_true(bmsvc.isBookmarked(uri("http://www.example.com"))); + txn2.undoTransaction(); + do_check_eq(observer._itemRemovedId, b2); + do_check_eq(observer._itemRemovedIndex, bmStartIndex); + + // Testing moving an item + ptSvc.commitTransaction(ptSvc.createItem(uri("http://www.example.com"), root, -1, "Testing2")); + ptSvc.commitTransaction(ptSvc.createItem(uri("http://www.example.com"), root, -1, "Testing3")); + ptSvc.commitTransaction(ptSvc.createItem(uri("http://www.example.com"), fldrId, -1, "Testing4")); + var bkmkIds = bmsvc.getBookmarkIdsForURI(uri("http://www.example.com"), {}); + var bkmk1Id = bkmkIds[0]; + var bkmk2Id = bkmkIds[1]; + var bkmk3Id = bkmkIds[2]; + var txn3 = ptSvc.moveItem(bkmk1Id, root, -1); + txn3.doTransaction(); + + // Moving items between the same folder + do_check_eq(observer._itemMovedId, bkmk1Id); + do_check_eq(observer._itemMovedOldParent, root); + do_check_eq(observer._itemMovedOldIndex, 1); + do_check_eq(observer._itemMovedNewParent, root); + do_check_eq(observer._itemMovedNewIndex, 2); + txn3.undoTransaction(); + do_check_eq(observer._itemMovedId, bkmk1Id); + do_check_eq(observer._itemMovedOldParent, root); + do_check_eq(observer._itemMovedOldIndex, 2); + do_check_eq(observer._itemMovedNewParent, root); + do_check_eq(observer._itemMovedNewIndex, 1); + + // Moving items between different folders + var txn3b = ptSvc.moveItem(bkmk1Id, fldrId, -1); + txn3b.doTransaction(); + do_check_eq(observer._itemMovedId, bkmk1Id); + do_check_eq(observer._itemMovedOldParent, root); + do_check_eq(observer._itemMovedOldIndex, 1); + do_check_eq(observer._itemMovedNewParent, fldrId); + do_check_eq(observer._itemMovedNewIndex, 2); + txn3.undoTransaction(); + do_check_eq(observer._itemMovedId, bkmk1Id); + do_check_eq(observer._itemMovedOldParent, fldrId); + do_check_eq(observer._itemMovedOldIndex, 2); + do_check_eq(observer._itemMovedNewParent, root); + do_check_eq(observer._itemMovedNewIndex, 1); + + // Test Removing a Folder + ptSvc.commitTransaction(ptSvc.createFolder("Folder2", root, -1)); + var fldrId2 = bmsvc.getChildFolder(root, "Folder2"); + var txn4 = ptSvc.removeItem(fldrId2); + txn4.doTransaction(); + do_check_eq(observer._itemRemovedId, fldrId2); + do_check_eq(observer._itemRemovedFolder, root); + do_check_eq(observer._itemRemovedIndex, 3); + txn4.undoTransaction(); + do_check_eq(observer._itemAddedId, fldrId2); + do_check_eq(observer._itemAddedParent, root); + do_check_eq(observer._itemAddedIndex, 3); + + // Test removing an item + var txn5 = ptSvc.removeItem(bkmk2Id); + txn5.doTransaction(); + do_check_eq(observer._itemRemovedId, bkmk2Id); + do_check_eq(observer._itemRemovedFolder, root); + do_check_eq(observer._itemRemovedIndex, 1); + txn5.undoTransaction(); + + do_check_eq(observer._itemAddedParent, root); + do_check_eq(observer._itemAddedIndex, 1); + + // Test creating a separator + var txn6 = ptSvc.createSeparator(root, 1); + txn6.doTransaction(); + var sepId = observer._itemAddedId; + do_check_eq(observer._itemAddedIndex, 1); + do_check_eq(observer._itemAddedParent, root); + txn6.undoTransaction(); + do_check_eq(observer._itemRemovedId, sepId); + do_check_eq(observer._itemRemovedFolder, root); + do_check_eq(observer._itemRemovedIndex, 1); + + // Test removing a separator + ptSvc.commitTransaction(ptSvc.createSeparator(root, 1)); + var sepId2 = observer._itemAddedId; + var txn7 = ptSvc.removeItem(sepId2); + txn7.doTransaction(); + do_check_eq(observer._itemRemovedId, sepId2); + do_check_eq(observer._itemRemovedFolder, root); + do_check_eq(observer._itemRemovedIndex, 1); + txn7.undoTransaction(); + do_check_eq(observer._itemAddedId, sepId2); //New separator created + do_check_eq(observer._itemAddedParent, root); + do_check_eq(observer._itemAddedIndex, 1); + + // Test editing item title + var txn8 = ptSvc.editItemTitle(bkmk1Id, "Testing2_mod"); + txn8.doTransaction(); + do_check_eq(observer._itemChangedId, bkmk1Id); + do_check_eq(observer._itemChangedProperty, "title"); + do_check_eq(observer._itemChangedValue, "Testing2_mod"); + txn8.undoTransaction(); + do_check_eq(observer._itemChangedId, bkmk1Id); + do_check_eq(observer._itemChangedProperty, "title"); + do_check_eq(observer._itemChangedValue, "Testing2"); + + // Test editing item uri + var txn9 = ptSvc.editBookmarkURI(bkmk1Id, uri("http://newuri.com")); + txn9.doTransaction(); + do_check_eq(observer._itemChangedId, bkmk1Id); + do_check_eq(observer._itemChangedProperty, "uri"); + do_check_eq(observer._itemChangedValue, "http://newuri.com/"); + txn9.undoTransaction(); + do_check_eq(observer._itemChangedId, bkmk1Id); + do_check_eq(observer._itemChangedProperty, "uri"); + do_check_eq(observer._itemChangedValue, "http://www.example.com/"); + + // Test edit item description + var txn10 = ptSvc.editItemDescription(bkmk1Id, "Description1"); + txn10.doTransaction(); + do_check_eq(observer._itemChangedId, bkmk1Id); + do_check_eq(observer._itemChangedProperty, "bookmarkProperties/description"); + + // Testing edit keyword + var txn11 = ptSvc.editBookmarkKeyword(bkmk1Id, "kw1"); + txn11.doTransaction(); + do_check_eq(observer._itemChangedId, bkmk1Id); + do_check_eq(observer._itemChangedProperty, "keyword"); + do_check_eq(observer._itemChangedValue, "kw1"); + txn11.undoTransaction(); + do_check_eq(observer._itemChangedId, bkmk1Id); + do_check_eq(observer._itemChangedProperty, "keyword"); + do_check_eq(observer._itemChangedValue, ""); + + var txn12 = ptSvc.createLivemark(uri("http://feeduri.com"), uri("http://siteuri.com"), "Livemark1", root); + txn12.doTransaction(); + + // Funky stuff going on here. + // In placesCreateLivemarkTxn, livemarks.createLivemark actually returns observer._itemAddedId -1 + // instead of observer._itemAddedId. Check w. someone. + do_check_true(lmsvc.isLivemark(observer._itemAddedId-1)); + do_check_eq(lmsvc.getSiteURI(observer._itemAddedId-1).spec, "http://siteuri.com/"); + do_check_eq(lmsvc.getFeedURI(observer._itemAddedId-1).spec, "http://feeduri.com/"); + var lvmkId = observer._itemAddedId-1; + + // editLivemarkSiteURI + var txn13 = ptSvc.editLivemarkSiteURI(lvmkId, uri("http://NEWsiteuri.com/")); + txn13.doTransaction(); + do_check_eq(observer._itemChangedId, lvmkId); + do_check_eq(observer._itemChangedProperty, "livemark/siteURI"); + + txn13.undoTransaction(); + do_check_eq(observer._itemChangedId, lvmkId); + do_check_eq(observer._itemChangedProperty, "livemark/siteURI"); + do_check_eq(observer._itemChangedValue, ""); + + // editLivemarkFeedURI + var txn14 = ptSvc.editLivemarkFeedURI(lvmkId, uri("http://NEWfeeduri.com/")); + txn14.doTransaction(); + do_check_eq(observer._itemChangedId, lvmkId); + do_check_eq(observer._itemChangedProperty, "livemark/feedURI"); + + txn14.undoTransaction(); + do_check_eq(observer._itemChangedId, lvmkId); + do_check_eq(observer._itemChangedProperty, "livemark/feedURI"); + do_check_eq(observer._itemChangedValue, ""); + + // setBookmarksToolbar + ptSvc.commitTransaction(ptSvc.createFolder("Testing toolbar folder", root, bmStartIndex)); + var tmpFolderId = bmsvc.getChildFolder(root, "Testing toolbar folder"); + var txn15 = ptSvc.setBookmarksToolbar(tmpFolderId); + txn15.doTransaction(); + do_check_eq(observer._itemChangedId, tmpFolderId); + do_check_eq(observer._itemChangedProperty, "became_toolbar_folder"); + txn15.undoTransaction(); + + // Test setLoadInSidebar + var txn16 = ptSvc.setLoadInSidebar(bkmk1Id, true); + txn16.doTransaction(); + do_check_eq(observer._itemChangedId, bkmk1Id); + do_check_eq(observer._itemChangedProperty, "bookmarkProperties/loadInSidebar"); + do_check_eq(observer._itemChanged_isAnnotationProperty, true); + txn16.undoTransaction(); + do_check_eq(observer._itemChangedId, bkmk1Id); + do_check_eq(observer._itemChangedProperty, "bookmarkProperties/loadInSidebar"); + do_check_eq(observer._itemChanged_isAnnotationProperty, true); + + // sortFolderByName + ptSvc.commitTransaction(ptSvc.createFolder("Sorting folder", root, bmStartIndex, [], null)); + var srtFldId = bmsvc.getChildFolder(root, "Sorting folder"); + ptSvc.commitTransaction(ptSvc.createItem(uri("http://www.sortingtest.com"), srtFldId, -1, "c")); + ptSvc.commitTransaction(ptSvc.createItem(uri("http://www.sortingtest.com"), srtFldId, -1, "b")); + ptSvc.commitTransaction(ptSvc.createItem(uri("http://www.sortingtest.com"), srtFldId, -1, "a")); + var b = bmsvc.getBookmarkIdsForURI(uri("http://www.sortingtest.com"), {}); + var b1 = b[0]; + var b2 = b[1]; + var b3 = b[2]; + do_check_eq(0, bmsvc.getItemIndex(b1)); + do_check_eq(1, bmsvc.getItemIndex(b2)); + do_check_eq(2, bmsvc.getItemIndex(b3)); + var txn17 = ptSvc.sortFolderByName(srtFldId, 1); + txn17.doTransaction(); + do_check_eq(2, bmsvc.getItemIndex(b1)); + do_check_eq(1, bmsvc.getItemIndex(b2)); + do_check_eq(0, bmsvc.getItemIndex(b3)); + txn17.undoTransaction(); + do_check_eq(0, bmsvc.getItemIndex(b1)); + do_check_eq(1, bmsvc.getItemIndex(b2)); + do_check_eq(2, bmsvc.getItemIndex(b3)); + + // editBookmarkMicrosummary + var tmpMs = mss.createMicrosummary(uri("http://testmicro.com"), + uri("http://dietrich.ganx4.com/mozilla/test-microsummary.xml")); + ptSvc.commitTransaction( + ptSvc.createItem(uri("http://dietrich.ganx4.com/mozilla/test-microsummary-content.php"), + root, -1, "micro test", null, null, null)); + var bId = (bmsvc.getBookmarkIdsForURI(uri("http://dietrich.ganx4.com/mozilla/test-microsummary-content.php"),{}))[0]; + do_check_true(!mss.hasMicrosummary(bId)); + var txn18 = ptSvc.editBookmarkMicrosummary(bId, tmpMs); + txn18.doTransaction(); + do_check_eq(observer._itemChangedId, bId); + do_check_eq(observer._itemChangedProperty, "bookmarks/generatedTitle"); + do_check_true(mss.hasMicrosummary(bId)); + txn18.undoTransaction(); + do_check_eq(observer._itemChangedId, bId); + do_check_eq(observer._itemChangedProperty, "bookmarks/generatedTitle"); + do_check_true(!mss.hasMicrosummary(bId)); + + // Testing edit Post Data... + // mmm.. cant figure out a good way to test this. +} diff --git a/browser/installer/unix/packages-static b/browser/installer/unix/packages-static index a3684781d9e..db6ea237ad2 100644 --- a/browser/installer/unix/packages-static +++ b/browser/installer/unix/packages-static @@ -198,6 +198,7 @@ bin/components/nsBrowserContentHandler.js bin/components/nsBrowserGlue.js bin/components/nsSetDefaultBrowser.js bin/components/nsMicrosummaryService.js +bin/components/nsPlacesTransactionsService.js bin/components/nsSearchService.js bin/components/nsSearchSuggestions.js bin/components/nsLoginInfo.js diff --git a/browser/installer/windows/packages-static b/browser/installer/windows/packages-static index 95fd7b3ebe2..8e6eebbe206 100644 --- a/browser/installer/windows/packages-static +++ b/browser/installer/windows/packages-static @@ -213,6 +213,7 @@ bin\components\nsXmlRpcClient.js bin\components\nsExtensionManager.js bin\components\nsUpdateService.js bin\components\nsMicrosummaryService.js +bin\components\nsPlacesTransactionsService.js bin\components\nsPostUpdateWin.js bin\components\nsLoginInfo.js bin\components\nsLoginManager.js