Mozilla/mozilla/browser/components/bookmarks/src/nsBookmarkTransactionManager.js
jminta%gmail.com 1923b801b9 Bug 342110 crash when copying and pasting bookmark folder [@ nsBookmarksService::UpdateBookmarkForwardProxy], r=sspitzer, sr=mconnor
git-svn-id: svn://10.0.0.236/trunk@200895 18797224-902f-48f8-a5cc-f745e15eee43
2006-06-26 18:32:22 +00:00

374 lines
13 KiB
JavaScript

/* ***** 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 <jminta@gmail.com>
*
* 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];
if (newValue) {
this.BMDS.Assert(this.item,
this.Properties[i],
newValue, true);
}
} else {
this.removedProp[i] = 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];
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;
}