# HG changeset patch # User Mark Banner # Date 1516635672 0 # Node ID 9e0080c2930f005fbb90a583b72efd52d12af882 # Parent accdaf7d6cc06d19ceab0b160924a62768c107a6 Bug 1131491 - Remove old synchronous Place's transactions. r=mak MozReview-Commit-ID: FNCijeajgeh diff --git a/services/sync/tests/unit/test_bookmark_tracker.js b/services/sync/tests/unit/test_bookmark_tracker.js --- a/services/sync/tests/unit/test_bookmark_tracker.js +++ b/services/sync/tests/unit/test_bookmark_tracker.js @@ -1289,78 +1289,16 @@ add_task(async function test_async_onIte await verifyTrackedItems(["menu"]); Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 3); } finally { _("Clean up."); await cleanup(); } }); -add_task(async function test_onItemMoved_setItemIndex() { - _("Items with updated indices should be tracked"); - - try { - await stopTracking(); - - let folder_id = PlacesUtils.bookmarks.createFolder( - PlacesUtils.bookmarks.bookmarksMenuFolder, - "Test folder", - PlacesUtils.bookmarks.DEFAULT_INDEX); - let folder_guid = await engine._store.GUIDForId(folder_id); - _(`Folder GUID: ${folder_guid}`); - - let tb_id = PlacesUtils.bookmarks.insertBookmark( - folder_id, - CommonUtils.makeURI("http://getthunderbird.com"), - PlacesUtils.bookmarks.DEFAULT_INDEX, - "Thunderbird"); - let tb_guid = await engine._store.GUIDForId(tb_id); - _(`Thunderbird GUID: ${tb_guid}`); - - let fx_id = PlacesUtils.bookmarks.insertBookmark( - folder_id, - CommonUtils.makeURI("http://getfirefox.com"), - PlacesUtils.bookmarks.DEFAULT_INDEX, - "Firefox"); - let fx_guid = await engine._store.GUIDForId(fx_id); - _(`Firefox GUID: ${fx_guid}`); - - let moz_id = PlacesUtils.bookmarks.insertBookmark( - PlacesUtils.bookmarks.bookmarksMenuFolder, - CommonUtils.makeURI("https://mozilla.org"), - PlacesUtils.bookmarks.DEFAULT_INDEX, - "Mozilla" - ); - let moz_guid = await engine._store.GUIDForId(moz_id); - _(`Mozilla GUID: ${moz_guid}`); - - await startTracking(); - - // PlacesSortFolderByNameTransaction exercises - // PlacesUtils.bookmarks.setItemIndex. - let txn = new PlacesSortFolderByNameTransaction(folder_id); - - // We're reordering items within the same folder, so only the folder - // should be tracked. - _("Execute the sort folder transaction"); - txn.doTransaction(); - await verifyTrackedItems([folder_guid]); - Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE); - await resetTracker(); - - _("Undo the sort folder transaction"); - txn.undoTransaction(); - await verifyTrackedItems([folder_guid]); - Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE); - } finally { - _("Clean up."); - await cleanup(); - } -}); - add_task(async function test_onItemDeleted_removeFolderTransaction() { _("Folders removed in a transaction should be tracked"); try { await stopTracking(); _("Create a folder with two children"); let folder_id = PlacesUtils.bookmarks.createFolder( diff --git a/toolkit/components/places/PlacesUtils.jsm b/toolkit/components/places/PlacesUtils.jsm --- a/toolkit/components/places/PlacesUtils.jsm +++ b/toolkit/components/places/PlacesUtils.jsm @@ -1,34 +1,14 @@ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -var EXPORTED_SYMBOLS = [ - "PlacesUtils", - "PlacesAggregatedTransaction", - "PlacesCreateFolderTransaction", - "PlacesCreateBookmarkTransaction", - "PlacesCreateSeparatorTransaction", - "PlacesCreateLivemarkTransaction", - "PlacesMoveItemTransaction", - "PlacesRemoveItemTransaction", - "PlacesEditItemTitleTransaction", - "PlacesEditBookmarkURITransaction", - "PlacesSetItemAnnotationTransaction", - "PlacesSetPageAnnotationTransaction", - "PlacesEditBookmarkKeywordTransaction", - "PlacesEditBookmarkPostDataTransaction", - "PlacesEditItemDateAddedTransaction", - "PlacesEditItemLastModifiedTransaction", - "PlacesSortFolderByNameTransaction", - "PlacesTagURITransaction", - "PlacesUntagURITransaction" -]; +var EXPORTED_SYMBOLS = ["PlacesUtils"]; Cu.importGlobalProperties(["URL"]); ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); ChromeUtils.import("resource://gre/modules/AppConstants.jsm"); XPCOMUtils.defineLazyModuleGetters(this, { Services: "resource://gre/modules/Services.jsm", @@ -36,21 +16,16 @@ XPCOMUtils.defineLazyModuleGetters(this, OS: "resource://gre/modules/osfile.jsm", Sqlite: "resource://gre/modules/Sqlite.jsm", Bookmarks: "resource://gre/modules/Bookmarks.jsm", History: "resource://gre/modules/History.jsm", AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm", PlacesSyncUtils: "resource://gre/modules/PlacesSyncUtils.jsm", }); -// The minimum amount of transactions before starting a batch. Usually we do -// do incremental updates, a batch will cause views to completely -// refresh instead. -const MIN_TRANSACTIONS_FOR_BATCH = 5; - // On Mac OSX, the transferable system converts "\r\n" to "\n\n", where // we really just want "\n". On other platforms, the transferable system // converts "\r\n" to "\n". const NEWLINE = AppConstants.platform == "macosx" ? "\n" : "\r\n"; // Timers resolution is not always good, it can have a 16ms precision on Win. const TIMERS_RESOLUTION_SKEW_MS = 16; @@ -596,20 +571,17 @@ var PlacesUtils = { throw new Error(`${name}: The following properties were expected: ${[...required].join(", ")}`); return normalizedInput; }, BOOKMARK_VALIDATORS, SYNC_BOOKMARK_VALIDATORS, SYNC_CHANGE_RECORD_VALIDATORS, - QueryInterface: XPCOMUtils.generateQI([ - Ci.nsIObserver, - Ci.nsITransactionListener - ]), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), _shutdownFunctions: [], registerShutdownFunction: function PU_registerShutdownFunction(aFunc) { // If this is the first registered function, add the shutdown observer. if (this._shutdownFunctions.length == 0) { Services.obs.addObserver(this, this.TOPIC_SHUTDOWN); } this._shutdownFunctions.push(aFunc); @@ -625,57 +597,16 @@ var PlacesUtils = { } break; } }, onPageAnnotationSet() {}, onPageAnnotationRemoved() {}, - - // nsITransactionListener - - didDo: function PU_didDo(aManager, aTransaction, aDoResult) { - updateCommandsOnActiveWindow(); - }, - - didUndo: function PU_didUndo(aManager, aTransaction, aUndoResult) { - updateCommandsOnActiveWindow(); - }, - - didRedo: function PU_didRedo(aManager, aTransaction, aRedoResult) { - updateCommandsOnActiveWindow(); - }, - - didBeginBatch: function PU_didBeginBatch(aManager, aResult) { - // A no-op transaction is pushed to the stack, in order to make safe and - // easy to implement "Undo" an unknown number of transactions (including 0), - // "above" beginBatch and endBatch. Otherwise,implementing Undo that way - // head to dataloss: for example, if no changes were done in the - // edit-item panel, the last transaction on the undo stack would be the - // initial createItem transaction, or even worse, the batched editing of - // some other item. - // DO NOT MOVE this to the window scope, that would leak (bug 490068)! - this.transactionManager.doTransaction({ doTransaction() {}, - undoTransaction() {}, - redoTransaction() {}, - isTransient: false, - merge() { return false; } - }); - }, - - willDo: function PU_willDo() {}, - willUndo: function PU_willUndo() {}, - willRedo: function PU_willRedo() {}, - willBeginBatch: function PU_willBeginBatch() {}, - willEndBatch: function PU_willEndBatch() {}, - didEndBatch: function PU_didEndBatch() {}, - willMerge: function PU_willMerge() {}, - didMerge: function PU_didMerge() {}, - /** * Determines whether or not a ResultNode is a host container. * @param aNode * A result node * @returns true if the node is a host container, false otherwise */ nodeIsHost: function PU_nodeIsHost(aNode) { return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY && @@ -1989,48 +1920,16 @@ XPCOMUtils.defineLazyServiceGetter(Place "@mozilla.org/browser/livemark-service;2", "mozIAsyncLivemarks"); XPCOMUtils.defineLazyGetter(PlacesUtils, "keywords", () => { gKeywordsCachePromise.catch(Cu.reportError); return Keywords; }); -XPCOMUtils.defineLazyGetter(PlacesUtils, "transactionManager", function() { - let tm = Cc["@mozilla.org/transactionmanager;1"]. - createInstance(Ci.nsITransactionManager); - tm.AddListener(PlacesUtils); - this.registerShutdownFunction(function() { - // Clear all references to local transactions in the transaction manager, - // this prevents from leaking it. - this.transactionManager.RemoveListener(this); - this.transactionManager.clear(); - }); - - // Bug 750269 - // The transaction manager keeps strong references to transactions, and by - // that, also to the global for each transaction. A transaction, however, - // could be either the transaction itself (for which the global is this - // module) or some js-proxy in another global, usually a window. The later - // would leak because the transaction lifetime (in the manager's stacks) - // is independent of the global from which doTransaction was called. - // To avoid such a leak, we hide the native doTransaction from callers, - // and let each doTransaction call go through this module. - // Doing so ensures that, as long as the transaction is any of the - // PlacesXXXTransaction objects declared in this module, the object - // referenced by the transaction manager has the module itself as global. - return Object.create(tm, { - "doTransaction": { - value(aTransaction) { - tm.doTransaction(aTransaction); - } - } - }); -}); - XPCOMUtils.defineLazyGetter(this, "bundle", function() { const PLACES_STRING_BUNDLE_URI = "chrome://places/locale/places.properties"; return Services.strings.createBundle(PLACES_STRING_BUNDLE_URI); }); // This is just used as a reasonably-random value for copy & paste / drag operations. XPCOMUtils.defineLazyGetter(PlacesUtils, "instanceId", () => { return PlacesUtils.history.makeGuid(); @@ -2589,1237 +2488,16 @@ var GuidHelper = { PlacesUtils.bookmarks.addObserver(this.observer); PlacesUtils.registerShutdownFunction(() => { PlacesUtils.bookmarks.removeObserver(this.observer); }); } } }; -// Transactions handlers. - -/** - * Updates commands in the undo group of the active window commands. - * Inactive windows commands will be updated on focus. - */ -function updateCommandsOnActiveWindow() { - let win = Services.focus.activeWindow; - if (win && win instanceof Ci.nsIDOMWindow) { - // Updating "undo" will cause a group update including "redo". - win.updateCommands("undo"); - } -} - - -/** - * Used to cache bookmark information in transactions. - * - * @note To avoid leaks any non-primitive property should be copied. - * @note Used internally, DO NOT EXPORT. - */ -function TransactionItemCache() { -} - -TransactionItemCache.prototype = { - set id(v) { - this._id = (parseInt(v) > 0 ? v : null); - }, - get id() { - return this._id || -1; - }, - set parentId(v) { - this._parentId = (parseInt(v) > 0 ? v : null); - }, - get parentId() { - return this._parentId || -1; - }, - keyword: null, - title: null, - dateAdded: null, - lastModified: null, - postData: null, - itemType: null, - set uri(v) { - this._uri = (v instanceof Ci.nsIURI ? v.clone() : null); - }, - get uri() { - return this._uri || null; - }, - set feedURI(v) { - this._feedURI = (v instanceof Ci.nsIURI ? v.clone() : null); - }, - get feedURI() { - return this._feedURI || null; - }, - set siteURI(v) { - this._siteURI = (v instanceof Ci.nsIURI ? v.clone() : null); - }, - get siteURI() { - return this._siteURI || null; - }, - set index(v) { - this._index = (parseInt(v) >= 0 ? v : null); - }, - // Index can be 0. - get index() { - return this._index != null ? this._index : PlacesUtils.bookmarks.DEFAULT_INDEX; - }, - set annotations(v) { - this._annotations = Array.isArray(v) ? Cu.cloneInto(v, {}) : null; - }, - get annotations() { - return this._annotations || null; - }, - set tags(v) { - this._tags = (v && Array.isArray(v) ? Array.prototype.slice.call(v) : null); - }, - get tags() { - return this._tags || null; - }, -}; - - -/** - * Base transaction implementation. - * - * @note used internally, DO NOT EXPORT. - */ -function BaseTransaction() { -} - -BaseTransaction.prototype = { - name: null, - set childTransactions(v) { - this._childTransactions = (Array.isArray(v) ? Array.prototype.slice.call(v) : null); - }, - get childTransactions() { - return this._childTransactions || null; - }, - doTransaction: function BTXN_doTransaction() {}, - redoTransaction: function BTXN_redoTransaction() { - return this.doTransaction(); - }, - undoTransaction: function BTXN_undoTransaction() {}, - merge: function BTXN_merge() { - return false; - }, - get isTransient() { - return false; - }, - QueryInterface: XPCOMUtils.generateQI([ - Ci.nsITransaction - ]), -}; - - -/** - * 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 - * - * @return nsITransaction object - */ -this.PlacesAggregatedTransaction = - function PlacesAggregatedTransaction(aName, aTransactions) { - // Copy the transactions array to decouple it from its prototype, which - // otherwise keeps alive its associated global object. - this.childTransactions = aTransactions; - this.name = aName; - this.item = new TransactionItemCache(); - - // Check child transactions number. We will batch if we have more than - // MIN_TRANSACTIONS_FOR_BATCH total number of transactions. - let countTransactions = function(aTransactions, aTxnCount) { - for (let i = 0; - i < aTransactions.length && aTxnCount < MIN_TRANSACTIONS_FOR_BATCH; - ++i, ++aTxnCount) { - let txn = aTransactions[i]; - if (txn.childTransactions && txn.childTransactions.length > 0) - aTxnCount = countTransactions(txn.childTransactions, aTxnCount); - } - return aTxnCount; - }; - - let txnCount = countTransactions(this.childTransactions, 0); - this._useBatch = txnCount >= MIN_TRANSACTIONS_FOR_BATCH; -}; - -PlacesAggregatedTransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function ATXN_doTransaction() { - this._isUndo = false; - if (this._useBatch) - PlacesUtils.bookmarks.runInBatchMode(this, null); - else - this.runBatched(false); - }, - - undoTransaction: function ATXN_undoTransaction() { - this._isUndo = true; - if (this._useBatch) - PlacesUtils.bookmarks.runInBatchMode(this, null); - else - this.runBatched(true); - }, - - runBatched: function ATXN_runBatched() { - // Use a copy of the transactions array, so we won't reverse the original - // one on undoing. - let transactions = this.childTransactions.slice(0); - if (this._isUndo) - transactions.reverse(); - for (let i = 0; i < transactions.length; ++i) { - let txn = transactions[i]; - if (this.item.parentId != -1) - txn.item.parentId = this.item.parentId; - if (this._isUndo) - txn.undoTransaction(); - else - txn.doTransaction(); - } - } -}; - - -/** - * Transaction for creating a new folder. - * - * @param aTitle - * the title for the new folder - * @param aParentId - * the id of the parent folder in which the new folder should be added - * @param [optional] aIndex - * the index of the item in aParentId - * @param [optional] aAnnotations - * array of annotations to set for the new folder - * @param [optional] aChildTransactions - * array of transactions for items to be created in the new folder - * - * @return nsITransaction object - */ -this.PlacesCreateFolderTransaction = - function PlacesCreateFolderTransaction(aTitle, aParentId, aIndex, aAnnotations, - aChildTransactions) { - this.item = new TransactionItemCache(); - this.item.title = aTitle; - this.item.parentId = aParentId; - this.item.index = aIndex; - this.item.annotations = aAnnotations; - this.childTransactions = aChildTransactions; -}; - -PlacesCreateFolderTransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function CFTXN_doTransaction() { - this.item.id = PlacesUtils.bookmarks.createFolder(this.item.parentId, - this.item.title, - this.item.index); - if (this.item.annotations && this.item.annotations.length > 0) - PlacesUtils.setAnnotationsForItem(this.item.id, this.item.annotations); - - if (this.childTransactions && this.childTransactions.length > 0) { - // Set the new parent id into child transactions. - for (let i = 0; i < this.childTransactions.length; ++i) { - this.childTransactions[i].item.parentId = this.item.id; - } - - let txn = new PlacesAggregatedTransaction("Create folder childTxn", - this.childTransactions); - txn.doTransaction(); - } - }, - - undoTransaction: function CFTXN_undoTransaction() { - if (this.childTransactions && this.childTransactions.length > 0) { - let txn = new PlacesAggregatedTransaction("Create folder childTxn", - this.childTransactions); - txn.undoTransaction(); - } - - // Remove item only after all child transactions have been reverted. - PlacesUtils.bookmarks.removeItem(this.item.id); - } -}; - - -/** - * Transaction for creating a new bookmark. - * - * @param aURI - * the nsIURI of the new bookmark - * @param aParentId - * the id of the folder in which the bookmark should be added. - * @param [optional] aIndex - * the index of the item in aParentId - * @param [optional] aTitle - * the title of the new bookmark - * @param [optional] aKeyword - * the keyword for the new bookmark - * @param [optional] aAnnotations - * array of 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. - * @param [optional] aPostData - * keyword's POST data, if available. - * - * @return nsITransaction object - */ -this.PlacesCreateBookmarkTransaction = - function PlacesCreateBookmarkTransaction(aURI, aParentId, aIndex, aTitle, - aKeyword, aAnnotations, - aChildTransactions, aPostData) { - this.item = new TransactionItemCache(); - this.item.uri = aURI; - this.item.parentId = aParentId; - this.item.index = aIndex; - this.item.title = aTitle; - this.item.keyword = aKeyword; - this.item.postData = aPostData; - this.item.annotations = aAnnotations; - this.childTransactions = aChildTransactions; -}; - -PlacesCreateBookmarkTransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function CITXN_doTransaction() { - this.item.id = PlacesUtils.bookmarks.insertBookmark(this.item.parentId, - this.item.uri, - this.item.index, - this.item.title); - if (this.item.keyword) { - PlacesUtils.bookmarks.setKeywordForBookmark(this.item.id, - this.item.keyword); - if (this.item.postData) { - PlacesUtils.setPostDataForBookmark(this.item.id, - this.item.postData); - } - } - if (this.item.annotations && this.item.annotations.length > 0) - PlacesUtils.setAnnotationsForItem(this.item.id, this.item.annotations); - - if (this.childTransactions && this.childTransactions.length > 0) { - // Set the new item id into child transactions. - for (let i = 0; i < this.childTransactions.length; ++i) { - this.childTransactions[i].item.id = this.item.id; - } - let txn = new PlacesAggregatedTransaction("Create item childTxn", - this.childTransactions); - txn.doTransaction(); - } - }, - - undoTransaction: function CITXN_undoTransaction() { - if (this.childTransactions && this.childTransactions.length > 0) { - // Undo transactions should always be done in reverse order. - let txn = new PlacesAggregatedTransaction("Create item childTxn", - this.childTransactions); - txn.undoTransaction(); - } - - // Remove item only after all child transactions have been reverted. - PlacesUtils.bookmarks.removeItem(this.item.id); - } -}; - - -/** - * Transaction for creating a new separator. - * - * @param aParentId - * the id of the folder in which the separator should be added - * @param [optional] aIndex - * the index of the item in aParentId - * - * @return nsITransaction object - */ -this.PlacesCreateSeparatorTransaction = - function PlacesCreateSeparatorTransaction(aParentId, aIndex) { - this.item = new TransactionItemCache(); - this.item.parentId = aParentId; - this.item.index = aIndex; -}; - -PlacesCreateSeparatorTransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function CSTXN_doTransaction() { - this.item.id = - PlacesUtils.bookmarks.insertSeparator(this.item.parentId, this.item.index); - }, - - undoTransaction: function CSTXN_undoTransaction() { - PlacesUtils.bookmarks.removeItem(this.item.id); - } -}; - - -/** - * Transaction for creating a new livemark item. - * - * @see mozIAsyncLivemarks for documentation regarding the arguments. - * - * @param aFeedURI - * nsIURI of the feed - * @param [optional] aSiteURI - * nsIURI of the page serving the feed - * @param aTitle - * title for the livemark - * @param aParentId - * the id of the folder in which the livemark should be added - * @param [optional] aIndex - * the index of the livemark in aParentId - * @param [optional] aAnnotations - * array of annotations to set for the new livemark. - * - * @return nsITransaction object - */ -this.PlacesCreateLivemarkTransaction = - function PlacesCreateLivemarkTransaction(aFeedURI, aSiteURI, aTitle, aParentId, - aIndex, aAnnotations) { - this.item = new TransactionItemCache(); - this.item.feedURI = aFeedURI; - this.item.siteURI = aSiteURI; - this.item.title = aTitle; - this.item.parentId = aParentId; - this.item.index = aIndex; - this.item.annotations = aAnnotations; -}; - -PlacesCreateLivemarkTransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function CLTXN_doTransaction() { - this._promise = PlacesUtils.livemarks.addLivemark( - { title: this.item.title, - feedURI: this.item.feedURI, - parentId: this.item.parentId, - index: this.item.index, - siteURI: this.item.siteURI - }).then(aLivemark => { - this.item.id = aLivemark.id; - if (this.item.annotations && this.item.annotations.length > 0) { - PlacesUtils.setAnnotationsForItem(this.item.id, - this.item.annotations); - } - }, Cu.reportError); - }, - - undoTransaction: function CLTXN_undoTransaction() { - // The getLivemark callback may fail, but it is used just to serialize, - // so it doesn't matter. - this._promise = PlacesUtils.livemarks.getLivemark({ id: this.item.id }) - .catch(() => {}).then(() => { - PlacesUtils.bookmarks.removeItem(this.item.id); - }); - } -}; - - -/** - * Transaction for removing a livemark item. - * - * @param aLivemarkId - * the identifier of the folder for the livemark. - * - * @return nsITransaction object - * @note used internally by PlacesRemoveItemTransaction, DO NOT EXPORT. - */ -function PlacesRemoveLivemarkTransaction(aLivemarkId) { - this.item = new TransactionItemCache(); - this.item.id = aLivemarkId; - this.item.title = PlacesUtils.bookmarks.getItemTitle(this.item.id); - this.item.parentId = PlacesUtils.bookmarks.getFolderIdForItem(this.item.id); - - let annos = PlacesUtils.getAnnotationsForItem(this.item.id); - // Exclude livemark service annotations, those will be recreated automatically - let annosToExclude = [PlacesUtils.LMANNO_FEEDURI, - PlacesUtils.LMANNO_SITEURI]; - this.item.annotations = annos.filter(function(aValue, aIndex, aArray) { - return !annosToExclude.includes(aValue.name); - }); - this.item.dateAdded = PlacesUtils.bookmarks.getItemDateAdded(this.item.id); - this.item.lastModified = - PlacesUtils.bookmarks.getItemLastModified(this.item.id); -} - -PlacesRemoveLivemarkTransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function RLTXN_doTransaction() { - PlacesUtils.livemarks.getLivemark({ id: this.item.id }) - .then(aLivemark => { - this.item.feedURI = aLivemark.feedURI; - this.item.siteURI = aLivemark.siteURI; - PlacesUtils.bookmarks.removeItem(this.item.id); - }, Cu.reportError); - }, - - undoTransaction: function RLTXN_undoTransaction() { - // Undo work must be serialized, otherwise won't be able to know the - // feedURI and siteURI of the livemark. - // The getLivemark callback is expected to receive a failure status but it - // is used just to serialize, so doesn't matter. - PlacesUtils.livemarks.getLivemark({ id: this.item.id }) - .catch(() => { - PlacesUtils.livemarks.addLivemark({ parentId: this.item.parentId, - title: this.item.title, - siteURI: this.item.siteURI, - feedURI: this.item.feedURI, - index: this.item.index, - lastModified: this.item.lastModified - }).then( - aLivemark => { - let itemId = aLivemark.id; - PlacesUtils.bookmarks.setItemDateAdded(itemId, this.item.dateAdded); - PlacesUtils.setAnnotationsForItem(itemId, this.item.annotations); - }, Cu.reportError); - }); - } -}; - - -/** - * Transaction for moving an Item. - * - * @param aItemId - * the id of the item to move - * @param aNewParentId - * id of the new parent to move to - * @param aNewIndex - * index of the new position to move to - * - * @return nsITransaction object - */ -this.PlacesMoveItemTransaction = - function PlacesMoveItemTransaction(aItemId, aNewParentId, aNewIndex) { - this.item = new TransactionItemCache(); - this.item.id = aItemId; - this.item.parentId = PlacesUtils.bookmarks.getFolderIdForItem(this.item.id); - this.new = new TransactionItemCache(); - this.new.parentId = aNewParentId; - this.new.index = aNewIndex; -}; - -PlacesMoveItemTransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function MITXN_doTransaction() { - this.item.index = PlacesUtils.bookmarks.getItemIndex(this.item.id); - PlacesUtils.bookmarks.moveItem(this.item.id, - this.new.parentId, this.new.index); - this._undoIndex = PlacesUtils.bookmarks.getItemIndex(this.item.id); - }, - - undoTransaction: function MITXN_undoTransaction() { - // moving down in the same parent takes in count removal of the item - // so to revert positions we must move to oldIndex + 1 - if (this.new.parentId == this.item.parentId && - this.item.index > this._undoIndex) { - PlacesUtils.bookmarks.moveItem(this.item.id, this.item.parentId, - this.item.index + 1); - } else { - PlacesUtils.bookmarks.moveItem(this.item.id, this.item.parentId, - this.item.index); - } - } -}; - - -/** - * Transaction for removing an Item - * - * @param aItemId - * id of the item to remove - * - * @return nsITransaction object - */ -this.PlacesRemoveItemTransaction = - function PlacesRemoveItemTransaction(aItemId) { - if (PlacesUtils.isRootItem(aItemId)) - throw Cr.NS_ERROR_INVALID_ARG; - - // if the item lives within a tag container, use the tagging transactions - let parent = PlacesUtils.bookmarks.getFolderIdForItem(aItemId); - let grandparent = PlacesUtils.bookmarks.getFolderIdForItem(parent); - if (grandparent == PlacesUtils.tagsFolderId) { - let uri = PlacesUtils.bookmarks.getBookmarkURI(aItemId); - return new PlacesUntagURITransaction(uri, [parent]); - } - - // if the item is a livemark container we will not save its children. - if (PlacesUtils.annotations.itemHasAnnotation(aItemId, - PlacesUtils.LMANNO_FEEDURI)) - return new PlacesRemoveLivemarkTransaction(aItemId); - - this.item = new TransactionItemCache(); - this.item.id = aItemId; - this.item.itemType = PlacesUtils.bookmarks.getItemType(this.item.id); - if (this.item.itemType == Ci.nsINavBookmarksService.TYPE_FOLDER) { - this.childTransactions = this._getFolderContentsTransactions(); - // Remove this folder itself. - let txn = PlacesUtils.bookmarks.getRemoveFolderTransaction(this.item.id); - this.childTransactions.push(txn); - } else if (this.item.itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK) { - this.item.uri = PlacesUtils.bookmarks.getBookmarkURI(this.item.id); - this.item.keyword = - PlacesUtils.bookmarks.getKeywordForBookmark(this.item.id); - if (this.item.keyword) - this.item.postData = PlacesUtils.getPostDataForBookmark(this.item.id); - } - - if (this.item.itemType != Ci.nsINavBookmarksService.TYPE_SEPARATOR) - this.item.title = PlacesUtils.bookmarks.getItemTitle(this.item.id); - - this.item.parentId = PlacesUtils.bookmarks.getFolderIdForItem(this.item.id); - this.item.annotations = PlacesUtils.getAnnotationsForItem(this.item.id); - this.item.dateAdded = PlacesUtils.bookmarks.getItemDateAdded(this.item.id); - this.item.lastModified = - PlacesUtils.bookmarks.getItemLastModified(this.item.id); -}; - -PlacesRemoveItemTransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function RITXN_doTransaction() { - this.item.index = PlacesUtils.bookmarks.getItemIndex(this.item.id); - - if (this.item.itemType == Ci.nsINavBookmarksService.TYPE_FOLDER) { - let txn = new PlacesAggregatedTransaction("Remove item childTxn", - this.childTransactions); - txn.doTransaction(); - } else { - // Before removing the bookmark, save its tags. - let tags = this.item.uri ? - PlacesUtils.tagging.getTagsForURI(this.item.uri) : null; - - PlacesUtils.bookmarks.removeItem(this.item.id); - - // If this was the last bookmark (excluding tag-items) for this url, - // persist the tags. - if (tags && PlacesUtils.getMostRecentBookmarkForURI(this.item.uri) == -1) { - this.item.tags = tags; - } - } - }, - - undoTransaction: function RITXN_undoTransaction() { - if (this.item.itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK) { - this.item.id = PlacesUtils.bookmarks.insertBookmark(this.item.parentId, - this.item.uri, - this.item.index, - this.item.title); - if (this.item.tags && this.item.tags.length > 0) - PlacesUtils.tagging.tagURI(this.item.uri, this.item.tags); - if (this.item.keyword) { - PlacesUtils.bookmarks.setKeywordForBookmark(this.item.id, - this.item.keyword); - if (this.item.postData) { - PlacesUtils.bookmarks.setPostDataForBookmark(this.item.id); - } - } - } else if (this.item.itemType == Ci.nsINavBookmarksService.TYPE_FOLDER) { - let txn = new PlacesAggregatedTransaction("Remove item childTxn", - this.childTransactions); - txn.undoTransaction(); - } else { // TYPE_SEPARATOR - this.item.id = PlacesUtils.bookmarks.insertSeparator(this.item.parentId, - this.item.index); - } - - if (this.item.annotations && this.item.annotations.length > 0) - PlacesUtils.setAnnotationsForItem(this.item.id, this.item.annotations); - - PlacesUtils.bookmarks.setItemDateAdded(this.item.id, this.item.dateAdded); - PlacesUtils.bookmarks.setItemLastModified(this.item.id, - this.item.lastModified); - }, - - /** - * Returns a flat, ordered list of transactions for a depth-first recreation - * of items within this folder. - */ - _getFolderContentsTransactions: - function RITXN__getFolderContentsTransactions() { - let transactions = []; - let contents = - PlacesUtils.getFolderContents(this.item.id, false, false).root; - for (let i = 0; i < contents.childCount; ++i) { - let txn = new PlacesRemoveItemTransaction(contents.getChild(i).itemId); - transactions.push(txn); - } - contents.containerOpen = false; - // Reverse transactions to preserve parent-child relationship. - return transactions.reverse(); - } -}; - - -/** - * Transaction for editting a bookmark's title. - * - * @param aItemId - * id of the item to edit - * @param aNewTitle - * new title for the item to edit - * - * @return nsITransaction object - */ -this.PlacesEditItemTitleTransaction = - function PlacesEditItemTitleTransaction(aItemId, aNewTitle) { - this.item = new TransactionItemCache(); - this.item.id = aItemId; - this.new = new TransactionItemCache(); - this.new.title = aNewTitle; -}; - -PlacesEditItemTitleTransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function EITTXN_doTransaction() { - this.item.title = PlacesUtils.bookmarks.getItemTitle(this.item.id); - PlacesUtils.bookmarks.setItemTitle(this.item.id, this.new.title); - }, - - undoTransaction: function EITTXN_undoTransaction() { - PlacesUtils.bookmarks.setItemTitle(this.item.id, this.item.title); - } -}; - - -/** - * Transaction for editing a bookmark's uri. - * - * @param aItemId - * id of the bookmark to edit - * @param aNewURI - * new uri for the bookmark - * - * @return nsITransaction object - */ -this.PlacesEditBookmarkURITransaction = - function PlacesEditBookmarkURITransaction(aItemId, aNewURI) { - this.item = new TransactionItemCache(); - this.item.id = aItemId; - this.new = new TransactionItemCache(); - this.new.uri = aNewURI; -}; - -PlacesEditBookmarkURITransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function EBUTXN_doTransaction() { - this.item.uri = PlacesUtils.bookmarks.getBookmarkURI(this.item.id); - PlacesUtils.bookmarks.changeBookmarkURI(this.item.id, this.new.uri); - // move tags from old URI to new URI - this.item.tags = PlacesUtils.tagging.getTagsForURI(this.item.uri); - if (this.item.tags.length > 0) { - // only untag the old URI if this is the only bookmark - if (PlacesUtils.getBookmarksForURI(this.item.uri, {}).length == 0) - PlacesUtils.tagging.untagURI(this.item.uri, this.item.tags); - PlacesUtils.tagging.tagURI(this.new.uri, this.item.tags); - } - }, - - undoTransaction: function EBUTXN_undoTransaction() { - PlacesUtils.bookmarks.changeBookmarkURI(this.item.id, this.item.uri); - // move tags from new URI to old URI - if (this.item.tags.length > 0) { - // only untag the new URI if this is the only bookmark - if (PlacesUtils.getBookmarksForURI(this.new.uri, {}).length == 0) - PlacesUtils.tagging.untagURI(this.new.uri, this.item.tags); - PlacesUtils.tagging.tagURI(this.item.uri, this.item.tags); - } - } -}; - - -/** - * Transaction for setting/unsetting an item annotation - * - * @param aItemId - * id of the item where to set annotation - * @param aAnnotationObject - * Object representing an annotation, containing the following - * properties: name, flags, expires, value. - * If value is null the annotation will be removed - * - * @return nsITransaction object - */ -this.PlacesSetItemAnnotationTransaction = - function PlacesSetItemAnnotationTransaction(aItemId, aAnnotationObject) { - this.item = new TransactionItemCache(); - this.item.id = aItemId; - this.new = new TransactionItemCache(); - this.new.annotations = [aAnnotationObject]; -}; - -PlacesSetItemAnnotationTransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function SIATXN_doTransaction() { - let annoName = this.new.annotations[0].name; - if (PlacesUtils.annotations.itemHasAnnotation(this.item.id, annoName)) { - // fill the old anno if it is set - let flags = {}, expires = {}, type = {}; - PlacesUtils.annotations.getItemAnnotationInfo(this.item.id, annoName, flags, - expires, type); - let value = PlacesUtils.annotations.getItemAnnotation(this.item.id, - annoName); - this.item.annotations = [{ name: annoName, - type: type.value, - flags: flags.value, - value, - expires: expires.value }]; - } else { - // create an empty old anno - this.item.annotations = [{ name: annoName, - flags: 0, - value: null, - expires: Ci.nsIAnnotationService.EXPIRE_NEVER }]; - } - - PlacesUtils.setAnnotationsForItem(this.item.id, this.new.annotations); - }, - - undoTransaction: function SIATXN_undoTransaction() { - PlacesUtils.setAnnotationsForItem(this.item.id, this.item.annotations); - } -}; - - -/** - * Transaction for setting/unsetting a page annotation - * - * @param aURI - * URI of the page where to set annotation - * @param aAnnotationObject - * Object representing an annotation, containing the following - * properties: name, flags, expires, value. - * If value is null the annotation will be removed - * - * @return nsITransaction object - */ -this.PlacesSetPageAnnotationTransaction = - function PlacesSetPageAnnotationTransaction(aURI, aAnnotationObject) { - this.item = new TransactionItemCache(); - this.item.uri = aURI; - this.new = new TransactionItemCache(); - this.new.annotations = [aAnnotationObject]; -}; - -PlacesSetPageAnnotationTransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function SPATXN_doTransaction() { - let annoName = this.new.annotations[0].name; - if (PlacesUtils.annotations.pageHasAnnotation(this.item.uri, annoName)) { - // fill the old anno if it is set - let flags = {}, expires = {}, type = {}; - PlacesUtils.annotations.getPageAnnotationInfo(this.item.uri, annoName, flags, - expires, type); - let value = PlacesUtils.annotations.getPageAnnotation(this.item.uri, - annoName); - this.item.annotations = [{ name: annoName, - flags: flags.value, - value, - expires: expires.value }]; - } else { - // create an empty old anno - this.item.annotations = [{ name: annoName, - type: Ci.nsIAnnotationService.TYPE_STRING, - flags: 0, - value: null, - expires: Ci.nsIAnnotationService.EXPIRE_NEVER }]; - } - - PlacesUtils.setAnnotationsForURI(this.item.uri, this.new.annotations); - }, - - undoTransaction: function SPATXN_undoTransaction() { - PlacesUtils.setAnnotationsForURI(this.item.uri, this.item.annotations); - } -}; - - -/** - * Transaction for editing a bookmark's keyword. - * - * @param aItemId - * id of the bookmark to edit - * @param aNewKeyword - * new keyword for the bookmark - * @param aNewPostData [optional] - * new keyword's POST data, if available - * @param aOldKeyword [optional] - * old keyword of the bookmark - * - * @return nsITransaction object - */ -this.PlacesEditBookmarkKeywordTransaction = - function PlacesEditBookmarkKeywordTransaction(aItemId, aNewKeyword, - aNewPostData, aOldKeyword) { - this.item = new TransactionItemCache(); - this.item.id = aItemId; - this.item.keyword = aOldKeyword; - this.item.href = (PlacesUtils.bookmarks.getBookmarkURI(aItemId)).spec; - this.new = new TransactionItemCache(); - this.new.keyword = aNewKeyword; - this.new.postData = aNewPostData; -}; - -PlacesEditBookmarkKeywordTransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function EBKTXN_doTransaction() { - let done = false; - (async () => { - if (this.item.keyword) { - let oldEntry = await PlacesUtils.keywords.fetch(this.item.keyword); - this.item.postData = oldEntry.postData; - await PlacesUtils.keywords.remove(this.item.keyword); - } - - if (this.new.keyword) { - await PlacesUtils.keywords.insert({ - url: this.item.href, - keyword: this.new.keyword, - postData: this.new.postData || this.item.postData - }); - } - })().catch(Cu.reportError) - .then(() => done = true); - // TODO: Until we can move to PlacesTransactions.jsm, we must spin the - // events loop :( - Services.tm.spinEventLoopUntil(() => done); - }, - - undoTransaction: function EBKTXN_undoTransaction() { - - let done = false; - (async () => { - if (this.new.keyword) { - await PlacesUtils.keywords.remove(this.new.keyword); - } - - if (this.item.keyword) { - await PlacesUtils.keywords.insert({ - url: this.item.href, - keyword: this.item.keyword, - postData: this.item.postData - }); - } - })().catch(Cu.reportError) - .then(() => done = true); - // TODO: Until we can move to PlacesTransactions.jsm, we must spin the - // events loop :( - Services.tm.spinEventLoopUntil(() => { - return done; - }); - } -}; - - -/** - * Transaction for editing the post data associated with a bookmark. - * - * @param aItemId - * id of the bookmark to edit - * @param aPostData - * post data - * - * @return nsITransaction object - */ -this.PlacesEditBookmarkPostDataTransaction = - function PlacesEditBookmarkPostDataTransaction(aItemId, aPostData) { - this.item = new TransactionItemCache(); - this.item.id = aItemId; - this.new = new TransactionItemCache(); - this.new.postData = aPostData; -}; - -PlacesEditBookmarkPostDataTransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction() { - // Setting null postData is not supported by the current schema. - if (this.new.postData) { - this.item.postData = PlacesUtils.getPostDataForBookmark(this.item.id); - PlacesUtils.setPostDataForBookmark(this.item.id, this.new.postData); - } - }, - - undoTransaction() { - // Setting null postData is not supported by the current schema. - if (this.item.postData) { - PlacesUtils.setPostDataForBookmark(this.item.id, this.item.postData); - } - } -}; - - -/** - * Transaction for editing an item's date added property. - * - * @param aItemId - * id of the item to edit - * @param aNewDateAdded - * new date added for the item - * - * @return nsITransaction object - */ -this.PlacesEditItemDateAddedTransaction = - function PlacesEditItemDateAddedTransaction(aItemId, aNewDateAdded) { - this.item = new TransactionItemCache(); - this.item.id = aItemId; - this.new = new TransactionItemCache(); - this.new.dateAdded = aNewDateAdded; -}; - -PlacesEditItemDateAddedTransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function EIDATXN_doTransaction() { - // Child transactions have the id set as parentId. - if (this.item.id == -1 && this.item.parentId != -1) - this.item.id = this.item.parentId; - this.item.dateAdded = - PlacesUtils.bookmarks.getItemDateAdded(this.item.id); - PlacesUtils.bookmarks.setItemDateAdded(this.item.id, this.new.dateAdded); - }, - - undoTransaction: function EIDATXN_undoTransaction() { - PlacesUtils.bookmarks.setItemDateAdded(this.item.id, this.item.dateAdded); - } -}; - - -/** - * Transaction for editing an item's last modified time. - * - * @param aItemId - * id of the item to edit - * @param aNewLastModified - * new last modified date for the item - * - * @return nsITransaction object - */ -this.PlacesEditItemLastModifiedTransaction = - function PlacesEditItemLastModifiedTransaction(aItemId, aNewLastModified) { - this.item = new TransactionItemCache(); - this.item.id = aItemId; - this.new = new TransactionItemCache(); - this.new.lastModified = aNewLastModified; -}; - -PlacesEditItemLastModifiedTransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: - function EILMTXN_doTransaction() { - // Child transactions have the id set as parentId. - if (this.item.id == -1 && this.item.parentId != -1) - this.item.id = this.item.parentId; - this.item.lastModified = - PlacesUtils.bookmarks.getItemLastModified(this.item.id); - PlacesUtils.bookmarks.setItemLastModified(this.item.id, - this.new.lastModified); - }, - - undoTransaction: - function EILMTXN_undoTransaction() { - PlacesUtils.bookmarks.setItemLastModified(this.item.id, - this.item.lastModified); - } -}; - - -/** - * Transaction for sorting a folder by name - * - * @param aFolderId - * id of the folder to sort - * - * @return nsITransaction object - */ -this.PlacesSortFolderByNameTransaction = - function PlacesSortFolderByNameTransaction(aFolderId) { - this.item = new TransactionItemCache(); - this.item.id = aFolderId; -}; - -PlacesSortFolderByNameTransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function SFBNTXN_doTransaction() { - this._oldOrder = []; - - let contents = - PlacesUtils.getFolderContents(this.item.id, false, false).root; - let count = contents.childCount; - - // sort between separators - let newOrder = []; - let preSep = []; // temporary array for sorting each group of items - let sortingMethod = - function(a, b) { - if (PlacesUtils.nodeIsContainer(a) && !PlacesUtils.nodeIsContainer(b)) - return -1; - if (!PlacesUtils.nodeIsContainer(a) && PlacesUtils.nodeIsContainer(b)) - return 1; - return a.title.localeCompare(b.title); - }; - - for (let i = 0; i < count; ++i) { - let 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, preSep.length); - } - newOrder.push(item); - } else - preSep.push(item); - } - contents.containerOpen = false; - - if (preSep.length > 0) { - preSep.sort(sortingMethod); - newOrder = newOrder.concat(preSep); - } - - // set the nex indexes - let callback = { - runBatched() { - for (let i = 0; i < newOrder.length; ++i) { - PlacesUtils.bookmarks.setItemIndex(newOrder[i].itemId, i); - } - } - }; - PlacesUtils.bookmarks.runInBatchMode(callback, null); - }, - - undoTransaction: function SFBNTXN_undoTransaction() { - let callback = { - _self: this, - runBatched() { - for (let item in this._self._oldOrder) - PlacesUtils.bookmarks.setItemIndex(item, this._self._oldOrder[item]); - } - }; - PlacesUtils.bookmarks.runInBatchMode(callback, null); - } -}; - - -/** - * Transaction for tagging a URL with the given set of tags. Current tags set - * for the URL persist. It's the caller's job to check whether or not aURI - * was already tagged by any of the tags in aTags, undoing this tags - * transaction removes them all from aURL! - * - * @param aURI - * the URL to tag. - * @param aTags - * Array of tags to set for the given URL. - */ -this.PlacesTagURITransaction = - function PlacesTagURITransaction(aURI, aTags) { - this.item = new TransactionItemCache(); - this.item.uri = aURI; - this.item.tags = aTags; -}; - -PlacesTagURITransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function TUTXN_doTransaction() { - if (PlacesUtils.getMostRecentBookmarkForURI(this.item.uri) == -1) { - // There is no bookmark for this uri, but we only allow to tag bookmarks. - // Force an unfiled bookmark first. - this.item.id = - PlacesUtils.bookmarks - .insertBookmark(PlacesUtils.unfiledBookmarksFolderId, - this.item.uri, - PlacesUtils.bookmarks.DEFAULT_INDEX, - PlacesUtils.history.getPageTitle(this.item.uri)); - } - PlacesUtils.tagging.tagURI(this.item.uri, this.item.tags); - }, - - undoTransaction: function TUTXN_undoTransaction() { - if (this.item.id != -1) { - PlacesUtils.bookmarks.removeItem(this.item.id); - this.item.id = -1; - } - PlacesUtils.tagging.untagURI(this.item.uri, this.item.tags); - } -}; - - -/** - * Transaction for removing tags from a URL. It's the caller's job to check - * whether or not aURI isn't tagged by any of the tags in aTags, undoing this - * tags transaction adds them all to aURL! - * - * @param aURI - * the URL to un-tag. - * @param aTags - * Array of tags to unset. pass null to remove all tags from the given - * url. - */ -this.PlacesUntagURITransaction = - function PlacesUntagURITransaction(aURI, aTags) { - this.item = new TransactionItemCache(); - this.item.uri = aURI; - if (aTags) { - // Within this transaction, we cannot rely on tags given by itemId - // since the tag containers may be gone after we call untagURI. - // Thus, we convert each tag given by its itemId to name. - let tags = []; - for (let i = 0; i < aTags.length; ++i) { - if (typeof(aTags[i]) == "number") - tags.push(PlacesUtils.bookmarks.getItemTitle(aTags[i])); - else - tags.push(aTags[i]); - } - this.item.tags = tags; - } -}; - -PlacesUntagURITransaction.prototype = { - __proto__: BaseTransaction.prototype, - - doTransaction: function UTUTXN_doTransaction() { - // Filter tags existing on the bookmark, otherwise on undo we may try to - // set nonexistent tags. - let tags = PlacesUtils.tagging.getTagsForURI(this.item.uri); - this.item.tags = this.item.tags.filter(function(aTag) { - return tags.includes(aTag); - }); - PlacesUtils.tagging.untagURI(this.item.uri, this.item.tags); - }, - - undoTransaction: function UTUTXN_undoTransaction() { - PlacesUtils.tagging.tagURI(this.item.uri, this.item.tags); - } -}; - /** * Executes a boolean validate function, throwing if it returns false. * * @param boolValidateFn * A boolean validate function. * @return the input value. * @throws if input doesn't pass the validate function. */ diff --git a/toolkit/components/places/tests/legacy/test_placesTxn.js b/toolkit/components/places/tests/legacy/test_placesTxn.js deleted file mode 100644 --- a/toolkit/components/places/tests/legacy/test_placesTxn.js +++ /dev/null @@ -1,935 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var bmsvc = PlacesUtils.bookmarks; -var tagssvc = PlacesUtils.tagging; -var annosvc = PlacesUtils.annotations; -var txnManager = PlacesUtils.transactionManager; -const DESCRIPTION_ANNO = "bookmarkProperties/description"; - -async function promiseKeyword(keyword, href, postData) { - while (true) { - let entry = await PlacesUtils.keywords.fetch(keyword); - if (href == null && !entry) - break; - if (entry && entry.url.href == href && entry.postData == postData) { - break; - } - - await new Promise(resolve => do_timeout(100, resolve)); - } -} - -// create and add bookmarks observer -var observer = { - - onBeginUpdateBatch() { - this._beginUpdateBatch = true; - }, - _beginUpdateBatch: false, - - onEndUpdateBatch() { - this._endUpdateBatch = true; - }, - _endUpdateBatch: false, - - onItemAdded(id, folder, index, itemType, uri) { - this._itemAddedId = id; - this._itemAddedParent = folder; - this._itemAddedIndex = index; - this._itemAddedType = itemType; - }, - _itemAddedId: null, - _itemAddedParent: null, - _itemAddedIndex: null, - _itemAddedType: null, - - onItemRemoved(id, folder, index, itemType) { - this._itemRemovedId = id; - this._itemRemovedFolder = folder; - this._itemRemovedIndex = index; - }, - _itemRemovedId: null, - _itemRemovedFolder: null, - _itemRemovedIndex: null, - - onItemChanged(id, property, isAnnotationProperty, newValue, - lastModified, itemType) { - // The transaction manager is being rewritten in bug 891303, so just - // skip checking this for now. - if (property == "tags") - return; - this._itemChangedId = id; - this._itemChangedProperty = property; - this._itemChanged_isAnnotationProperty = isAnnotationProperty; - this._itemChangedValue = newValue; - }, - _itemChangedId: null, - _itemChangedProperty: null, - _itemChanged_isAnnotationProperty: null, - _itemChangedValue: null, - - onItemVisited(id, visitID, time) { - this._itemVisitedId = id; - this._itemVisitedVistId = visitID; - this._itemVisitedTime = time; - }, - _itemVisitedId: null, - _itemVisitedVistId: null, - _itemVisitedTime: null, - - onItemMoved(id, oldParent, oldIndex, newParent, newIndex, - itemType) { - this._itemMovedId = id; - this._itemMovedOldParent = oldParent; - this._itemMovedOldIndex = oldIndex; - this._itemMovedNewParent = newParent; - this._itemMovedNewIndex = newIndex; - }, - _itemMovedId: null, - _itemMovedOldParent: null, - _itemMovedOldIndex: null, - _itemMovedNewParent: null, - _itemMovedNewIndex: null, - - QueryInterface(iid) { - if (iid.equals(Ci.nsINavBookmarkObserver) || - iid.equals(Ci.nsISupports)) { - return this; - } - throw Cr.NS_ERROR_NO_INTERFACE; - } -}; - -// index at which items should begin -var bmStartIndex = 0; - -// get bookmarks root id -var root = PlacesUtils.bookmarksMenuFolderId; - -add_task(async function init() { - bmsvc.addObserver(observer); - registerCleanupFunction(function() { - bmsvc.removeObserver(observer); - }); -}); - -add_task(async function test_create_folder_with_description() { - const TEST_FOLDERNAME = "Test creating a folder with a description"; - const TEST_DESCRIPTION = "this is my test description"; - - let annos = [{ name: DESCRIPTION_ANNO, - type: annosvc.TYPE_STRING, - flags: 0, - value: TEST_DESCRIPTION, - expires: annosvc.EXPIRE_NEVER }]; - let txn = new PlacesCreateFolderTransaction(TEST_FOLDERNAME, root, bmStartIndex, annos); - txnManager.doTransaction(txn); - - // This checks that calling undoTransaction on an "empty batch" doesn't - // undo the previous transaction (getItemTitle will fail) - txnManager.beginBatch(null); - txnManager.endBatch(false); - txnManager.undoTransaction(); - - let folderId = observer._itemAddedId; - Assert.equal(bmsvc.getItemTitle(folderId), TEST_FOLDERNAME); - Assert.equal(observer._itemAddedIndex, bmStartIndex); - Assert.equal(observer._itemAddedParent, root); - Assert.equal(observer._itemAddedId, folderId); - Assert.equal(TEST_DESCRIPTION, annosvc.getItemAnnotation(folderId, DESCRIPTION_ANNO)); - - txn.undoTransaction(); - Assert.equal(observer._itemRemovedId, folderId); - Assert.equal(observer._itemRemovedFolder, root); - Assert.equal(observer._itemRemovedIndex, bmStartIndex); - - txn.redoTransaction(); - Assert.equal(observer._itemAddedIndex, bmStartIndex); - Assert.equal(observer._itemAddedParent, root); - Assert.equal(observer._itemAddedId, folderId); - - txn.undoTransaction(); - Assert.equal(observer._itemRemovedId, folderId); - Assert.equal(observer._itemRemovedFolder, root); - Assert.equal(observer._itemRemovedIndex, bmStartIndex); -}); - -add_task(async function test_create_item() { - let testURI = NetUtil.newURI("http://test_create_item.com"); - - let txn = new PlacesCreateBookmarkTransaction(testURI, root, bmStartIndex, - "Test creating an item"); - - txnManager.doTransaction(txn); - let id = bmsvc.getBookmarkIdsForURI(testURI)[0]; - Assert.equal(observer._itemAddedId, id); - Assert.equal(observer._itemAddedIndex, bmStartIndex); - Assert.notEqual(await PlacesUtils.bookmarks.fetch({url: testURI}), null); - - txn.undoTransaction(); - Assert.equal(observer._itemRemovedId, id); - Assert.equal(observer._itemRemovedIndex, bmStartIndex); - Assert.equal(await PlacesUtils.bookmarks.fetch({url: testURI}), null); - - txn.redoTransaction(); - Assert.notEqual(await PlacesUtils.bookmarks.fetch({url: testURI}), null); - let newId = bmsvc.getBookmarkIdsForURI(testURI)[0]; - Assert.equal(observer._itemAddedIndex, bmStartIndex); - Assert.equal(observer._itemAddedParent, root); - Assert.equal(observer._itemAddedId, newId); - - txn.undoTransaction(); - Assert.equal(observer._itemRemovedId, newId); - Assert.equal(observer._itemRemovedFolder, root); - Assert.equal(observer._itemRemovedIndex, bmStartIndex); -}); - -add_task(async function test_create_item_to_folder() { - const TEST_FOLDERNAME = "Test creating item to a folder"; - let testURI = NetUtil.newURI("http://test_create_item_to_folder.com"); - let folderId = bmsvc.createFolder(root, TEST_FOLDERNAME, bmsvc.DEFAULT_INDEX); - - let txn = new PlacesCreateBookmarkTransaction(testURI, folderId, bmStartIndex, - "Test creating item"); - txnManager.doTransaction(txn); - let bkmId = bmsvc.getBookmarkIdsForURI(testURI)[0]; - Assert.equal(observer._itemAddedId, bkmId); - Assert.equal(observer._itemAddedIndex, bmStartIndex); - Assert.notEqual(await PlacesUtils.bookmarks.fetch({url: testURI}), null); - - txn.undoTransaction(); - Assert.equal(observer._itemRemovedId, bkmId); - Assert.equal(observer._itemRemovedIndex, bmStartIndex); - - txn.redoTransaction(); - let newBkmId = bmsvc.getBookmarkIdsForURI(testURI)[0]; - Assert.equal(observer._itemAddedIndex, bmStartIndex); - Assert.equal(observer._itemAddedParent, folderId); - Assert.equal(observer._itemAddedId, newBkmId); - - txn.undoTransaction(); - Assert.equal(observer._itemRemovedId, newBkmId); - Assert.equal(observer._itemRemovedFolder, folderId); - Assert.equal(observer._itemRemovedIndex, bmStartIndex); -}); - -add_task(async function test_move_items_to_folder() { - let testFolderId = bmsvc.createFolder(root, "Test move items", bmsvc.DEFAULT_INDEX); - let testURI = NetUtil.newURI("http://test_move_items.com"); - let testBkmId = bmsvc.insertBookmark(testFolderId, testURI, bmsvc.DEFAULT_INDEX, "1: Test move items"); - bmsvc.insertBookmark(testFolderId, testURI, bmsvc.DEFAULT_INDEX, "2: Test move items"); - - // Moving items between the same folder - let sameTxn = new PlacesMoveItemTransaction(testBkmId, testFolderId, bmsvc.DEFAULT_INDEX); - - sameTxn.doTransaction(); - Assert.equal(observer._itemMovedId, testBkmId); - Assert.equal(observer._itemMovedOldParent, testFolderId); - Assert.equal(observer._itemMovedOldIndex, 0); - Assert.equal(observer._itemMovedNewParent, testFolderId); - Assert.equal(observer._itemMovedNewIndex, 1); - - sameTxn.undoTransaction(); - Assert.equal(observer._itemMovedId, testBkmId); - Assert.equal(observer._itemMovedOldParent, testFolderId); - Assert.equal(observer._itemMovedOldIndex, 1); - Assert.equal(observer._itemMovedNewParent, testFolderId); - Assert.equal(observer._itemMovedNewIndex, 0); - - sameTxn.redoTransaction(); - Assert.equal(observer._itemMovedId, testBkmId); - Assert.equal(observer._itemMovedOldParent, testFolderId); - Assert.equal(observer._itemMovedOldIndex, 0); - Assert.equal(observer._itemMovedNewParent, testFolderId); - Assert.equal(observer._itemMovedNewIndex, 1); - - sameTxn.undoTransaction(); - Assert.equal(observer._itemMovedId, testBkmId); - Assert.equal(observer._itemMovedOldParent, testFolderId); - Assert.equal(observer._itemMovedOldIndex, 1); - Assert.equal(observer._itemMovedNewParent, testFolderId); - Assert.equal(observer._itemMovedNewIndex, 0); - - // Moving items between different folders - let folderId = bmsvc.createFolder(testFolderId, - "Test move items between different folders", - bmsvc.DEFAULT_INDEX); - let diffTxn = new PlacesMoveItemTransaction(testBkmId, folderId, bmsvc.DEFAULT_INDEX); - - diffTxn.doTransaction(); - Assert.equal(observer._itemMovedId, testBkmId); - Assert.equal(observer._itemMovedOldParent, testFolderId); - Assert.equal(observer._itemMovedOldIndex, 0); - Assert.equal(observer._itemMovedNewParent, folderId); - Assert.equal(observer._itemMovedNewIndex, 0); - - sameTxn.undoTransaction(); - Assert.equal(observer._itemMovedId, testBkmId); - Assert.equal(observer._itemMovedOldParent, folderId); - Assert.equal(observer._itemMovedOldIndex, 0); - Assert.equal(observer._itemMovedNewParent, testFolderId); - Assert.equal(observer._itemMovedNewIndex, 0); - - diffTxn.redoTransaction(); - Assert.equal(observer._itemMovedId, testBkmId); - Assert.equal(observer._itemMovedOldParent, testFolderId); - Assert.equal(observer._itemMovedOldIndex, 0); - Assert.equal(observer._itemMovedNewParent, folderId); - Assert.equal(observer._itemMovedNewIndex, 0); - - sameTxn.undoTransaction(); - Assert.equal(observer._itemMovedId, testBkmId); - Assert.equal(observer._itemMovedOldParent, folderId); - Assert.equal(observer._itemMovedOldIndex, 0); - Assert.equal(observer._itemMovedNewParent, testFolderId); - Assert.equal(observer._itemMovedNewIndex, 0); -}); - -add_task(async function test_remove_folder() { - let testFolder = bmsvc.createFolder(root, "Test Removing a Folder", bmsvc.DEFAULT_INDEX); - let folderId = bmsvc.createFolder(testFolder, "Removed Folder", bmsvc.DEFAULT_INDEX); - - let txn = new PlacesRemoveItemTransaction(folderId); - - txn.doTransaction(); - Assert.equal(observer._itemRemovedId, folderId); - Assert.equal(observer._itemRemovedFolder, testFolder); - Assert.equal(observer._itemRemovedIndex, 0); - - txn.undoTransaction(); - Assert.equal(observer._itemAddedId, folderId); - Assert.equal(observer._itemAddedParent, testFolder); - Assert.equal(observer._itemAddedIndex, 0); - - txn.redoTransaction(); - Assert.equal(observer._itemRemovedId, folderId); - Assert.equal(observer._itemRemovedFolder, testFolder); - Assert.equal(observer._itemRemovedIndex, 0); - - txn.undoTransaction(); - Assert.equal(observer._itemAddedId, folderId); - Assert.equal(observer._itemAddedParent, testFolder); - Assert.equal(observer._itemAddedIndex, 0); -}); - -add_task(async function test_remove_item_with_tag() { - // Notice in this case the tag persists since other bookmarks have same uri. - let testFolder = bmsvc.createFolder(root, "Test removing an item with a tag", - bmsvc.DEFAULT_INDEX); - - const TAG_NAME = "tag-test_remove_item_with_tag"; - let testURI = NetUtil.newURI("http://test_remove_item_with_tag.com"); - let testBkmId = bmsvc.insertBookmark(testFolder, testURI, bmsvc.DEFAULT_INDEX, "test-item1"); - - // create bookmark for not removing tag. - bmsvc.insertBookmark(testFolder, testURI, bmsvc.DEFAULT_INDEX, "test-item2"); - - // set tag - tagssvc.tagURI(testURI, [TAG_NAME]); - - let txn = new PlacesRemoveItemTransaction(testBkmId); - - txn.doTransaction(); - Assert.equal(observer._itemRemovedId, testBkmId); - Assert.equal(observer._itemRemovedFolder, testFolder); - Assert.equal(observer._itemRemovedIndex, 0); - Assert.equal(tagssvc.getTagsForURI(testURI), TAG_NAME); - - txn.undoTransaction(); - let newbkmk2Id = observer._itemAddedId; - Assert.equal(observer._itemAddedParent, testFolder); - Assert.equal(observer._itemAddedIndex, 0); - Assert.equal(tagssvc.getTagsForURI(testURI)[0], TAG_NAME); - - txn.redoTransaction(); - Assert.equal(observer._itemRemovedId, newbkmk2Id); - Assert.equal(observer._itemRemovedFolder, testFolder); - Assert.equal(observer._itemRemovedIndex, 0); - Assert.equal(tagssvc.getTagsForURI(testURI)[0], TAG_NAME); - - txn.undoTransaction(); - Assert.equal(observer._itemAddedParent, testFolder); - Assert.equal(observer._itemAddedIndex, 0); - Assert.equal(tagssvc.getTagsForURI(testURI)[0], TAG_NAME); -}); - -add_task(async function test_remove_item_with_keyword() { - // Notice in this case the tag persists since other bookmarks have same uri. - let testFolder = bmsvc.createFolder(root, "Test removing an item with a keyword", - bmsvc.DEFAULT_INDEX); - - const KEYWORD = "test: test removing an item with a keyword"; - let testURI = NetUtil.newURI("http://test_remove_item_with_keyword.com"); - let testBkmId = bmsvc.insertBookmark(testFolder, testURI, bmsvc.DEFAULT_INDEX, "test-item1"); - - // set keyword - await PlacesUtils.keywords.insert({ url: testURI.spec, keyword: KEYWORD}); - - let txn = new PlacesRemoveItemTransaction(testBkmId); - - txn.doTransaction(); - Assert.equal(observer._itemRemovedId, testBkmId); - Assert.equal(observer._itemRemovedFolder, testFolder); - Assert.equal(observer._itemRemovedIndex, 0); - await promiseKeyword(KEYWORD, null); - - txn.undoTransaction(); - let newbkmk2Id = observer._itemAddedId; - Assert.equal(observer._itemAddedParent, testFolder); - Assert.equal(observer._itemAddedIndex, 0); - await promiseKeyword(KEYWORD, testURI.spec); - - txn.redoTransaction(); - Assert.equal(observer._itemRemovedId, newbkmk2Id); - Assert.equal(observer._itemRemovedFolder, testFolder); - Assert.equal(observer._itemRemovedIndex, 0); - await promiseKeyword(KEYWORD, null); - - txn.undoTransaction(); - Assert.equal(observer._itemAddedParent, testFolder); - Assert.equal(observer._itemAddedIndex, 0); -}); - -add_task(async function test_creating_separator() { - let testFolder = bmsvc.createFolder(root, "Test creating a separator", bmsvc.DEFAULT_INDEX); - - let txn = new PlacesCreateSeparatorTransaction(testFolder, 0); - txn.doTransaction(); - - let sepId = observer._itemAddedId; - Assert.equal(observer._itemAddedIndex, 0); - Assert.equal(observer._itemAddedParent, testFolder); - - txn.undoTransaction(); - Assert.equal(observer._itemRemovedId, sepId); - Assert.equal(observer._itemRemovedFolder, testFolder); - Assert.equal(observer._itemRemovedIndex, 0); - - txn.redoTransaction(); - let newSepId = observer._itemAddedId; - Assert.equal(observer._itemAddedIndex, 0); - Assert.equal(observer._itemAddedParent, testFolder); - - txn.undoTransaction(); - Assert.equal(observer._itemRemovedId, newSepId); - Assert.equal(observer._itemRemovedFolder, testFolder); - Assert.equal(observer._itemRemovedIndex, 0); -}); - -add_task(async function test_removing_separator() { - let testFolder = bmsvc.createFolder(root, "Test removing a separator", bmsvc.DEFAULT_INDEX); - - let sepId = bmsvc.insertSeparator(testFolder, 0); - let txn = new PlacesRemoveItemTransaction(sepId); - - txn.doTransaction(); - Assert.equal(observer._itemRemovedId, sepId); - Assert.equal(observer._itemRemovedFolder, testFolder); - Assert.equal(observer._itemRemovedIndex, 0); - - txn.undoTransaction(); - Assert.equal(observer._itemAddedId, sepId); // New separator created - Assert.equal(observer._itemAddedParent, testFolder); - Assert.equal(observer._itemAddedIndex, 0); - - txn.redoTransaction(); - Assert.equal(observer._itemRemovedId, sepId); - Assert.equal(observer._itemRemovedFolder, testFolder); - Assert.equal(observer._itemRemovedIndex, 0); - - txn.undoTransaction(); - Assert.equal(observer._itemAddedId, sepId); // New separator created - Assert.equal(observer._itemAddedParent, testFolder); - Assert.equal(observer._itemAddedIndex, 0); -}); - -add_task(async function test_editing_item_title() { - const TITLE = "Test editing item title"; - const MOD_TITLE = "Mod: Test editing item title"; - let testURI = NetUtil.newURI("http://www.test_editing_item_title.com"); - let testBkmId = bmsvc.insertBookmark(root, testURI, bmsvc.DEFAULT_INDEX, TITLE); - - let txn = new PlacesEditItemTitleTransaction(testBkmId, MOD_TITLE); - - txn.doTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, "title"); - Assert.equal(observer._itemChangedValue, MOD_TITLE); - - txn.undoTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, "title"); - Assert.equal(observer._itemChangedValue, TITLE); - - txn.redoTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, "title"); - Assert.equal(observer._itemChangedValue, MOD_TITLE); - - txn.undoTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, "title"); - Assert.equal(observer._itemChangedValue, TITLE); -}); - -add_task(async function test_editing_item_uri() { - const OLD_TEST_URI = NetUtil.newURI("http://old.test_editing_item_uri.com/"); - const NEW_TEST_URI = NetUtil.newURI("http://new.test_editing_item_uri.com/"); - let testBkmId = bmsvc.insertBookmark(root, OLD_TEST_URI, bmsvc.DEFAULT_INDEX, - "Test editing item title"); - tagssvc.tagURI(OLD_TEST_URI, ["tag"]); - - let txn = new PlacesEditBookmarkURITransaction(testBkmId, NEW_TEST_URI); - - txn.doTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, "uri"); - Assert.equal(observer._itemChangedValue, NEW_TEST_URI.spec); - Assert.equal(JSON.stringify(tagssvc.getTagsForURI(NEW_TEST_URI)), JSON.stringify(["tag"])); - Assert.equal(JSON.stringify(tagssvc.getTagsForURI(OLD_TEST_URI)), JSON.stringify([])); - - txn.undoTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, "uri"); - Assert.equal(observer._itemChangedValue, OLD_TEST_URI.spec); - Assert.equal(JSON.stringify(tagssvc.getTagsForURI(OLD_TEST_URI)), JSON.stringify(["tag"])); - Assert.equal(JSON.stringify(tagssvc.getTagsForURI(NEW_TEST_URI)), JSON.stringify([])); - - txn.redoTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, "uri"); - Assert.equal(observer._itemChangedValue, NEW_TEST_URI.spec); - Assert.equal(JSON.stringify(tagssvc.getTagsForURI(NEW_TEST_URI)), JSON.stringify(["tag"])); - Assert.equal(JSON.stringify(tagssvc.getTagsForURI(OLD_TEST_URI)), JSON.stringify([])); - - txn.undoTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, "uri"); - Assert.equal(observer._itemChangedValue, OLD_TEST_URI.spec); - Assert.equal(JSON.stringify(tagssvc.getTagsForURI(OLD_TEST_URI)), JSON.stringify(["tag"])); - Assert.equal(JSON.stringify(tagssvc.getTagsForURI(NEW_TEST_URI)), JSON.stringify([])); -}); - -add_task(async function test_edit_description_transaction() { - let testURI = NetUtil.newURI("http://test_edit_description_transaction.com"); - let testBkmId = bmsvc.insertBookmark(root, testURI, bmsvc.DEFAULT_INDEX, "Test edit description transaction"); - - let anno = { - name: DESCRIPTION_ANNO, - type: Ci.nsIAnnotationService.TYPE_STRING, - flags: 0, - value: "Test edit Description", - expires: Ci.nsIAnnotationService.EXPIRE_NEVER, - }; - let txn = new PlacesSetItemAnnotationTransaction(testBkmId, anno); - - txn.doTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, DESCRIPTION_ANNO); -}); - -add_task(async function test_edit_keyword() { - const KEYWORD = "keyword-test_edit_keyword"; - - let testURI = NetUtil.newURI("http://test_edit_keyword.com"); - let testBkmId = bmsvc.insertBookmark(root, testURI, bmsvc.DEFAULT_INDEX, "Test edit keyword"); - - let txn = new PlacesEditBookmarkKeywordTransaction(testBkmId, KEYWORD, "postData"); - - txn.doTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, "keyword"); - Assert.equal(observer._itemChangedValue, KEYWORD); - Assert.equal(PlacesUtils.getPostDataForBookmark(testBkmId), "postData"); - - txn.undoTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, "keyword"); - Assert.equal(observer._itemChangedValue, ""); - Assert.equal(PlacesUtils.getPostDataForBookmark(testBkmId), null); -}); - -add_task(async function test_edit_specific_keyword() { - const KEYWORD = "keyword-test_edit_keyword2"; - - let testURI = NetUtil.newURI("http://test_edit_keyword2.com"); - let testBkmId = bmsvc.insertBookmark(root, testURI, bmsvc.DEFAULT_INDEX, "Test edit keyword"); - // Add multiple keyword to this uri. - await PlacesUtils.keywords.insert({ keyword: "kw1", url: testURI.spec, postData: "postData1" }); - await PlacesUtils.keywords.insert({keyword: "kw2", url: testURI.spec, postData: "postData2" }); - - // Try to change only kw2. - let txn = new PlacesEditBookmarkKeywordTransaction(testBkmId, KEYWORD, "postData2", "kw2"); - - txn.doTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, "keyword"); - Assert.equal(observer._itemChangedValue, KEYWORD); - let entry = await PlacesUtils.keywords.fetch("kw1"); - Assert.equal(entry.url.href, testURI.spec); - Assert.equal(entry.postData, "postData1"); - await promiseKeyword(KEYWORD, testURI.spec, "postData2"); - await promiseKeyword("kw2", null); - - txn.undoTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, "keyword"); - Assert.equal(observer._itemChangedValue, "kw2"); - Assert.equal(PlacesUtils.getPostDataForBookmark(testBkmId), "postData1"); - entry = await PlacesUtils.keywords.fetch("kw1"); - Assert.equal(entry.url.href, testURI.spec); - Assert.equal(entry.postData, "postData1"); - await promiseKeyword("kw2", testURI.spec, "postData2"); - await promiseKeyword("keyword", null); -}); - -add_task(async function test_LoadInSidebar_transaction() { - let testURI = NetUtil.newURI("http://test_LoadInSidebar_transaction.com"); - let testBkmId = bmsvc.insertBookmark(root, testURI, bmsvc.DEFAULT_INDEX, "Test LoadInSidebar transaction"); - - const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar"; - let anno = { name: LOAD_IN_SIDEBAR_ANNO, - type: Ci.nsIAnnotationService.TYPE_INT32, - flags: 0, - value: true, - expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; - let txn = new PlacesSetItemAnnotationTransaction(testBkmId, anno); - - txn.doTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, LOAD_IN_SIDEBAR_ANNO); - Assert.equal(observer._itemChanged_isAnnotationProperty, true); - - txn.undoTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, LOAD_IN_SIDEBAR_ANNO); - Assert.equal(observer._itemChanged_isAnnotationProperty, true); -}); - -add_task(async function test_generic_item_annotation() { - let testURI = NetUtil.newURI("http://test_generic_item_annotation.com"); - let testBkmId = bmsvc.insertBookmark(root, testURI, bmsvc.DEFAULT_INDEX, "Test generic item annotation"); - - let itemAnnoObj = { name: "testAnno/testInt", - type: Ci.nsIAnnotationService.TYPE_INT32, - flags: 0, - value: 123, - expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; - let txn = new PlacesSetItemAnnotationTransaction(testBkmId, itemAnnoObj); - - txn.doTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, "testAnno/testInt"); - Assert.equal(observer._itemChanged_isAnnotationProperty, true); - - txn.undoTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, "testAnno/testInt"); - Assert.equal(observer._itemChanged_isAnnotationProperty, true); - - txn.redoTransaction(); - Assert.equal(observer._itemChangedId, testBkmId); - Assert.equal(observer._itemChangedProperty, "testAnno/testInt"); - Assert.equal(observer._itemChanged_isAnnotationProperty, true); -}); - -add_task(async function test_editing_item_date_added() { - let testURI = NetUtil.newURI("http://test_editing_item_date_added.com"); - let testBkmId = bmsvc.insertBookmark(root, testURI, bmsvc.DEFAULT_INDEX, - "Test editing item date added"); - - let oldAdded = bmsvc.getItemDateAdded(testBkmId); - let newAdded = Date.now() * 1000 + 1000; - let txn = new PlacesEditItemDateAddedTransaction(testBkmId, newAdded); - - txn.doTransaction(); - Assert.equal(newAdded, bmsvc.getItemDateAdded(testBkmId)); - - txn.undoTransaction(); - Assert.equal(oldAdded, bmsvc.getItemDateAdded(testBkmId)); -}); - -add_task(async function test_edit_item_last_modified() { - let testURI = NetUtil.newURI("http://test_edit_item_last_modified.com"); - let testBkmId = bmsvc.insertBookmark(root, testURI, bmsvc.DEFAULT_INDEX, - "Test editing item last modified"); - - let oldModified = bmsvc.getItemLastModified(testBkmId); - let newModified = Date.now() * 1000 + 1000; - let txn = new PlacesEditItemLastModifiedTransaction(testBkmId, newModified); - - txn.doTransaction(); - Assert.equal(newModified, bmsvc.getItemLastModified(testBkmId)); - - txn.undoTransaction(); - Assert.equal(oldModified, bmsvc.getItemLastModified(testBkmId)); -}); - -add_task(async function test_generic_page_annotation() { - const TEST_ANNO = "testAnno/testInt"; - let testURI = NetUtil.newURI("http://www.mozilla.org/"); - PlacesTestUtils.addVisits(testURI).then(function() { - let pageAnnoObj = { name: TEST_ANNO, - type: Ci.nsIAnnotationService.TYPE_INT32, - flags: 0, - value: 123, - expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; - let txn = new PlacesSetPageAnnotationTransaction(testURI, pageAnnoObj); - - txn.doTransaction(); - Assert.ok(annosvc.pageHasAnnotation(testURI, TEST_ANNO)); - - txn.undoTransaction(); - Assert.ok(!annosvc.pageHasAnnotation(testURI, TEST_ANNO)); - - txn.redoTransaction(); - Assert.ok(annosvc.pageHasAnnotation(testURI, TEST_ANNO)); - }); -}); - -add_task(async function test_sort_folder_by_name() { - let testFolder = bmsvc.createFolder(root, "Test PlacesSortFolderByNameTransaction", - bmsvc.DEFAULT_INDEX); - let testURI = NetUtil.newURI("http://test_sort_folder_by_name.com"); - - bmsvc.insertBookmark(testFolder, testURI, bmsvc.DEFAULT_INDEX, "bookmark3"); - bmsvc.insertBookmark(testFolder, testURI, bmsvc.DEFAULT_INDEX, "bookmark2"); - bmsvc.insertBookmark(testFolder, testURI, bmsvc.DEFAULT_INDEX, "bookmark1"); - - let bkmIds = bmsvc.getBookmarkIdsForURI(testURI); - bkmIds.sort(); - - let b1 = bkmIds[0]; - let b2 = bkmIds[1]; - let b3 = bkmIds[2]; - - Assert.equal(0, bmsvc.getItemIndex(b1)); - Assert.equal(1, bmsvc.getItemIndex(b2)); - Assert.equal(2, bmsvc.getItemIndex(b3)); - - let txn = new PlacesSortFolderByNameTransaction(testFolder); - - txn.doTransaction(); - Assert.equal(2, bmsvc.getItemIndex(b1)); - Assert.equal(1, bmsvc.getItemIndex(b2)); - Assert.equal(0, bmsvc.getItemIndex(b3)); - - txn.undoTransaction(); - Assert.equal(0, bmsvc.getItemIndex(b1)); - Assert.equal(1, bmsvc.getItemIndex(b2)); - Assert.equal(2, bmsvc.getItemIndex(b3)); - - txn.redoTransaction(); - Assert.equal(2, bmsvc.getItemIndex(b1)); - Assert.equal(1, bmsvc.getItemIndex(b2)); - Assert.equal(0, bmsvc.getItemIndex(b3)); - - txn.undoTransaction(); - Assert.equal(0, bmsvc.getItemIndex(b1)); - Assert.equal(1, bmsvc.getItemIndex(b2)); - Assert.equal(2, bmsvc.getItemIndex(b3)); -}); - -add_task(async function test_tagURI_untagURI() { - const TAG_1 = "tag-test_tagURI_untagURI-bar"; - const TAG_2 = "tag-test_tagURI_untagURI-foo"; - let tagURI = NetUtil.newURI("http://test_tagURI_untagURI.com"); - - // Test tagURI - let tagTxn = new PlacesTagURITransaction(tagURI, [TAG_1, TAG_2]); - - tagTxn.doTransaction(); - Assert.equal(JSON.stringify(tagssvc.getTagsForURI(tagURI)), JSON.stringify([TAG_1, TAG_2])); - - tagTxn.undoTransaction(); - Assert.equal(tagssvc.getTagsForURI(tagURI).length, 0); - - tagTxn.redoTransaction(); - Assert.equal(JSON.stringify(tagssvc.getTagsForURI(tagURI)), JSON.stringify([TAG_1, TAG_2])); - - // Test untagURI - let untagTxn = new PlacesUntagURITransaction(tagURI, [TAG_1]); - - untagTxn.doTransaction(); - Assert.equal(JSON.stringify(tagssvc.getTagsForURI(tagURI)), JSON.stringify([TAG_2])); - - untagTxn.undoTransaction(); - Assert.equal(JSON.stringify(tagssvc.getTagsForURI(tagURI)), JSON.stringify([TAG_1, TAG_2])); - - untagTxn.redoTransaction(); - Assert.equal(JSON.stringify(tagssvc.getTagsForURI(tagURI)), JSON.stringify([TAG_2])); -}); - -add_task(async function test_aggregate_removeItem_Txn() { - let testFolder = bmsvc.createFolder(root, "Test aggregate removeItem transaction", bmsvc.DEFAULT_INDEX); - - const TEST_URL = "http://test_aggregate_removeitem_txn.com/"; - const FOLDERNAME = "Folder"; - let testURI = NetUtil.newURI(TEST_URL); - - let bkmk1Id = bmsvc.insertBookmark(testFolder, testURI, 0, "Mozilla"); - let bkmk2Id = bmsvc.insertSeparator(testFolder, 1); - let bkmk3Id = bmsvc.createFolder(testFolder, FOLDERNAME, 2); - - let bkmk3_1Id = bmsvc.insertBookmark(bkmk3Id, testURI, 0, "Mozilla"); - let bkmk3_2Id = bmsvc.insertSeparator(bkmk3Id, 1); - let bkmk3_3Id = bmsvc.createFolder(bkmk3Id, FOLDERNAME, 2); - - let childTxn1 = new PlacesRemoveItemTransaction(bkmk1Id); - let childTxn2 = new PlacesRemoveItemTransaction(bkmk2Id); - let childTxn3 = new PlacesRemoveItemTransaction(bkmk3Id); - let transactions = [childTxn1, childTxn2, childTxn3]; - let txn = new PlacesAggregatedTransaction("RemoveItems", transactions); - - txn.doTransaction(); - Assert.equal(bmsvc.getItemIndex(bkmk1Id), -1); - Assert.equal(bmsvc.getItemIndex(bkmk2Id), -1); - Assert.equal(bmsvc.getItemIndex(bkmk3Id), -1); - Assert.equal(bmsvc.getItemIndex(bkmk3_1Id), -1); - Assert.equal(bmsvc.getItemIndex(bkmk3_2Id), -1); - Assert.equal(bmsvc.getItemIndex(bkmk3_3Id), -1); - // Check last removed item id. - Assert.equal(observer._itemRemovedId, bkmk3Id); - - txn.undoTransaction(); - let newBkmk1Id = bmsvc.getIdForItemAt(testFolder, 0); - let newBkmk2Id = bmsvc.getIdForItemAt(testFolder, 1); - let newBkmk3Id = bmsvc.getIdForItemAt(testFolder, 2); - let newBkmk3_1Id = bmsvc.getIdForItemAt(newBkmk3Id, 0); - let newBkmk3_2Id = bmsvc.getIdForItemAt(newBkmk3Id, 1); - let newBkmk3_3Id = bmsvc.getIdForItemAt(newBkmk3Id, 2); - Assert.equal(bmsvc.getItemType(newBkmk1Id), bmsvc.TYPE_BOOKMARK); - Assert.equal(bmsvc.getBookmarkURI(newBkmk1Id).spec, TEST_URL); - Assert.equal(bmsvc.getItemType(newBkmk2Id), bmsvc.TYPE_SEPARATOR); - Assert.equal(bmsvc.getItemType(newBkmk3Id), bmsvc.TYPE_FOLDER); - Assert.equal(bmsvc.getItemTitle(newBkmk3Id), FOLDERNAME); - Assert.equal(bmsvc.getFolderIdForItem(newBkmk3_1Id), newBkmk3Id); - Assert.equal(bmsvc.getItemType(newBkmk3_1Id), bmsvc.TYPE_BOOKMARK); - Assert.equal(bmsvc.getBookmarkURI(newBkmk3_1Id).spec, TEST_URL); - Assert.equal(bmsvc.getFolderIdForItem(newBkmk3_2Id), newBkmk3Id); - Assert.equal(bmsvc.getItemType(newBkmk3_2Id), bmsvc.TYPE_SEPARATOR); - Assert.equal(bmsvc.getFolderIdForItem(newBkmk3_3Id), newBkmk3Id); - Assert.equal(bmsvc.getItemType(newBkmk3_3Id), bmsvc.TYPE_FOLDER); - Assert.equal(bmsvc.getItemTitle(newBkmk3_3Id), FOLDERNAME); - // Check last added back item id. - // Notice items are restored in reverse order. - Assert.equal(observer._itemAddedId, newBkmk1Id); - - txn.redoTransaction(); - Assert.equal(bmsvc.getItemIndex(newBkmk1Id), -1); - Assert.equal(bmsvc.getItemIndex(newBkmk2Id), -1); - Assert.equal(bmsvc.getItemIndex(newBkmk3Id), -1); - Assert.equal(bmsvc.getItemIndex(newBkmk3_1Id), -1); - Assert.equal(bmsvc.getItemIndex(newBkmk3_2Id), -1); - Assert.equal(bmsvc.getItemIndex(newBkmk3_3Id), -1); - // Check last removed item id. - Assert.equal(observer._itemRemovedId, newBkmk3Id); - - txn.undoTransaction(); - newBkmk1Id = bmsvc.getIdForItemAt(testFolder, 0); - newBkmk2Id = bmsvc.getIdForItemAt(testFolder, 1); - newBkmk3Id = bmsvc.getIdForItemAt(testFolder, 2); - newBkmk3_1Id = bmsvc.getIdForItemAt(newBkmk3Id, 0); - newBkmk3_2Id = bmsvc.getIdForItemAt(newBkmk3Id, 1); - newBkmk3_3Id = bmsvc.getIdForItemAt(newBkmk3Id, 2); - Assert.equal(bmsvc.getItemType(newBkmk1Id), bmsvc.TYPE_BOOKMARK); - Assert.equal(bmsvc.getBookmarkURI(newBkmk1Id).spec, TEST_URL); - Assert.equal(bmsvc.getItemType(newBkmk2Id), bmsvc.TYPE_SEPARATOR); - Assert.equal(bmsvc.getItemType(newBkmk3Id), bmsvc.TYPE_FOLDER); - Assert.equal(bmsvc.getItemTitle(newBkmk3Id), FOLDERNAME); - Assert.equal(bmsvc.getFolderIdForItem(newBkmk3_1Id), newBkmk3Id); - Assert.equal(bmsvc.getItemType(newBkmk3_1Id), bmsvc.TYPE_BOOKMARK); - Assert.equal(bmsvc.getBookmarkURI(newBkmk3_1Id).spec, TEST_URL); - Assert.equal(bmsvc.getFolderIdForItem(newBkmk3_2Id), newBkmk3Id); - Assert.equal(bmsvc.getItemType(newBkmk3_2Id), bmsvc.TYPE_SEPARATOR); - Assert.equal(bmsvc.getFolderIdForItem(newBkmk3_3Id), newBkmk3Id); - Assert.equal(bmsvc.getItemType(newBkmk3_3Id), bmsvc.TYPE_FOLDER); - Assert.equal(bmsvc.getItemTitle(newBkmk3_3Id), FOLDERNAME); - // Check last added back item id. - // Notice items are restored in reverse order. - Assert.equal(observer._itemAddedId, newBkmk1Id); -}); - -add_task(async function test_create_item_with_childTxn() { - let testFolder = bmsvc.createFolder(root, "Test creating an item with childTxns", bmsvc.DEFAULT_INDEX); - - const BOOKMARK_TITLE = "parent item"; - let testURI = NetUtil.newURI("http://test_create_item_with_childTxn.com"); - let childTxns = []; - let newDateAdded = Date.now() * 1000 - 20000; - let editDateAdddedTxn = new PlacesEditItemDateAddedTransaction(null, newDateAdded); - childTxns.push(editDateAdddedTxn); - - let itemChildAnnoObj = { name: "testAnno/testInt", - type: Ci.nsIAnnotationService.TYPE_INT32, - flags: 0, - value: 123, - expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; - let annoTxn = new PlacesSetItemAnnotationTransaction(null, itemChildAnnoObj); - childTxns.push(annoTxn); - - let itemWChildTxn = new PlacesCreateBookmarkTransaction(testURI, testFolder, bmStartIndex, - BOOKMARK_TITLE, null, null, - childTxns); - try { - txnManager.doTransaction(itemWChildTxn); - let itemId = bmsvc.getBookmarkIdsForURI(testURI)[0]; - Assert.equal(observer._itemAddedId, itemId); - Assert.equal(newDateAdded, bmsvc.getItemDateAdded(itemId)); - Assert.equal(observer._itemChangedProperty, "testAnno/testInt"); - Assert.equal(observer._itemChanged_isAnnotationProperty, true); - Assert.ok(annosvc.itemHasAnnotation(itemId, itemChildAnnoObj.name)) - Assert.equal(annosvc.getItemAnnotation(itemId, itemChildAnnoObj.name), itemChildAnnoObj.value); - - itemWChildTxn.undoTransaction(); - Assert.equal(observer._itemRemovedId, itemId); - - itemWChildTxn.redoTransaction(); - Assert.notEqual(await PlacesUtils.bookmarks.fetch({url: testURI}), null); - let newId = bmsvc.getBookmarkIdsForURI(testURI)[0]; - Assert.equal(newDateAdded, bmsvc.getItemDateAdded(newId)); - Assert.equal(observer._itemAddedId, newId); - Assert.equal(observer._itemChangedProperty, "testAnno/testInt"); - Assert.equal(observer._itemChanged_isAnnotationProperty, true); - Assert.ok(annosvc.itemHasAnnotation(newId, itemChildAnnoObj.name)) - Assert.equal(annosvc.getItemAnnotation(newId, itemChildAnnoObj.name), itemChildAnnoObj.value); - - itemWChildTxn.undoTransaction(); - Assert.equal(observer._itemRemovedId, newId); - } catch (ex) { - do_throw("Setting a child transaction in a createItem transaction did throw: " + ex); - } -}); - -add_task(async function test_create_folder_with_child_itemTxn() { - let childURI = NetUtil.newURI("http://test_create_folder_with_child_itemTxn.com"); - let childItemTxn = new PlacesCreateBookmarkTransaction(childURI, root, - bmStartIndex, "childItem"); - let txn = new PlacesCreateFolderTransaction("Test creating a folder with child itemTxns", - root, bmStartIndex, null, [childItemTxn]); - try { - txnManager.doTransaction(txn); - let childItemId = bmsvc.getBookmarkIdsForURI(childURI)[0]; - Assert.equal(observer._itemAddedId, childItemId); - Assert.equal(observer._itemAddedIndex, 0); - Assert.notEqual(await PlacesUtils.bookmarks.fetch({url: childURI}), null); - - txn.undoTransaction(); - Assert.equal(await PlacesUtils.bookmarks.fetch({url: childURI}), null); - - txn.redoTransaction(); - let newchildItemId = bmsvc.getBookmarkIdsForURI(childURI)[0]; - Assert.equal(observer._itemAddedIndex, 0); - Assert.equal(observer._itemAddedId, newchildItemId); - Assert.notEqual(await PlacesUtils.bookmarks.fetch({url: childURI}), null); - - txn.undoTransaction(); - Assert.equal(await PlacesUtils.bookmarks.fetch({url: childURI}), null); - } catch (ex) { - do_throw("Setting a child item transaction in a createFolder transaction did throw: " + ex); - } -}); diff --git a/toolkit/components/places/tests/legacy/xpcshell.ini b/toolkit/components/places/tests/legacy/xpcshell.ini --- a/toolkit/components/places/tests/legacy/xpcshell.ini +++ b/toolkit/components/places/tests/legacy/xpcshell.ini @@ -3,11 +3,10 @@ [DEFAULT] head = head_legacy.js firefox-appdir = browser [test_418643_removeFolderChildren.js] [test_bookmarks_setNullTitle.js] [test_changeBookmarkURI.js] -[test_placesTxn.js] [test_protectRoots.js] [test_removeItem.js] diff --git a/tools/lint/eslint/modules.json b/tools/lint/eslint/modules.json --- a/tools/lint/eslint/modules.json +++ b/tools/lint/eslint/modules.json @@ -147,17 +147,17 @@ "PageMenu.jsm": ["PageMenuParent", "PageMenuChild"], "PageThumbs.jsm": ["PageThumbs", "PageThumbsStorage"], "Parser.jsm": ["Parser", "ParserHelpers", "SyntaxTreeVisitor"], "ParseSymbols.jsm": ["ParseSymbols"], "passwords.js": ["PasswordEngine", "LoginRec", "PasswordValidator"], "passwords.jsm": ["Password", "DumpPasswords"], "PdfJsNetwork.jsm": ["NetworkManager"], "PhoneNumberMetaData.jsm": ["PHONE_NUMBER_META_DATA"], - "PlacesUtils.jsm": ["PlacesUtils", "PlacesAggregatedTransaction", "PlacesCreateFolderTransaction", "PlacesCreateBookmarkTransaction", "PlacesCreateSeparatorTransaction", "PlacesCreateLivemarkTransaction", "PlacesMoveItemTransaction", "PlacesRemoveItemTransaction", "PlacesEditItemTitleTransaction", "PlacesEditBookmarkURITransaction", "PlacesSetItemAnnotationTransaction", "PlacesSetPageAnnotationTransaction", "PlacesEditBookmarkKeywordTransaction", "PlacesEditBookmarkPostDataTransaction", "PlacesEditItemDateAddedTransaction", "PlacesEditItemLastModifiedTransaction", "PlacesSortFolderByNameTransaction", "PlacesTagURITransaction", "PlacesUntagURITransaction"], + "PlacesUtils.jsm": ["PlacesUtils"], "PointerAdapter.jsm": ["PointerRelay", "PointerAdapter"], "policies.js": ["ErrorHandler", "SyncScheduler"], "prefs.js": ["PrefsEngine", "PrefRec"], "prefs.jsm": ["Preference"], "ProfileStorage.jsm": ["profileStorage"], "PromiseWorker.jsm": ["BasePromiseWorker"], "PushCrypto.jsm": ["PushCrypto", "concatArray"], "quit.js": ["goQuitApplication"],