From dca4d1ab35181f160354cc471a440755b18e16a3 Mon Sep 17 00:00:00 2001 From: "jminta%gmail.com" Date: Thu, 15 Jun 2006 21:58:43 +0000 Subject: [PATCH] Bug 168411 Move bookmarks transactions into a JS service (adding a bookmark leaks the Add Bookmark dialog) r=sspitzer, sr+a=mconnor --- .../components/bookmarks/content/bookmarks.js | 440 ++++-------------- .../bookmarks/content/bookmarksManager.js | 8 +- .../bookmarks/content/bookmarksTree.xml | 47 +- .../components/bookmarks/public/Makefile.in | 3 +- .../public/nsIBookmarkTransactionManager.idl | 130 ++++++ .../bookmarks/public/nsIBookmarksService.idl | 3 - browser/components/bookmarks/src/Makefile.in | 1 + .../src/nsBookmarkTransactionManager.js | 373 +++++++++++++++ .../bookmarks/src/nsBookmarksService.cpp | 12 - .../bookmarks/src/nsBookmarksService.h | 2 - 10 files changed, 645 insertions(+), 374 deletions(-) create mode 100644 browser/components/bookmarks/public/nsIBookmarkTransactionManager.idl create mode 100644 browser/components/bookmarks/src/nsBookmarkTransactionManager.js diff --git a/browser/components/bookmarks/content/bookmarks.js b/browser/components/bookmarks/content/bookmarks.js index 1a38573d839c..efabd75ccb14 100644 --- a/browser/components/bookmarks/content/bookmarks.js +++ b/browser/components/bookmarks/content/bookmarks.js @@ -21,6 +21,7 @@ # # Contributor(s): # Ben Goodger (Original Author) +# Joey Minta # # 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 @@ -42,6 +43,8 @@ const ADD_BM_DIALOG_FEATURES = "centerscreen,chrome,dialog,resizable,modal"; const ADD_BM_DIALOG_FEATURES = "centerscreen,chrome,dialog,resizable,dependent"; #endif +const kBATCH_LIMIT = 4; + var gNC_NS, gWEB_NS, gRDF_NS, gXUL_NS, gNC_NS_CMD; // definition of the services frequently used for bookmarks @@ -84,6 +87,7 @@ var kIOIID; var IOSVC; var gBmProperties; +var gBkmkTxnSvc; // should be moved in a separate file function initServices() @@ -135,6 +139,8 @@ function initServices() RDF.GetResource(gNC_NS+"Description"), RDF.GetResource(gNC_NS+"WebPanel"), RDF.GetResource(gNC_NS+"FeedURL")]; + gBkmkTxnSvc = Components.classes["@mozilla.org/bookmarks/transactionmanager;1"] + .getService(Components.interfaces.nsIBookmarkTransactionManager); } function initBMService() @@ -142,8 +148,6 @@ function initBMService() kBMSVCIID = Components.interfaces.nsIBookmarksService; BMDS = RDF.GetDataSource("rdf:bookmarks"); BMSVC = BMDS.QueryInterface(kBMSVCIID); - BookmarkTransaction.prototype.RDFC = RDFC; - BookmarkTransaction.prototype.BMDS = BMDS; } /** @@ -415,14 +419,14 @@ var BookmarksCommand = { undoBookmarkTransaction: function () { - BMSVC.transactionManager.undoTransaction(); + gBkmkTxnSvc.undo(); BookmarksUtils.refreshSearch(); BookmarksUtils.flushDataSource(); }, redoBookmarkTransaction: function () { - BMSVC.transactionManager.redoTransaction(); + gBkmkTxnSvc.redo(); BookmarksUtils.refreshSearch(); BookmarksUtils.flushDataSource(); }, @@ -456,7 +460,7 @@ var BookmarksCommand = { sBookmarkItem += aSelection.item[i].Value + "\n"; // save the selection property into text string that we will use later in paste function - // and in BookmarkInsertTransaction + // and in INSERT tranasactions // (if the selection is folder or livemark save all childs property) var aType = BookmarksUtils.resolveType(aSelection.item[i]); if (aType == "Livemark") { @@ -900,15 +904,17 @@ var BookmarksCommand = { this.doBookmarksCommand(rTarget, gNC_NS_CMD+"import", args); var countAfter = parseInt(BookmarksUtils.getProperty(rTarget, gRDF_NS+"nextVal")); - var transaction = new BookmarkImportTransaction("import"); + if (countAfter - countBefore > 1) + gBkmkTxnSvc.startBatch(); for (var index = countBefore; index < countAfter; index++) { var nChildArc = RDFCU.IndexToOrdinalResource(index); var rChild = BMDS.GetTarget(rTarget, nChildArc, true); - transaction.item .push(rChild); - transaction.parent .push(rTarget); - transaction.index .push(index); + gBkmkTxnSvc.createAndCommitTxn(gBkmkTxnSvc.IMPORT, "IMPORT", rChild, index, + rTarget, 0, null); } - BMSVC.transactionManager.doTransaction(transaction); + if (countAfter - countBefore > 1) + gBkmkTxnSvc.endBatch(); + BookmarksUtils.flushDataSource(); }, @@ -1111,10 +1117,10 @@ var BookmarksController = { switch(aCommand) { case "cmd_undo": case "cmd_bm_undo": - return BMSVC.transactionManager.numberOfUndoItems > 0; + return gBkmkTxnSvc.canUndo(); case "cmd_redo": case "cmd_bm_redo": - return BMSVC.transactionManager.numberOfRedoItems > 0; + return gBkmkTxnSvc.canRedo(); case "cmd_paste": if (ptype0 == "Livemark" || (aTarget && !BookmarksUtils.isValidTargetContainer(aTarget.parent))) return false; @@ -1548,11 +1554,11 @@ var BookmarksUtils = { removeSelection: function (aAction, aSelection) { - var transaction = new BookmarkRemoveTransaction(aAction); - transaction.item = []; - transaction.parent = []; - transaction.index = []; - transaction.removedProp = []; + if (aSelection.length > 1) + gBkmkTxnSvc.startBatch(); + if (aSelection.length > this.BATCH_LIMIT && aAction != "move") + BMDS.beginUpdateBatch(); + for (var i = 0; i < aSelection.length; ++i) { // try to put back aSelection.parent[i] if it's null, so we can delete after searching if (aSelection.parent[i] == null) @@ -1560,32 +1566,37 @@ var BookmarksUtils = { if (aSelection.parent[i]) { RDFC.Init(BMDS, aSelection.parent[i]); - transaction.item .push(aSelection.item[i]); - transaction.parent.push(aSelection.parent[i]); - transaction.index .push(RDFC.IndexOf(aSelection.item[i])); - // save the selection property into array that uses later in BookmarkRemoveTransaction + // save the selection property into array that is used later in + // when performing the REMOVE transaction // (if the selection is folder save all childs property) + var propArray; if (aAction != "move") { - var propArray = []; - propArray.push([aSelection.item[i], null, null, null, null, null, null]); + propArray = [aSelection.item[i], null, null, null, null, null, null]; var aType = BookmarksUtils.resolveType(aSelection.item[i]); if (aType != "Livemark") {// don't change livemark properties for (var j = 0; j < gBmProperties.length; ++j) { var oldValue = BMDS.GetTarget(aSelection.item[i], gBmProperties[j], true); if (oldValue) - propArray[0][j+1] = oldValue.QueryInterface(kRDFLITIID); + propArray[j+1] = oldValue.QueryInterface(kRDFLITIID); } } if (aType == "Folder" || aType == "Livemark") BookmarksUtils.getAllChildren(aSelection.item[i], propArray); - transaction.removedProp.push(propArray); - } else { - transaction.removedProp.push(null); } + + var proplength = propArray ? propArray.length : 0; + gBkmkTxnSvc.createAndCommitTxn(gBkmkTxnSvc.REMOVE, aAction, + aSelection.item[i], + RDFC.IndexOf(aSelection.item[i]), + aSelection.parent[i], + proplength, propArray); } } - BMSVC.transactionManager.doTransaction(transaction); + if (aSelection.length > 1) + gBkmkTxnSvc.endBatch(); + if (aSelection.length > this.BATCH_LIMIT && aAction != "move") + BMDS.beginUpdateBatch(); return true; }, @@ -1645,20 +1656,22 @@ var BookmarksUtils = { insertSelection: function (aAction, aSelection, aTarget, aTargetIndex) { - var transaction = new BookmarkInsertTransaction(aAction); - transaction.item = new Array(aSelection.length); - transaction.parent = new Array(aSelection.length); - transaction.index = new Array(aSelection.length); - transaction.removedProp = new Array(aSelection.length); + var item, removedProps; var index = aTarget.index; + var brokenIndex = aTarget.index; + + if (aSelection.length > 1) + gBkmkTxnSvc.startBatch(); + if (aSelection.length > this.BATCH_LIMIT && aAction != "move") + BMDS.beginUpdateBatch(); + for (var i=0; i 1) + gBkmkTxnSvc.endBatch(); + if (aSelection.length > this.BATCH_LIMIT && aAction != "move") + BMDS.endUpdateBatch(); }, moveAndCheckSelection: function (aAction, aSelection, aTarget) @@ -1697,8 +1716,15 @@ var BookmarksUtils = { moveSelection: function (aAction, aSelection, aTarget) { - var txn = new BookmarkMoveTransaction(aAction, aSelection, aTarget); - BMSVC.transactionManager.doTransaction(txn); + if (aSelection.length > kBATCH_LIMIT) + BMDS.beginUpdateBatch(); + + gBkmkTxnSvc.startBatch(); + BookmarksUtils.removeSelection("move", aSelection); + BookmarksUtils.insertSelection("move", aSelection, aTarget); + gBkmkTxnSvc.endBatch(); + if (aSelection.length > kBATCH_LIMIT) + BMDS.endUpdateBatch(); }, // returns true if this selection should be copied instead of moved, @@ -1918,297 +1944,8 @@ var BookmarksUtils = { } } -function BookmarkTransaction() -{ -} - -BookmarkTransaction.prototype = { - BATCH_LIMIT : 4, - RDFC : null, - BMDS : null, - - QueryInterface: function (iid) - { - if (!iid.equals(Components.interfaces.nsITransaction) && - !iid.equals(Components.interfaces.nsISupports)) - throw Components.results.NS_ERROR_NO_INTERFACE; - - return this; - }, - - beginUpdateBatch: function() - { - if (this.item.length > this.BATCH_LIMIT) { - this.BMDS.beginUpdateBatch(); - } - }, - - endUpdateBatch: function() - { - if (this.item.length > this.BATCH_LIMIT) { - this.BMDS.endUpdateBatch(); - } - }, - merge : function (aTxn) {return false}, - getHelperForLanguage: function (aCount) {return null}, - getInterfaces : function (aCount) {return null}, - canCreateWrapper : function (aIID) {return "AllAccess"} -} - -function BookmarkInsertTransaction (aAction) -{ - this.wrappedJSObject = this; - this.type = "insert"; - this.action = aAction; - this.item = null; - this.parent = null; - this.index = null; - this.removedProp = null; - this.Properties = gBmProperties; - // move container declaration to her so it can be recognize if - // undoTransaction is call after the BM manager is close and reopen. - this.container = Components.classes[kRDFCContractID].createInstance(kRDFCIID); -} - -BookmarkInsertTransaction.prototype = -{ - __proto__: BookmarkTransaction.prototype, - - isTransient: false, - - doTransaction: function () - { - this.beginUpdateBatch(); - for (var i=0; i= 0; i--) { - this.container.Init(this.BMDS, this.parent[i]); - - // remove all properties befor we remove the element so nsLocalSearchService - // don't return deleted element in Search - var props = this.removedProp[i]; - if (props){ - for (var k = 0; k < props.length; ++k) { - for (var j = 0; j < this.Properties.length; ++j) { - var oldValue = props[k][j+1]; - if (oldValue) - this.BMDS.Unassert(props[k][0], this.Properties[j], oldValue); - } - } - } - - this.container.RemoveElementAt(this.index[i], true); - } - this.endUpdateBatch(); - }, - - redoTransaction: function () - { - this.doTransaction(); - } -} - -function BookmarkRemoveTransaction (aAction) -{ - this.wrappedJSObject = this; - this.type = "remove"; - this.action = aAction; - this.item = null; - this.parent = null; - this.index = null; - this.removedProp = null; - this.Properties = gBmProperties; -} - -BookmarkRemoveTransaction.prototype = -{ - __proto__: BookmarkTransaction.prototype, - - isTransient: false, - - doTransaction: function () - { - this.beginUpdateBatch(); - for (var i=0; i=0; i--) { - this.RDFC.Init(this.BMDS, this.parent[i]); - this.RDFC.InsertElementAt(this.item[i], this.index[i], false); - - // insert back all the properties - var props = this.removedProp[i]; - if (props) { - for (var k = 0; k < props.length; ++k) { - for (var j = 0; j < this.Properties.length; ++j) { - var newValue = props[k][j+1]; - if (newValue) - this.BMDS.Assert(props[k][0], this.Properties[j], newValue, true); - } - } - } - - } - this.endUpdateBatch(); - }, - - redoTransaction: function () - { - this.doTransaction(); - } -} - -function BookmarkMoveTransaction (aAction, aSelection, aTarget) -{ - this.wrappedJSObject = this; - this.type = "move"; - this.action = aAction; - this.selection = aSelection; - this.target = aTarget; -} - -BookmarkMoveTransaction.prototype = -{ - __proto__: BookmarkTransaction.prototype, - - isTransient: false, - - beginUpdateBatch: function() - { - if (this.selection.length > this.BATCH_LIMIT) { - this.BMDS.beginUpdateBatch(); - } - }, - - endUpdateBatch: function() - { - if (this.selection.length > this.BATCH_LIMIT) { - this.BMDS.endUpdateBatch(); - } - }, - - doTransaction: function () - { - this.beginUpdateBatch(); - BookmarksUtils.removeSelection("move", this.selection); - BookmarksUtils.insertSelection("move", this.selection, this.target); - this.endUpdateBatch(); - }, - - undoTransaction: function () {}, - redoTransaction: function () {} -} - -function BookmarkImportTransaction (aAction) -{ - this.wrappedJSObject = this; - this.type = "import"; - this.action = aAction; - this.item = []; - this.parent = []; - this.index = []; -} - -BookmarkImportTransaction.prototype = -{ - __proto__: BookmarkTransaction.prototype, - - isTransient: false, - - doTransaction: function () - { - }, - - undoTransaction: function () - { - this.beginUpdateBatch(); - for (var i=this.item.length-1; i>=0; i--) { - this.RDFC.Init(this.BMDS, this.parent[i]); - this.RDFC.RemoveElementAt(this.index[i], true); - } - this.endUpdateBatch(); - }, - - redoTransaction: function () - { - this.beginUpdateBatch(); - for (var i=0; i (Original Author) # Blake Ross # Pierre Chanial (v 2.0) +# Joey Minta # # 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 @@ -504,11 +505,30 @@ @@ -813,21 +833,28 @@ mOuter: this, + mLastTxnWasDo: null, + willDo: function (aTxmgr, aTxn) {}, didDo : function (aTxmgr, aTxn) { + this.mLastTxnWasDo = true; this.mOuter.preUpdateTreeSelection(aTxn, true); }, willUndo: function (aTxmgr, aTxn) {}, didUndo : function (aTxmgr, aTxn) { + this.mLastTxnWasDo = false; this.mOuter.preUpdateTreeSelection(aTxn, false); }, willRedo: function (aTxmgr, aTxn) {}, didRedo : function (aTxmgr, aTxn) { + this.mLastTxnWasDo = true; this.mOuter.preUpdateTreeSelection(aTxn, true); }, didMerge : function (aTxmgr, aTxn) {}, didBeginBatch : function (aTxmgr, aTxn) {}, - didEndBatch : function (aTxmgr, aTxn) {}, + didEndBatch : function (aTxmgr, aTxn) { + this.mOuter.preUpdateTreeSelection(aTxn, this.mLastTxnWasDo); + }, willMerge : function (aTxmgr, aTxn) {}, willBeginBatch : function (aTxmgr, aTxn) {}, willEndBatch : function (aTxmgr, aTxn) {} @@ -925,10 +952,14 @@ // Adding the transaction listener - BMSVC.transactionManager.AddListener(this.bookmarkTreeTransactionListener); + var bkmkTxnSvc = Components.classes["@mozilla.org/bookmarks/transactionmanager;1"] + .getService(Components.interfaces.nsIBookmarkTransactionManager); + bkmkTxnSvc.transactionManager.AddListener(this.bookmarkTreeTransactionListener); - BMSVC.transactionManager.RemoveListener(this.bookmarkTreeTransactionListener); + var bkmkTxnSvc = Components.classes["@mozilla.org/bookmarks/transactionmanager;1"] + .getService(Components.interfaces.nsIBookmarkTransactionManager); + bkmkTxnSvc.transactionManager.RemoveListener(this.bookmarkTreeTransactionListener); 2 diff --git a/browser/components/bookmarks/public/Makefile.in b/browser/components/bookmarks/public/Makefile.in index 9b5d0a4a75f6..1074577611f4 100644 --- a/browser/components/bookmarks/public/Makefile.in +++ b/browser/components/bookmarks/public/Makefile.in @@ -45,7 +45,8 @@ include $(DEPTH)/config/autoconf.mk MODULE = bookmarks XPIDL_MODULE = bookmarks -XPIDLSRCS = nsIBookmarksService.idl +XPIDLSRCS = nsIBookmarksService.idl \ + nsIBookmarkTransactionManager.idl include $(topsrcdir)/config/rules.mk diff --git a/browser/components/bookmarks/public/nsIBookmarkTransactionManager.idl b/browser/components/bookmarks/public/nsIBookmarkTransactionManager.idl new file mode 100644 index 000000000000..e4b651026981 --- /dev/null +++ b/browser/components/bookmarks/public/nsIBookmarkTransactionManager.idl @@ -0,0 +1,130 @@ +/* -*- 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 Bookmarks transaction code. + * + * The Initial Developer of the Original Code is + * Joey Minta + * + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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 nsIRDFResource; +interface nsIRDFNode; +interface nsITransactionManager; + +/** + * nsIBookmarkTransactionService is a service designed to handle + * nsITransactions that correspond to changes in bookmarks. It is here as a + * service so that we can keep the transactions around without holding onto + * the whole global js scope+window. + */ + +[scriptable, uuid(f6305e79-1760-4991-ab4d-a42db60f0e67)] +interface nsIBookmarkTransactionManager : nsISupports +{ + /** + * Performs a new transaction according to the specified parameters + * + * @param aType the type of transaction being performed. Must be one + * of the three constants defined below + * @param aAction the action to be performed. Expected values are: + * import, insert, move, and remove (see bookmarks.js) + * @param aItem the rdf node the transaction is being performed on + * @param aIndex the index of the item in its RDF Container + * @param aParent the rdf-parent of aItem, that is, the folder it + * should be inserted into. + * @param aRemovedProps properties removed from the item in question + * + */ + void createAndCommitTxn(in unsigned long aType, + in aString aAction, + in nsIRDFNode aItem, + in long aIndex, + in nsIRDFResource aParent, + in unsigned long aPropCount, + [array, size_is(aPropCount)] in nsIRDFNode aRemovedProps); + + /** + * Constants corresponding to the 3 different types of transactions possible + * Note that moving bookmarks is a combination of REMOVE+INSERT + */ + const unsigned long IMPORT = 0; + const unsigned long INSERT = 1; + const unsigned long REMOVE = 2; + + /** + * Signals the transaction manager that a series of transactions are going to + * be performed, but that, for the purposes of undo and redo, they should all + * be regarded as a single transaction. That is, a single undo() call will + * undo all of the transactions created and committed between startBatch() and + * endBatch(). See also nsITransactionManager::beginBatch + * + * @note if startBatch() is called multiple times. The batch will not end + * endBatch() has been called the same number of times. + */ + void startBatch(); + + /** + * Ends the batch transaction in process, subject to the note above about + * multiple, successive calls of startBatch(). See also + * nsITransactionManager::endBatch + */ + void endBatch(); + + /** + * Undo the last transaction in the transaction manager's stack + */ + void undo(); + + /** + * Returns true if it is possible to undo a transaction at this time + */ + boolean canUndo(); + + /** + * Redo the last transaction + */ + void redo(); + + /** + * Returns true if it is possible to redo a transaction at this time + */ + boolean canRedo(); + + /** + * A reference to the transaction manager for bookmarks + */ + readonly attribute nsITransactionManager transactionManager; + +}; diff --git a/browser/components/bookmarks/public/nsIBookmarksService.idl b/browser/components/bookmarks/public/nsIBookmarksService.idl index 99e87cee5f61..89994f879aa7 100644 --- a/browser/components/bookmarks/public/nsIBookmarksService.idl +++ b/browser/components/bookmarks/public/nsIBookmarksService.idl @@ -46,7 +46,6 @@ interface nsIArray; interface nsIRDFResource; -interface nsITransactionManager; [scriptable, uuid(ccc48346-429d-4931-94dc-beb1afcea292)] interface nsIBookmarksService : nsISupports @@ -108,8 +107,6 @@ interface nsIBookmarksService : nsISupports string resolveKeyword(in wstring aName, out wstring aPostData); - readonly attribute nsITransactionManager transactionManager; - //XXXpch: to be removed. void addBookmarkImmediately(in wstring aURI, in wstring aTitle, in long bmType, in wstring docCharset); boolean isBookmarked(in string aURL); diff --git a/browser/components/bookmarks/src/Makefile.in b/browser/components/bookmarks/src/Makefile.in index a56073688487..e077a25e877f 100644 --- a/browser/components/bookmarks/src/Makefile.in +++ b/browser/components/bookmarks/src/Makefile.in @@ -73,6 +73,7 @@ CPPSRCS = nsBookmarksService.cpp \ $(NULL) #EXTRA_COMPONENTS = nsBookmarkProtocolHandler.js +EXTRA_COMPONENTS = nsBookmarkTransactionManager.js # we don't want the shared lib, but we want to force the creation of a # static lib. diff --git a/browser/components/bookmarks/src/nsBookmarkTransactionManager.js b/browser/components/bookmarks/src/nsBookmarkTransactionManager.js new file mode 100644 index 000000000000..6c88f536dc23 --- /dev/null +++ b/browser/components/bookmarks/src/nsBookmarkTransactionManager.js @@ -0,0 +1,373 @@ +/* ***** 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 bookmark transaction code. + * + * The Initial Developer of the Original Code is + * Joey Minta + * + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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 ***** */ + +function bookmarkTransactionManager() { + this.wrappedJSObject = this; + this.mTransactionManager = Components.classes["@mozilla.org/transactionmanager;1"] + .createInstance(Components.interfaces.nsITransactionManager); + + this.mBatchCount = 0; + + this.classInfo = { + getInterfaces: function (count) { + var ifaces = [ + Components.interfaces.nsISupports, + Components.interfaces.nsIClassInfo + ]; + count.value = ifaces.length; + return ifaces; + }, + + getHelperForLanguage: function (language) { + return null; + }, + + contractID: "@mozilla.org/bookmarks/transactionManager;1", + classDescription: "Booksmarks Transaction Manager", + classID: Components.ID("{62d2f7fb-acd2-4876-aa2d-b607de9329ff}"), + implementationLanguage: Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT, + flags: 0 + }; + + // Define our transactions + function bkmkTxn() { + this.item = null; + this.parent = null; + this.index = null; + this.removedProp = null; + }; + + bkmkTxn.prototype = { + BMDS: null, + + QueryInterface: function bkmkTxnQI(iid) { + if (!iid.equals(Components.interfaces.nsITransaction) && + !iid.equals(Components.interfaces.nsISupports)) + throw Components.results.NS_ERROR_NO_INTERFACE; + + return this; + }, + + merge : function (aTxn) {return false}, + getHelperForLanguage: function (aCount) {return null}, + getInterfaces : function (aCount) {return null}, + canCreateWrapper : function (aIID) {return "AllAccess"}, + + mAssertProperties: function bkmkTxnAssertProps(aProps) { + if (!aProps) { + return; + } + + for each (var prop in aProps) { + for (var i = 0; i < this.Properties.length; i++) { + var oldValue = this.BMDS.GetTarget(this.item, this.Properties[i], true); + // must check, if paste call after copy the oldvalue didn't remove. + if (!oldValue) { + var newValue = aProps[i+1]; + if (newValue) { + this.BMDS.Assert(this.item, + this.Properties[i], + newValue, true); + } + } else { + this.removedProp[i+1] = oldValue; + } + } + } + }, + + mUnassertProperties: function bkmkTxnUnassertProps(aProps) { + if (!aProps) { + return; + } + for each (var prop in aProps) { + for (var i = 0; i < this.Properties.length; i++) { + var oldValue = aProps[i+1]; + if (oldValue) { + this.BMDS.Unassert(this.item, this.Properties[i], oldValue); + } + } + } + } + }; + + bkmkTxn.prototype.RDFC = + Components.classes["@mozilla.org/rdf/container;1"] + .createInstance(Components.interfaces.nsIRDFContainer); + var rdfService = Components.classes["@mozilla.org/rdf/rdf-service;1"] + .getService(Components.interfaces.nsIRDFService); + bkmkTxn.prototype.BMDS = rdfService.GetDataSource("rdf:bookmarks"); + + bkmkTxn.prototype.Properties = + [rdfService.GetResource("http://home.netscape.com/NC-rdf#Name"), + rdfService.GetResource("http://home.netscape.com/NC-rdf#URL"), + rdfService.GetResource("http://home.netscape.com/NC-rdf#ShortcutURL"), + rdfService.GetResource("http://home.netscape.com/NC-rdf#Description"), + rdfService.GetResource("http://home.netscape.com/NC-rdf#WebPanel"), + rdfService.GetResource("http://home.netscape.com/NC-rdf#FeedURL")]; + + function bkmkInsertTxn(aAction) { + this.type = "insert"; + // move container declaration to here so it can be recognized if + // undoTransaction is call after the BM manager is close and reopen. + this.container = Components.classes["@mozilla.org/rdf/container;1"] + .createInstance(Components.interfaces.nsIRDFContainer); + } + + bkmkInsertTxn.prototype = { + __proto__: bkmkTxn.prototype, + + isTransient: false, + + doTransaction: function bkmkInsertDoTxn() { + this.RDFC.Init(this.BMDS, this.parent); + // if the index is -1, we use appendElement, and then update the + // index so that undoTransaction can still function + if (this.index == -1) { + this.RDFC.AppendElement(this.item); + this.index = this.RDFC.GetCount(); + } else { +/*XXX- broken insert code, see bug 264571 + try { + this.RDFC.InsertElementAt(this.item, this.index, true); + } catch (e if e.result == Components.results.NS_ERROR_ILLEGAL_VALUE) { + // if this failed, then we assume that we really want to append, + // because things are out of whack until we renumber. + this.RDFC.AppendElement(this.item); + // and then fix up the index so undo works + this.index = this.RDFC.GetCount(); + } +*/ + this.RDFC.InsertElementAt(this.item, this.index, true); + } + + // insert back all the properties + this.mAssertProperties(this.removedProp); + }, + + undoTransaction: function bkmkInsertUndoTxn() { + // XXXvarga Can't use |RDFC| here because it's being "reused" elsewhere. + this.container.Init(this.BMDS, this.parent); + + // remove all properties befor we remove the element so + // nsLocalSearchService doesn't return deleted element in Search + this.mUnassertProperties(this.removedProp); + + this.container.RemoveElementAt(this.index, true); + }, + + redoTransaction: function bkmkInsertRedoTxn() { + this.doTransaction(); + } + }; + + function bkmkRemoveTxn() { + this.type = "remove"; + } + + bkmkRemoveTxn.prototype = { + __proto__: bkmkTxn.prototype, + + isTransient: false, + + doTransaction: function bkmkRemoveDoTxn() { + this.RDFC.Init(this.BMDS, this.parent); + + // remove all properties befor we remove the element so + // nsLocalSearchService doesn't return deleted element in Search + this.mUnassertProperties(this.removedProp); + + this.RDFC.RemoveElementAt(this.index, false); + }, + + undoTransaction: function bkmkRemoveUndoTxn() { + this.RDFC.Init(this.BMDS, this.parent); + this.RDFC.InsertElementAt(this.item, this.index, false); + + // insert back all the properties + this.mAssertProperties(this.removedProp); + }, + + redoTransaction: function () { + this.doTransaction(); + } + } + + function bkmkImportTxn(aAction) { + this.type = "import"; + this.action = aAction; + } + + bkmkImportTxn.prototype = { + __proto__: bkmkTxn.prototype, + + isTransient: false, + + doTransaction: function bkmkImportDoTxn() {}, + + undoTransaction: function mkmkImportUndoTxn() { + this.RDFC.Init(this.BMDS, this.parent); + this.RDFC.RemoveElementAt(this.index, true); + }, + + redoTransaction: function bkmkImportredoTxn() { + this.RDFC.Init(this.BMDS, this.parent); + this.RDFC.InsertElementAt(this.item, this.index, true); + } + }; + + this.BookmarkRemoveTransaction = bkmkRemoveTxn; + this.BookmarkInsertTransaction = bkmkInsertTxn; + this.BookmarkImportTransaction = bkmkImportTxn; +} + +bookmarkTransactionManager.prototype.QueryInterface = function bkTxnMgrQI(aIID) { + if (aIID.equals(Components.interfaces.nsISupports) || + aIID.equals(Components.interfaces.nsIBookmarkTransactionManager)) { + return this; + } + if (aIID.equals(Components.interfaces.nsIClassInfo)) { + return this.classInfo; + } + + throw NS_ERROR_NO_INTERFACE; +} + +bookmarkTransactionManager.prototype.createAndCommitTxn = +function bkmkTxnMgrCandC(aType, aAction, aItem, aIndex, aParent, aPropCount, aRemovedProps) { + var txn; + var nsIBookmarkTransactionManager = Components.interfaces.nsIBookmarkTransactionManager; + switch (aType) { + case nsIBookmarkTransactionManager.IMPORT: + txn = new this.BookmarkImportTransaction(aAction); + break; + case nsIBookmarkTransactionManager.INSERT: + txn = new this.BookmarkInsertTransaction(aAction); + break; + case nsIBookmarkTransactionManager.REMOVE: + txn = new this.BookmarkRemoveTransaction(aAction); + break; + default: + Components.utils.reportError("Unknown bookmark transaction type:"+aType); + throw NS_ERROR_FAILURE; + } + txn.item = aItem; + txn.parent = aParent; + txn.index = aIndex; + txn.removedProp = aRemovedProps; + txn.action = aAction; + txn.wrappedJSObject = txn; + this.mTransactionManager.doTransaction(txn); +} + +bookmarkTransactionManager.prototype.startBatch = function bkmkTxnMgrUndo() { + if (this.mBatchCount == 0) { + this.mTransactionManager.beginBatch(); + } + this.mBatchCount++; +} + +bookmarkTransactionManager.prototype.endBatch = function bkmkTxnMgrUndo() { + this.mBatchCount--; + if (this.mBatchCount == 0) { + this.mTransactionManager.endBatch(); + } +} + +bookmarkTransactionManager.prototype.undo = function bkmkTxnMgrUndo() { + this.mTransactionManager.undoTransaction(); +} + +bookmarkTransactionManager.prototype.redo = function bkmkTxnMgrRedo() { + this.mTransactionManager.redoTransaction(); +} + +bookmarkTransactionManager.prototype.canUndo = function bkmkTxnMgrCanUndo() { + return this.mTransactionManager.numberOfUndoItems > 0; +} + +bookmarkTransactionManager.prototype.canRedo = function bkmkTxnMgrCanRedo() { + return this.mTransactionManager.numberOfRedoItems > 0; +} + +bookmarkTransactionManager.prototype.__defineGetter__("transactionManager", +function bkmkTxnMgrGetter() { return this.mTransactionManager; }); + +/**** + **** module registration + ****/ + +const kFactory = { + createInstance: function (outer, iid) { + if (outer != null) + throw Components.results.NS_ERROR_NO_AGGREGATION; + return (new bookmarkTransactionManager()).QueryInterface(iid); + } +}; + +var bkmkTxnMgrModule = { + mCID: Components.ID("{8be133d0-681d-4f0b-972b-6a68e41afb62}"), + mContractID: "@mozilla.org/bookmarks/transactionmanager;1", + + registerSelf: function (compMgr, fileSpec, location, type) { + compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar); + compMgr.registerFactoryLocation(this.mCID, + "Bookmark Transaction Manager", + this.mContractID, + fileSpec, + location, + type); + }, + + getClassObject: function (compMgr, cid, iid) { + if (!cid.equals(this.mCID)) + throw Components.results.NS_ERROR_NO_INTERFACE; + + if (!iid.equals(Components.interfaces.nsIFactory)) + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + + return kFactory; + }, + + canUnload: function(compMgr) { + return true; + } +}; + +function NSGetModule(compMgr, fileSpec) { + return bkmkTxnMgrModule; +} diff --git a/browser/components/bookmarks/src/nsBookmarksService.cpp b/browser/components/bookmarks/src/nsBookmarksService.cpp index 593bd690864a..ee2e8178d6f3 100644 --- a/browser/components/bookmarks/src/nsBookmarksService.cpp +++ b/browser/components/bookmarks/src/nsBookmarksService.cpp @@ -1698,9 +1698,6 @@ nsBookmarksService::Init() nsICache::STREAM_BASED, getter_AddRefs(mCacheSession)); } - mTransactionManager = do_CreateInstance(NS_TRANSACTIONMANAGER_CONTRACTID, &rv); - if (NS_FAILED(rv)) return rv; - /* create a URL for the string resource file */ nsCOMPtr uri; if (NS_SUCCEEDED(rv = mNetService->NewURI(bookmark_properties, nsnull, nsnull, @@ -3531,15 +3528,6 @@ nsBookmarksService::ResolveKeyword(const PRUnichar *aUserInput, PRUnichar** aPos return NS_RDF_NO_VALUE; } -NS_IMETHODIMP -nsBookmarksService::GetTransactionManager(nsITransactionManager** aTransactionManager) -{ - NS_ENSURE_ARG_POINTER(aTransactionManager); - - NS_ADDREF(*aTransactionManager = mTransactionManager); - return NS_OK; -} - NS_IMETHODIMP nsBookmarksService::GetBookmarksToolbarFolder(nsIRDFResource** aResult) { diff --git a/browser/components/bookmarks/src/nsBookmarksService.h b/browser/components/bookmarks/src/nsBookmarksService.h index f12329a1054f..381a33425b33 100644 --- a/browser/components/bookmarks/src/nsBookmarksService.h +++ b/browser/components/bookmarks/src/nsBookmarksService.h @@ -56,7 +56,6 @@ #include "nsIIOService.h" #include "nsICacheService.h" #include "nsICacheSession.h" -#include "nsITransactionManager.h" #include "nsIPrefBranch.h" class nsIOutputStream; @@ -84,7 +83,6 @@ protected: nsCOMPtr mNetService; nsCOMPtr mCacheService; nsCOMPtr mCacheSession; - nsCOMPtr mTransactionManager; PRUint32 htmlSize; PRInt32 mUpdateBatchNest;