--- a/services/sync/modules/bookmark_utils.js
+++ b/services/sync/modules/bookmark_utils.js
@@ -48,21 +48,24 @@ let BookmarkSpecialIds = {
// Special IDs. Note that mobile can attempt to create a record on
// dereference; special accessors are provided to prevent recursion within
// observers.
guids: ["menu", "places", "tags", "toolbar", "unfiled", "mobile"],
// Create the special mobile folder to store mobile bookmarks.
createMobileRoot: function createMobileRoot() {
let root = PlacesUtils.placesRootId;
- let mRoot = PlacesUtils.bookmarks.createFolder(root, "mobile", -1);
+ let mRoot = PlacesUtils.bookmarks.createFolder(root, "mobile", -1,
+ /* guid */ null, PlacesUtils.bookmarks.SOURCE_SYNC);
PlacesUtils.annotations.setItemAnnotation(
- mRoot, BookmarkAnnos.MOBILEROOT_ANNO, 1, 0, PlacesUtils.annotations.EXPIRE_NEVER);
+ mRoot, BookmarkAnnos.MOBILEROOT_ANNO, 1, 0,
+ PlacesUtils.annotations.EXPIRE_NEVER, PlacesUtils.bookmarks.SOURCE_SYNC);
PlacesUtils.annotations.setItemAnnotation(
- mRoot, BookmarkAnnos.EXCLUDEBACKUP_ANNO, 1, 0, PlacesUtils.annotations.EXPIRE_NEVER);
+ mRoot, BookmarkAnnos.EXCLUDEBACKUP_ANNO, 1, 0,
+ PlacesUtils.annotations.EXPIRE_NEVER, PlacesUtils.bookmarks.SOURCE_SYNC);
return mRoot;
},
findMobileRoot: function findMobileRoot(create) {
// Use the (one) mobile root if it already exists.
let root = PlacesUtils.annotations.getItemsWithAnnotation(
BookmarkAnnos.MOBILEROOT_ANNO, {});
if (root.length != 0)
--- a/services/sync/modules/engines/bookmarks.js
+++ b/services/sync/modules/engines/bookmarks.js
@@ -22,16 +22,17 @@ Cu.import("resource://services-sync/book
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/PlacesBackups.jsm");
const ANNOS_TO_TRACK = [BookmarkAnnos.DESCRIPTION_ANNO, BookmarkAnnos.SIDEBAR_ANNO,
PlacesUtils.LMANNO_FEEDURI, PlacesUtils.LMANNO_SITEURI];
const SERVICE_NOT_SUPPORTED = "Service not supported on this platform";
const FOLDER_SORTINDEX = 1000000;
+const { SOURCE_SYNC } = Ci.nsINavBookmarksService;
// Maps Sync record property names to `PlacesSyncUtils` bookmark properties.
const RECORD_PROPS_TO_BOOKMARK_PROPS = {
title: "title",
bmkUri: "url",
tags: "tags",
keyword: "keyword",
description: "description",
@@ -1051,29 +1052,29 @@ BookmarksTracker.prototype = {
let mobile = find(BookmarkAnnos.MOBILE_ANNO);
let queryURI = Utils.makeURI("place:folder=" + BookmarkSpecialIds.mobile);
let title = Str.sync.get("mobile.label");
// Don't add OR remove the mobile bookmarks if there's nothing.
if (PlacesUtils.bookmarks.getIdForItemAt(BookmarkSpecialIds.mobile, 0) == -1) {
if (mobile.length != 0)
- PlacesUtils.bookmarks.removeItem(mobile[0]);
+ PlacesUtils.bookmarks.removeItem(mobile[0], SOURCE_SYNC);
}
// Add the mobile bookmarks query if it doesn't exist
else if (mobile.length == 0) {
- let query = PlacesUtils.bookmarks.insertBookmark(all[0], queryURI, -1, title);
+ let query = PlacesUtils.bookmarks.insertBookmark(all[0], queryURI, -1, title, /* guid */ null, SOURCE_SYNC);
PlacesUtils.annotations.setItemAnnotation(query, BookmarkAnnos.ORGANIZERQUERY_ANNO, BookmarkAnnos.MOBILE_ANNO, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
+ PlacesUtils.annotations.EXPIRE_NEVER, SOURCE_SYNC);
PlacesUtils.annotations.setItemAnnotation(query, BookmarkAnnos.EXCLUDEBACKUP_ANNO, 1, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
+ PlacesUtils.annotations.EXPIRE_NEVER, SOURCE_SYNC);
}
// Make sure the existing title is correct
else if (PlacesUtils.bookmarks.getItemTitle(mobile[0]) != title) {
- PlacesUtils.bookmarks.setItemTitle(mobile[0], title);
+ PlacesUtils.bookmarks.setItemTitle(mobile[0], title, SOURCE_SYNC);
}
this.ignoreAll = false;
},
// This method is oddly structured, but the idea is to return as quickly as
// possible -- this handler gets called *every time* a bookmark changes, for
// *each change*.
@@ -1110,17 +1111,17 @@ BookmarksTracker.prototype = {
this._log.trace("onItemMoved: " + itemId);
this._add(oldParent, oldParentGuid);
if (oldParent != newParent) {
this._add(itemId, guid);
this._add(newParent, newParentGuid);
}
// Remove any position annotations now that the user moved the item
- PlacesUtils.annotations.removeItemAnnotation(itemId, BookmarkAnnos.PARENT_ANNO);
+ PlacesUtils.annotations.removeItemAnnotation(itemId, BookmarkAnnos.PARENT_ANNO, SOURCE_SYNC);
},
onBeginUpdateBatch: function () {
++this._batchDepth;
},
onEndUpdateBatch: function () {
if (--this._batchDepth === 0 && this._batchSawScoreIncrement) {
this.score += SCORE_INCREMENT_XLARGE;
--- a/toolkit/components/places/BookmarkHTMLUtils.jsm
+++ b/toolkit/components/places/BookmarkHTMLUtils.jsm
@@ -309,16 +309,20 @@ function Frame(aFrameId) {
* Used to override the values set by insertBookmark, createFolder, etc.
*/
this.previousDateAdded = 0;
this.previousLastModifiedDate = 0;
}
function BookmarkImporter(aInitialImport) {
this._isImportDefaults = aInitialImport;
+ // The bookmark change source, used to determine the sync status and change
+ // counter.
+ this._source = aInitialImport ? PlacesUtils.bookmarks.SOURCE_IMPORT_REPLACE :
+ PlacesUtils.bookmarks.SOURCE_IMPORT;
this._frames = new Array();
this._frames.push(new Frame(PlacesUtils.bookmarksMenuFolderId));
}
BookmarkImporter.prototype = {
_safeTrim: function safeTrim(aStr) {
return aStr ? aStr.trim() : aStr;
@@ -344,17 +348,18 @@ BookmarkImporter.prototype = {
let containerType = frame.lastContainerType;
switch (containerType) {
case Container_Normal:
// append a new folder
containerId =
PlacesUtils.bookmarks.createFolder(frame.containerId,
containerTitle,
- PlacesUtils.bookmarks.DEFAULT_INDEX);
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ /* aGuid */ null, this._source);
break;
case Container_Places:
containerId = PlacesUtils.placesRootId;
break;
case Container_Menu:
containerId = PlacesUtils.bookmarksMenuFolderId;
break;
case Container_Unfiled:
@@ -365,24 +370,24 @@ BookmarkImporter.prototype = {
break;
default:
// NOT REACHED
throw new Error("Unreached");
}
if (frame.previousDateAdded > 0) {
try {
- PlacesUtils.bookmarks.setItemDateAdded(containerId, frame.previousDateAdded);
+ PlacesUtils.bookmarks.setItemDateAdded(containerId, frame.previousDateAdded, this._source);
} catch (e) {
}
frame.previousDateAdded = 0;
}
if (frame.previousLastModifiedDate > 0) {
try {
- PlacesUtils.bookmarks.setItemLastModified(containerId, frame.previousLastModifiedDate);
+ PlacesUtils.bookmarks.setItemLastModified(containerId, frame.previousLastModifiedDate, this._source);
} catch (e) {
}
// don't clear last-modified, in case there's a description
}
frame.previousId = containerId;
this._frames.push(new Frame(containerId));
@@ -396,17 +401,19 @@ BookmarkImporter.prototype = {
* We also don't import ADD_DATE or LAST_MODIFIED for separators because
* pre-Places bookmarks did not support them.
*/
_handleSeparator: function handleSeparator(aElt) {
let frame = this._curFrame;
try {
frame.previousId =
PlacesUtils.bookmarks.insertSeparator(frame.containerId,
- PlacesUtils.bookmarks.DEFAULT_INDEX);
+ PlacesUtils.bookmarks.DEFAULT_INDEX,
+ /* aGuid */ null,
+ this._source);
} catch (e) {}
},
/**
* Handles <H1>. We check for the attribute PLACES_ROOT and reset the
* container id if it's found. Otherwise, the default bookmark menu
* root is assumed and imported things will go into the bookmarks menu.
*/
@@ -559,35 +566,37 @@ BookmarkImporter.prototype = {
}
// Create the bookmark. The title is unknown for now, we will set it later.
try {
frame.previousId =
PlacesUtils.bookmarks.insertBookmark(frame.containerId,
frame.previousLink,
PlacesUtils.bookmarks.DEFAULT_INDEX,
- "");
+ /* aTitle */ "",
+ /* aGuid */ null,
+ this._source);
} catch (e) {
return;
}
// Set the date added value, if we have it.
if (dateAdded) {
try {
PlacesUtils.bookmarks.setItemDateAdded(frame.previousId,
- this._convertImportedDateToInternalDate(dateAdded));
+ this._convertImportedDateToInternalDate(dateAdded), this._source);
} catch (e) {
}
}
// Adds tags to the URI, if there are any.
if (tags) {
try {
let tagsArray = tags.split(",");
- PlacesUtils.tagging.tagURI(frame.previousLink, tagsArray);
+ PlacesUtils.tagging.tagURI(frame.previousLink, tagsArray, this._source);
} catch (e) {
}
}
// Save the favicon.
if (icon || iconUri) {
let iconUriObject;
try {
@@ -601,35 +610,37 @@ BookmarkImporter.prototype = {
}
}
}
// Save the keyword.
if (keyword) {
let kwPromise = PlacesUtils.keywords.insert({ keyword,
url: frame.previousLink.spec,
- postData });
+ postData,
+ source: this._source });
this._importPromises.push(kwPromise);
}
// Set load-in-sidebar annotation for the bookmark.
if (webPanel && webPanel.toLowerCase() == "true") {
try {
PlacesUtils.annotations.setItemAnnotation(frame.previousId,
LOAD_IN_SIDEBAR_ANNO,
1,
0,
- PlacesUtils.annotations.EXPIRE_NEVER);
+ PlacesUtils.annotations.EXPIRE_NEVER,
+ this._source);
} catch (e) {
}
}
// Import last charset.
if (lastCharset) {
- let chPromise = PlacesUtils.setCharsetForURI(frame.previousLink, lastCharset);
+ let chPromise = PlacesUtils.setCharsetForURI(frame.previousLink, lastCharset, this._source);
this._importPromises.push(chPromise);
}
},
_handleContainerBegin: function handleContainerBegin() {
this._curFrame.containerNesting++;
},
@@ -643,17 +654,18 @@ BookmarkImporter.prototype = {
if (frame.containerNesting > 0)
frame.containerNesting --;
if (this._frames.length > 1 && frame.containerNesting == 0) {
// we also need to re-set the imported last-modified date here. Otherwise
// the addition of items will override the imported field.
let prevFrame = this._previousFrame;
if (prevFrame.previousLastModifiedDate > 0) {
PlacesUtils.bookmarks.setItemLastModified(frame.containerId,
- prevFrame.previousLastModifiedDate);
+ prevFrame.previousLastModifiedDate,
+ this._source);
}
this._frames.pop();
}
},
/**
* Creates the new frame for this heading now that we know the name of the
* container (tokens since the heading open tag will have been placed in
@@ -675,32 +687,35 @@ BookmarkImporter.prototype = {
// The is a live bookmark. We create it here since in HandleLinkBegin we
// don't know the title.
let lmPromise = PlacesUtils.livemarks.addLivemark({
"title": frame.previousText,
"parentId": frame.containerId,
"index": PlacesUtils.bookmarks.DEFAULT_INDEX,
"feedURI": frame.previousFeed,
"siteURI": frame.previousLink,
+ "source": this._source,
});
this._importPromises.push(lmPromise);
} else if (frame.previousLink) {
// This is a common bookmark.
PlacesUtils.bookmarks.setItemTitle(frame.previousId,
- frame.previousText);
+ frame.previousText,
+ this._source);
}
} catch (e) {
}
// Set last modified date as the last change.
if (frame.previousId > 0 && frame.previousLastModifiedDate > 0) {
try {
PlacesUtils.bookmarks.setItemLastModified(frame.previousId,
- frame.previousLastModifiedDate);
+ frame.previousLastModifiedDate,
+ this._source);
} catch (e) {
}
// Note: don't clear previousLastModifiedDate, because if this item has a
// description, we'll need to set it again.
}
frame.previousText = "";
@@ -753,17 +768,18 @@ BookmarkImporter.prototype = {
: frame.previousId;
try {
if (!PlacesUtils.annotations.itemHasAnnotation(itemId, DESCRIPTION_ANNO)) {
PlacesUtils.annotations.setItemAnnotation(itemId,
DESCRIPTION_ANNO,
frame.previousText,
0,
- PlacesUtils.annotations.EXPIRE_NEVER);
+ PlacesUtils.annotations.EXPIRE_NEVER,
+ this._source);
}
} catch (e) {
}
frame.previousText = "";
// Set last-modified a 2nd time for all items with descriptions
// we need to set last-modified as the *last* step in processing
// any item type in the bookmarks.html file, so that we do
@@ -778,17 +794,18 @@ BookmarkImporter.prototype = {
let lastModified;
if (!frame.previousLink) {
lastModified = this._previousFrame.previousLastModifiedDate;
} else {
lastModified = frame.previousLastModifiedDate;
}
if (itemId > 0 && lastModified > 0) {
- PlacesUtils.bookmarks.setItemLastModified(itemId, lastModified);
+ PlacesUtils.bookmarks.setItemLastModified(itemId, lastModified,
+ this._source);
}
}
frame.inDescription = false;
}
if (aElt.namespaceURI != "http://www.w3.org/1999/xhtml") {
return;
}
@@ -889,19 +906,19 @@ BookmarkImporter.prototype = {
},
runBatched: function runBatched(aDoc) {
if (!aDoc) {
return;
}
if (this._isImportDefaults) {
- PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.bookmarksMenuFolderId);
- PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.toolbarFolderId);
- PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
+ PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.bookmarksMenuFolderId, this._source);
+ PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.toolbarFolderId, this._source);
+ PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId, this._source);
}
let current = aDoc;
let next;
for (;;) {
switch (current.nodeType) {
case Ci.nsIDOMNode.ELEMENT_NODE:
this._openContainer(current);
--- a/toolkit/components/places/BookmarkJSONUtils.jsm
+++ b/toolkit/components/places/BookmarkJSONUtils.jsm
@@ -171,16 +171,20 @@ this.BookmarkJSONUtils = Object.freeze({
yield OS.File.writeAtomic(aFilePath, jsonString, writeOptions);
return { count: count, hash: hash };
});
}
});
function BookmarkImporter(aReplace) {
this._replace = aReplace;
+ // The bookmark change source, used to determine the sync status and change
+ // counter.
+ this._source = aReplace ? PlacesUtils.bookmarks.SOURCE_IMPORT_REPLACE :
+ PlacesUtils.bookmarks.SOURCE_IMPORT;
}
BookmarkImporter.prototype = {
/**
* Import bookmarks from a url.
*
* @param aSpec
* url of the bookmark data.
*
@@ -283,19 +287,20 @@ BookmarkImporter.prototype = {
childIds.push(childId);
}
}
root.containerOpen = false;
for (let i = 0; i < childIds.length; i++) {
let rootItemId = childIds[i];
if (PlacesUtils.isRootItem(rootItemId)) {
- PlacesUtils.bookmarks.removeFolderChildren(rootItemId);
+ PlacesUtils.bookmarks.removeFolderChildren(rootItemId,
+ this._source);
} else {
- PlacesUtils.bookmarks.removeItem(rootItemId);
+ PlacesUtils.bookmarks.removeItem(rootItemId, this._source);
}
}
}
let searchIds = [];
let folderIdMap = [];
for (let node of batch.nodes) {
@@ -336,17 +341,17 @@ BookmarkImporter.prototype = {
}
}
// Fixup imported place: uris that contain folders
searchIds.forEach(function(aId) {
let oldURI = PlacesUtils.bookmarks.getBookmarkURI(aId);
let uri = fixupQuery(oldURI, folderIdMap);
if (!uri.equals(oldURI)) {
- PlacesUtils.bookmarks.changeBookmarkURI(aId, uri);
+ PlacesUtils.bookmarks.changeBookmarkURI(aId, uri, this._source);
}
});
deferred.resolve();
}.bind(this)
};
PlacesUtils.bookmarks.runInBatchMode(batch, null);
@@ -382,17 +387,17 @@ BookmarkImporter.prototype = {
switch (aData.type) {
case PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER:
if (aContainer == PlacesUtils.tagsFolderId) {
// Node is a tag
if (aData.children) {
aData.children.forEach(function(aChild) {
try {
PlacesUtils.tagging.tagURI(
- NetUtil.newURI(aChild.uri), [aData.title]);
+ NetUtil.newURI(aChild.uri), [aData.title], this._source);
} catch (ex) {
// Invalid tag child, skip it
}
});
return [folderIdMap, searchIds];
}
} else if (aData.annos &&
aData.annos.some(anno => anno.name == PlacesUtils.LMANNO_FEEDURI)) {
@@ -415,29 +420,32 @@ BookmarkImporter.prototype = {
if (feedURI) {
let lmPromise = PlacesUtils.livemarks.addLivemark({
title: aData.title,
feedURI: feedURI,
parentId: aContainer,
index: aIndex,
lastModified: aData.lastModified,
siteURI: siteURI,
- guid: aData.guid
- }).then(function (aLivemark) {
+ guid: aData.guid,
+ source: this._source
+ }).then(aLivemark => {
let id = aLivemark.id;
if (aData.dateAdded)
- PlacesUtils.bookmarks.setItemDateAdded(id, aData.dateAdded);
+ PlacesUtils.bookmarks.setItemDateAdded(id, aData.dateAdded,
+ this._source);
if (aData.annos && aData.annos.length)
- PlacesUtils.setAnnotationsForItem(id, aData.annos);
+ PlacesUtils.setAnnotationsForItem(id, aData.annos,
+ this._source);
});
this._importPromises.push(lmPromise);
}
} else {
id = PlacesUtils.bookmarks.createFolder(
- aContainer, aData.title, aIndex, aData.guid);
+ aContainer, aData.title, aIndex, aData.guid, this._source);
folderIdMap[aData.id] = id;
// Process children
if (aData.children) {
for (let i = 0; i < aData.children.length; i++) {
let child = aData.children[i];
let [folders, searches] =
this.importJSONNode(child, id, i, aContainer);
for (let j = 0; j < folders.length; j++) {
@@ -446,35 +454,36 @@ BookmarkImporter.prototype = {
}
searchIds = searchIds.concat(searches);
}
}
}
break;
case PlacesUtils.TYPE_X_MOZ_PLACE:
id = PlacesUtils.bookmarks.insertBookmark(
- aContainer, NetUtil.newURI(aData.uri), aIndex, aData.title, aData.guid);
+ aContainer, NetUtil.newURI(aData.uri), aIndex, aData.title, aData.guid, this._source);
if (aData.keyword) {
// POST data could be set in 2 ways:
// 1. new backups have a postData property
// 2. old backups have an item annotation
let postDataAnno = aData.annos &&
aData.annos.find(anno => anno.name == PlacesUtils.POST_DATA_ANNO);
let postData = aData.postData || (postDataAnno && postDataAnno.value);
let kwPromise = PlacesUtils.keywords.insert({ keyword: aData.keyword,
url: aData.uri,
- postData });
+ postData,
+ source: this._source });
this._importPromises.push(kwPromise);
}
if (aData.tags) {
let tags = aData.tags.split(",").filter(aTag =>
aTag.length <= Ci.nsITaggingService.MAX_TAG_LENGTH);
if (tags.length) {
try {
- PlacesUtils.tagging.tagURI(NetUtil.newURI(aData.uri), tags);
+ PlacesUtils.tagging.tagURI(NetUtil.newURI(aData.uri), tags, this._source);
} catch (ex) {
// Invalid tag child, skip it.
Cu.reportError(`Unable to set tags "${tags.join(", ")}" for ${aData.uri}: ${ex}`);
}
}
}
if (aData.charset) {
PlacesUtils.annotations.setPageAnnotation(
@@ -505,31 +514,33 @@ BookmarkImporter.prototype = {
PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, null,
Services.scriptSecurityManager.getSystemPrincipal());
} catch (ex) {
Components.utils.reportError("Failed to import favicon URI:" + ex);
}
}
break;
case PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR:
- id = PlacesUtils.bookmarks.insertSeparator(aContainer, aIndex, aData.guid);
+ id = PlacesUtils.bookmarks.insertSeparator(aContainer, aIndex, aData.guid, this._source);
break;
default:
// Unknown node type
}
// Set generic properties, valid for all nodes
if (id != -1 && aContainer != PlacesUtils.tagsFolderId &&
aGrandParentId != PlacesUtils.tagsFolderId) {
if (aData.dateAdded)
- PlacesUtils.bookmarks.setItemDateAdded(id, aData.dateAdded);
+ PlacesUtils.bookmarks.setItemDateAdded(id, aData.dateAdded,
+ this._source);
if (aData.lastModified)
- PlacesUtils.bookmarks.setItemLastModified(id, aData.lastModified);
+ PlacesUtils.bookmarks.setItemLastModified(id, aData.lastModified,
+ this._source);
if (aData.annos && aData.annos.length)
- PlacesUtils.setAnnotationsForItem(id, aData.annos);
+ PlacesUtils.setAnnotationsForItem(id, aData.annos, this._source);
}
return [folderIdMap, searchIds];
}
}
function notifyObservers(topic) {
Services.obs.notifyObservers(null, topic, "json");
--- a/toolkit/components/places/Bookmarks.jsm
+++ b/toolkit/components/places/Bookmarks.jsm
@@ -94,16 +94,27 @@ var Bookmarks = Object.freeze({
/**
* Default index used to append a bookmark-item at the end of a folder.
* This should stay consistent with nsINavBookmarksService.idl
*/
DEFAULT_INDEX: -1,
/**
+ * Bookmark change source constants, passed as optional properties and
+ * forwarded to observers. See nsINavBookmarksService.idl for an explanation.
+ */
+ SOURCES: {
+ DEFAULT: Ci.nsINavBookmarksService.SOURCE_DEFAULT,
+ SYNC: Ci.nsINavBookmarksService.SOURCE_SYNC,
+ IMPORT: Ci.nsINavBookmarksService.SOURCE_IMPORT,
+ IMPORT_REPLACE: Ci.nsINavBookmarksService.SOURCE_IMPORT_REPLACE,
+ },
+
+ /**
* Special GUIDs associated with bookmark roots.
* It's guaranteed that the roots will always have these guids.
*/
rootGuid: "root________",
menuGuid: "menu________",
toolbarGuid: "toolbar_____",
unfiledGuid: "unfiled_____",
@@ -149,16 +160,17 @@ var Bookmarks = Object.freeze({
, title: { validIf: b => [ this.TYPE_BOOKMARK
, this.TYPE_FOLDER ].includes(b.type) }
, dateAdded: { defaultValue: time
, validIf: b => !b.lastModified ||
b.dateAdded <= b.lastModified }
, lastModified: { defaultValue: time,
validIf: b => (!b.dateAdded && b.lastModified >= time) ||
(b.dateAdded && b.lastModified >= b.dateAdded) }
+ , source: { defaultValue: this.SOURCES.DEFAULT }
});
return Task.spawn(function* () {
// Ensure the parent exists.
let parent = yield fetchBookmark({ guid: insertInfo.parentGuid });
if (!parent)
throw new Error("parentGuid must be valid");
@@ -174,31 +186,32 @@ var Bookmarks = Object.freeze({
let observers = PlacesUtils.bookmarks.getObservers();
// We need the itemId to notify, though once the switch to guids is
// complete we may stop using it.
let uri = item.hasOwnProperty("url") ? PlacesUtils.toURI(item.url) : null;
let itemId = yield PlacesUtils.promiseItemId(item.guid);
notify(observers, "onItemAdded", [ itemId, parent._id, item.index,
item.type, uri, item.title || null,
PlacesUtils.toPRTime(item.dateAdded), item.guid,
- item.parentGuid ]);
+ item.parentGuid, item.source ]);
// If it's a tag, notify OnItemChanged to all bookmarks for this URL.
let isTagging = parent._parentId == PlacesUtils.tagsFolderId;
if (isTagging) {
for (let entry of (yield fetchBookmarksByURL(item))) {
notify(observers, "onItemChanged", [ entry._id, "tags", false, "",
PlacesUtils.toPRTime(entry.lastModified),
entry.type, entry._parentId,
entry.guid, entry.parentGuid,
- "" ]);
+ "", item.source ]);
}
}
// Remove non-enumerable properties.
+ delete item.source;
return Object.assign({}, item);
}.bind(this));
},
/**
* Updates a bookmark-item.
*
* Only set the properties which should be changed (undefined properties
@@ -220,37 +233,38 @@ var Bookmarks = Object.freeze({
update(info) {
// The info object is first validated here to ensure it's consistent, then
// it's compared to the existing item to remove any properties that don't
// need to be updated.
let updateInfo = validateBookmarkObject(info,
{ guid: { required: true }
, index: { requiredIf: b => b.hasOwnProperty("parentGuid")
, validIf: b => b.index >= 0 || b.index == this.DEFAULT_INDEX }
+ , source: { defaultValue: this.SOURCES.DEFAULT }
});
- // There should be at last one more property in addition to guid.
- if (Object.keys(updateInfo).length < 2)
+ // There should be at last one more property in addition to guid and source.
+ if (Object.keys(updateInfo).length < 3)
throw new Error("Not enough properties to update");
return Task.spawn(function* () {
// Ensure the item exists.
let item = yield fetchBookmark(updateInfo);
if (!item)
throw new Error("No bookmarks found for the provided GUID");
if (updateInfo.hasOwnProperty("type") && updateInfo.type != item.type)
throw new Error("The bookmark type cannot be changed");
if (updateInfo.hasOwnProperty("dateAdded") &&
updateInfo.dateAdded.getTime() != item.dateAdded.getTime())
throw new Error("The bookmark dateAdded cannot be changed");
// Remove any property that will stay the same.
removeSameValueProperties(updateInfo, item);
// Check if anything should still be updated.
- if (Object.keys(updateInfo).length < 2) {
+ if (Object.keys(updateInfo).length < 3) {
// Remove non-enumerable properties.
return Object.assign({}, item);
}
let time = (updateInfo && updateInfo.dateAdded) || new Date();
updateInfo = validateBookmarkObject(updateInfo,
{ url: { validIf: () => item.type == this.TYPE_BOOKMARK }
, title: { validIf: () => [ this.TYPE_BOOKMARK
@@ -321,64 +335,71 @@ var Bookmarks = Object.freeze({
item.lastModified != updatedItem.lastModified) {
notify(observers, "onItemChanged", [ updatedItem._id, "lastModified",
false,
`${PlacesUtils.toPRTime(updatedItem.lastModified)}`,
PlacesUtils.toPRTime(updatedItem.lastModified),
updatedItem.type,
updatedItem._parentId,
updatedItem.guid,
- updatedItem.parentGuid, "" ]);
+ updatedItem.parentGuid, "",
+ updatedItem.source ]);
}
if (updateInfo.hasOwnProperty("title")) {
notify(observers, "onItemChanged", [ updatedItem._id, "title",
false, updatedItem.title,
PlacesUtils.toPRTime(updatedItem.lastModified),
updatedItem.type,
updatedItem._parentId,
updatedItem.guid,
- updatedItem.parentGuid, "" ]);
+ updatedItem.parentGuid, "",
+ updatedItem.source ]);
}
if (updateInfo.hasOwnProperty("url")) {
notify(observers, "onItemChanged", [ updatedItem._id, "uri",
false, updatedItem.url.href,
PlacesUtils.toPRTime(updatedItem.lastModified),
updatedItem.type,
updatedItem._parentId,
updatedItem.guid,
updatedItem.parentGuid,
- item.url.href ]);
+ item.url.href,
+ updatedItem.source ]);
}
// If the item was moved, notify onItemMoved.
if (item.parentGuid != updatedItem.parentGuid ||
item.index != updatedItem.index) {
notify(observers, "onItemMoved", [ updatedItem._id, item._parentId,
item.index, updatedItem._parentId,
updatedItem.index, updatedItem.type,
updatedItem.guid, item.parentGuid,
- updatedItem.parentGuid ]);
+ updatedItem.parentGuid,
+ updatedItem.source ]);
}
// Remove non-enumerable properties.
+ delete updatedItem.source;
return Object.assign({}, updatedItem);
}.bind(this)));
}.bind(this));
},
/**
* Removes a bookmark-item.
*
* @param guidOrInfo
* The globally unique identifier of the item to remove, or an
* object representing it, as defined above.
* @param {Object} [options={}]
* Additional options that can be passed to the function.
- * Currently supports preventRemovalOfNonEmptyFolders which
- * will cause an exception to be thrown if attempting to remove
- * a folder that is not empty.
+ * Currently supports the following properties:
+ * - preventRemovalOfNonEmptyFolders: Causes an exception to be
+ * thrown when attempting to remove a folder that is not empty.
+ * - source: The change source, forwarded to all bookmark observers.
+ * Defaults to nsINavBookmarksService::SOURCE_DEFAULT.
*
* @return {Promise} resolved when the removal is complete.
* @resolves to an object representing the removed bookmark.
* @rejects if the provided guid doesn't match any existing bookmark.
* @throws if the arguments are invalid.
*/
remove(guidOrInfo, options={}) {
let info = guidOrInfo;
@@ -404,47 +425,53 @@ var Bookmarks = Object.freeze({
item = yield removeBookmark(item, options);
// Notify onItemRemoved to listeners.
let observers = PlacesUtils.bookmarks.getObservers();
let uri = item.hasOwnProperty("url") ? PlacesUtils.toURI(item.url) : null;
notify(observers, "onItemRemoved", [ item._id, item._parentId, item.index,
item.type, uri, item.guid,
- item.parentGuid ]);
+ item.parentGuid,
+ removeInfo.source ]);
let isUntagging = item._grandParentId == PlacesUtils.tagsFolderId;
if (isUntagging) {
for (let entry of (yield fetchBookmarksByURL(item))) {
notify(observers, "onItemChanged", [ entry._id, "tags", false, "",
PlacesUtils.toPRTime(entry.lastModified),
entry.type, entry._parentId,
entry.guid, entry.parentGuid,
- "" ]);
+ "", removeInfo.source ]);
}
}
// Remove non-enumerable properties.
return Object.assign({}, item);
});
},
/**
* Removes ALL bookmarks, resetting the bookmarks storage to an empty tree.
*
* Note that roots are preserved, only their children will be removed.
*
+ * @param {Object} [options={}]
+ * Additional options. Currently supports the following properties:
+ * - source: The change source, forwarded to all bookmark observers.
+ * Defaults to nsINavBookmarksService::SOURCE_DEFAULT.
+ *
* @return {Promise} resolved when the removal is complete.
* @resolves once the removal is complete.
*/
- eraseEverything: function() {
+ eraseEverything: function(options={}) {
return PlacesUtils.withConnectionWrapper("Bookmarks.jsm: eraseEverything",
db => db.executeTransaction(function* () {
const folderGuids = [this.toolbarGuid, this.menuGuid, this.unfiledGuid];
- yield removeFoldersContents(db, folderGuids);
+ yield removeFoldersContents(db, folderGuids, options);
const time = PlacesUtils.toPRTime(new Date());
for (let folderGuid of folderGuids) {
yield db.executeCached(
`UPDATE moz_bookmarks SET lastModified = :time
WHERE id IN (SELECT id FROM moz_bookmarks WHERE guid = :folderGuid )
`, { folderGuid, time });
}
}.bind(this))
@@ -643,23 +670,27 @@ var Bookmarks = Object.freeze({
*
* @param parentGuid
* The globally unique identifier of the folder whose contents should
* be reordered.
* @param orderedChildrenGuids
* Ordered array of the children's GUIDs. If this list contains
* non-existing entries they will be ignored. If the list is
* incomplete, missing entries will be appended.
+ * @param {Object} [options={}]
+ * Additional options. Currently supports the following properties:
+ * - source: The change source, forwarded to all bookmark observers.
+ * Defaults to nsINavBookmarksService::SOURCE_DEFAULT.
*
* @return {Promise} resolved when reordering is complete.
* @rejects if an error happens while reordering.
* @throws if the arguments are invalid.
*/
- reorder(parentGuid, orderedChildrenGuids) {
- let info = { guid: parentGuid };
+ reorder(parentGuid, orderedChildrenGuids, options={}) {
+ let info = { guid: parentGuid, source: this.SOURCES.DEFAULT };
info = validateBookmarkObject(info, { guid: { required: true } });
if (!Array.isArray(orderedChildrenGuids) || !orderedChildrenGuids.length)
throw new Error("Must provide a sorted array of children GUIDs.");
try {
orderedChildrenGuids.forEach(PlacesUtils.BOOKMARK_VALIDATORS.guid);
} catch (ex) {
throw new Error("Invalid GUID found in the sorted children array.");
@@ -667,25 +698,27 @@ var Bookmarks = Object.freeze({
return Task.spawn(function* () {
let parent = yield fetchBookmark(info);
if (!parent || parent.type != this.TYPE_FOLDER)
throw new Error("No folder found for the provided GUID.");
let sortedChildren = yield reorderChildren(parent, orderedChildrenGuids);
+ let { source = Ci.nsINavBookmarksService.SOURCE_DEFAULT } = options;
let observers = PlacesUtils.bookmarks.getObservers();
// Note that child.index is the old index.
for (let i = 0; i < sortedChildren.length; ++i) {
let child = sortedChildren[i];
notify(observers, "onItemMoved", [ child._id, child._parentId,
child.index, child._parentId,
i, child.type,
child.guid, child.parentGuid,
- child.parentGuid ]);
+ child.parentGuid,
+ source ]);
}
}.bind(this));
},
/**
* Searches a list of bookmark-items by a search term, url or title.
*
* IMPORTANT:
@@ -1065,28 +1098,28 @@ function fetchBookmarksByParent(info) {
return rowsToItemsArray(rows);
}));
}
////////////////////////////////////////////////////////////////////////////////
// Remove implementation.
function removeBookmark(item, options) {
- return PlacesUtils.withConnectionWrapper("Bookmarks.jsm: updateBookmark",
+ return PlacesUtils.withConnectionWrapper("Bookmarks.jsm: removeBookmark",
Task.async(function*(db) {
let isUntagging = item._grandParentId == PlacesUtils.tagsFolderId;
yield db.executeTransaction(function* transaction() {
// If it's a folder, remove its contents first.
if (item.type == Bookmarks.TYPE_FOLDER) {
if (options.preventRemovalOfNonEmptyFolders && item._childCount > 0) {
throw new Error("Cannot remove a non-empty folder.");
}
- yield removeFoldersContents(db, [item.guid]);
+ yield removeFoldersContents(db, [item.guid], options);
}
// Remove annotations first. If it's a tag, we can avoid paying that cost.
if (!isUntagging) {
// We don't go through the annotations service for this cause otherwise
// we'd get a pointless onItemChanged notification and it would also
// set lastModified to an unexpected value.
yield removeAnnotationsForItem(db, item._id);
@@ -1379,17 +1412,17 @@ var setAncestorsLastModified = Task.asyn
* Remove all descendants of one or more bookmark folders.
*
* @param db
* the Sqlite.jsm connection handle.
* @param folderGuids
* array of folder guids.
*/
var removeFoldersContents =
-Task.async(function* (db, folderGuids) {
+Task.async(function* (db, folderGuids, options) {
let itemsRemoved = [];
for (let folderGuid of folderGuids) {
let rows = yield db.executeCached(
`WITH RECURSIVE
descendants(did) AS (
SELECT b.id FROM moz_bookmarks b
JOIN moz_bookmarks p ON b.parent = p.id
WHERE p.guid = :folderGuid
@@ -1430,31 +1463,33 @@ Task.async(function* (db, folderGuids) {
updateFrecency(db, urls).then(null, Cu.reportError);
// Send onItemRemoved notifications to listeners.
// TODO (Bug 1087580): for the case of eraseEverything, this should send a
// single clear bookmarks notification rather than notifying for each
// bookmark.
// Notify listeners in reverse order to serve children before parents.
+ let { source = Ci.nsINavBookmarksService.SOURCE_DEFAULT } = options;
let observers = PlacesUtils.bookmarks.getObservers();
for (let item of itemsRemoved.reverse()) {
let uri = item.hasOwnProperty("url") ? PlacesUtils.toURI(item.url) : null;
notify(observers, "onItemRemoved", [ item._id, item._parentId,
item.index, item.type, uri,
- item.guid, item.parentGuid ]);
+ item.guid, item.parentGuid,
+ source ]);
let isUntagging = item._grandParentId == PlacesUtils.tagsFolderId;
if (isUntagging) {
for (let entry of (yield fetchBookmarksByURL(item))) {
notify(observers, "onItemChanged", [ entry._id, "tags", false, "",
PlacesUtils.toPRTime(entry.lastModified),
entry.type, entry._parentId,
entry.guid, entry.parentGuid,
- "" ]);
+ "", source ]);
}
}
}
});
/**
* Tries to insert a new place if it doesn't exist yet.
* @param url
--- a/toolkit/components/places/PlacesSyncUtils.jsm
+++ b/toolkit/components/places/PlacesSyncUtils.jsm
@@ -29,16 +29,18 @@ XPCOMUtils.defineLazyModuleGetter(this,
*/
var PlacesSyncUtils = {};
const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
const DESCRIPTION_ANNO = "bookmarkProperties/description";
const SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
const PARENT_ANNO = "sync/parent";
+const { SOURCE_SYNC } = Ci.nsINavBookmarksService;
+
const BookmarkSyncUtils = PlacesSyncUtils.bookmarks = Object.freeze({
KINDS: {
BOOKMARK: "bookmark",
// Microsummaries were removed from Places in bug 524091. For now, Sync
// treats them identically to bookmarks. Bug 745410 tracks removing them
// entirely.
MICROSUMMARY: "microsummary",
QUERY: "query",
@@ -151,37 +153,39 @@ const BookmarkSyncUtils = PlacesSyncUtil
// Notify observers.
let observers = PlacesUtils.bookmarks.getObservers();
for (let child of children) {
notify(observers, "onItemMoved", [ child.id, child.parentId,
child.oldIndex, child.parentId,
child.index, child.type,
child.guid, parentGuid,
- parentGuid ]);
+ parentGuid, SOURCE_SYNC ]);
}
})
);
}),
/**
* Removes an item from the database.
*/
remove: Task.async(function* (guid) {
- return PlacesUtils.bookmarks.remove(guid);
+ return PlacesUtils.bookmarks.remove(guid, {
+ source: SOURCE_SYNC,
+ });
}),
/**
* Removes a folder's children. This is a temporary method that can be
* replaced by `eraseEverything` once Places supports the Sync-specific
* mobile root.
*/
clear: Task.async(function* (folderGuid) {
let folderId = yield PlacesUtils.promiseItemId(folderGuid);
- PlacesUtils.bookmarks.removeFolderChildren(folderId);
+ PlacesUtils.bookmarks.removeFolderChildren(folderId, SOURCE_SYNC);
}),
/**
* Ensures an item with the |itemId| has a GUID, assigning one if necessary.
* We should never have a bookmark without a GUID, but the old Sync bookmarks
* engine code does this, so we'll match its behavior until we're sure it's
* not needed.
*
@@ -260,17 +264,19 @@ const BookmarkSyncUtils = PlacesSyncUtil
* @rejects if it's not possible to update the given bookmark.
* @throws if the arguments are invalid.
*/
update: Task.async(function* (info) {
let updateInfo = validateSyncBookmarkObject(info,
{ guid: { required: true }
, type: { validIf: () => false }
, index: { validIf: () => false }
+ , source: { validIf: () => false }
});
+ updateInfo.source = SOURCE_SYNC;
return updateSyncBookmark(updateInfo);
}),
/**
* Inserts a synced bookmark into the tree. Only Sync should call this
* method; other callers should use `Bookmarks.insert`.
*
@@ -406,17 +412,18 @@ var updateTagQueryFolder = Task.async(fu
return item;
});
var annotateOrphan = Task.async(function* (item, requestedParentGuid) {
let itemId = yield PlacesUtils.promiseItemId(item.guid);
PlacesUtils.annotations.setItemAnnotation(itemId,
PARENT_ANNO, requestedParentGuid, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
+ PlacesUtils.annotations.EXPIRE_NEVER,
+ SOURCE_SYNC);
});
var reparentOrphans = Task.async(function* (item) {
if (item.type != PlacesUtils.bookmarks.TYPE_FOLDER) {
return;
}
let orphanIds = findAnnoItems(PARENT_ANNO, item.guid);
// The annotations API returns item IDs, but the asynchronous bookmarks
@@ -432,26 +439,27 @@ var reparentOrphans = Task.async(functio
// Reparenting can fail if we have a corrupted or incomplete tree
// where an item's parent is one of its descendants.
BookmarkSyncLog.trace(`reparentOrphans: Attempting to move item ${
orphanGuids[i]} to new parent ${item.guid}`);
yield PlacesUtils.bookmarks.update({
guid: orphanGuids[i],
parentGuid: item.guid,
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
+ source: SOURCE_SYNC,
});
isReparented = true;
} catch (ex) {
BookmarkSyncLog.error(`reparentOrphans: Failed to reparent item ${
orphanGuids[i]} to ${item.guid}`, ex);
}
if (isReparented) {
// Remove the annotation once we've reparented the item.
PlacesUtils.annotations.removeItemAnnotation(orphanIds[i],
- PARENT_ANNO);
+ PARENT_ANNO, SOURCE_SYNC);
}
}
});
// Inserts a synced bookmark into the database.
var insertSyncBookmark = Task.async(function* (insertInfo) {
let requestedParentGuid = insertInfo.parentGuid;
let parent = yield PlacesUtils.bookmarks.fetch(requestedParentGuid);
@@ -511,56 +519,61 @@ var insertSyncLivemark = Task.async(func
let siteURI = insertInfo.site ? PlacesUtils.toURI(insertInfo.site) : null;
let item = yield PlacesUtils.livemarks.addLivemark({
title: insertInfo.title,
parentGuid: insertInfo.parentGuid,
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
feedURI,
siteURI,
guid: insertInfo.guid,
+ source: SOURCE_SYNC,
});
return insertBookmarkMetadata(item.id, item, insertInfo);
});
// Sets annotations, keywords, and tags on a new synced bookmark.
var insertBookmarkMetadata = Task.async(function* (itemId, item, insertInfo) {
if (insertInfo.query) {
PlacesUtils.annotations.setItemAnnotation(itemId,
SMART_BOOKMARKS_ANNO, insertInfo.query, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
+ PlacesUtils.annotations.EXPIRE_NEVER,
+ SOURCE_SYNC);
item.query = insertInfo.query;
}
try {
item.tags = yield tagItem(item, insertInfo.tags);
} catch (ex) {
BookmarkSyncLog.warn(`insertBookmarkMetadata: Error tagging item ${
item.guid}`, ex);
}
if (insertInfo.keyword) {
yield PlacesUtils.keywords.insert({
keyword: insertInfo.keyword,
url: item.url.href,
+ source: SOURCE_SYNC,
});
item.keyword = insertInfo.keyword;
}
if (insertInfo.description) {
PlacesUtils.annotations.setItemAnnotation(itemId,
DESCRIPTION_ANNO, insertInfo.description, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
+ PlacesUtils.annotations.EXPIRE_NEVER,
+ SOURCE_SYNC);
item.description = insertInfo.description;
}
if (insertInfo.loadInSidebar) {
PlacesUtils.annotations.setItemAnnotation(itemId,
SIDEBAR_ANNO, insertInfo.loadInSidebar, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
+ PlacesUtils.annotations.EXPIRE_NEVER,
+ SOURCE_SYNC);
item.loadInSidebar = insertInfo.loadInSidebar;
}
return item;
});
// Determines the Sync record kind for an existing bookmark.
var getKindForItem = Task.async(function* (item) {
@@ -654,17 +667,20 @@ var updateSyncBookmark = Task.async(func
BookmarkSyncLog.debug(`updateSyncBookmark: Local ${
oldItem.guid} and remote ${
updateInfo.guid} livemarks have different URLs`);
}
}
if (shouldReinsert) {
delete updateInfo.source;
let newItem = validateNewBookmark(updateInfo);
- yield PlacesUtils.bookmarks.remove(oldItem.guid);
+ yield PlacesUtils.bookmarks.remove({
+ guid: oldItem.guid,
+ source: SOURCE_SYNC,
+ });
// A reinsertion likely indicates a confused client, since there aren't
// public APIs for changing livemark URLs or an item's kind (e.g., turning
// a folder into a separator while preserving its annos and position).
// This might be a good case to repair later; for now, we assume Sync has
// passed a complete record for the new item, and don't try to merge
// `oldItem` with `updateInfo`.
return insertSyncBookmark(newItem);
}
@@ -730,55 +746,62 @@ var updateBookmarkMetadata = Task.async(
}
if (updateInfo.hasOwnProperty("keyword")) {
// Unconditionally remove the old keyword.
let entry = yield PlacesUtils.keywords.fetch({
url: oldItem.url.href,
});
if (entry) {
- yield PlacesUtils.keywords.remove(entry.keyword);
+ yield PlacesUtils.keywords.remove({
+ keyword: entry.keyword,
+ source: SOURCE_SYNC,
+ });
}
if (updateInfo.keyword) {
yield PlacesUtils.keywords.insert({
keyword: updateInfo.keyword,
url: newItem.url.href,
+ source: SOURCE_SYNC,
});
}
newItem.keyword = updateInfo.keyword;
}
if (updateInfo.hasOwnProperty("description")) {
if (updateInfo.description) {
PlacesUtils.annotations.setItemAnnotation(itemId,
DESCRIPTION_ANNO, updateInfo.description, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
+ PlacesUtils.annotations.EXPIRE_NEVER,
+ SOURCE_SYNC);
} else {
PlacesUtils.annotations.removeItemAnnotation(itemId,
- DESCRIPTION_ANNO);
+ DESCRIPTION_ANNO, SOURCE_SYNC);
}
newItem.description = updateInfo.description;
}
if (updateInfo.hasOwnProperty("loadInSidebar")) {
if (updateInfo.loadInSidebar) {
PlacesUtils.annotations.setItemAnnotation(itemId,
SIDEBAR_ANNO, updateInfo.loadInSidebar, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
+ PlacesUtils.annotations.EXPIRE_NEVER,
+ SOURCE_SYNC);
} else {
PlacesUtils.annotations.removeItemAnnotation(itemId,
- SIDEBAR_ANNO);
+ SIDEBAR_ANNO, SOURCE_SYNC);
}
newItem.loadInSidebar = updateInfo.loadInSidebar;
}
if (updateInfo.hasOwnProperty("query")) {
PlacesUtils.annotations.setItemAnnotation(itemId,
SMART_BOOKMARKS_ANNO, updateInfo.query, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
+ PlacesUtils.annotations.EXPIRE_NEVER,
+ SOURCE_SYNC);
newItem.query = updateInfo.query;
}
return newItem;
});
function generateGuid(db) {
return db.executeCached("SELECT GENERATE_GUID() AS guid").then(rows =>
@@ -809,16 +832,18 @@ function validateNewBookmark(info) {
{ kind: { required: true }
// Explicitly prevent callers from passing types.
, type: { validIf: () => false }
// Because Sync applies bookmarks as it receives them, it doesn't pass
// an index. Instead, Sync calls `BookmarkSyncUtils.order` at the end of
// the sync, which orders children according to their placement in the
// `BookmarkFolder::children` array.
, index: { validIf: () => false }
+ // This module always uses `nsINavBookmarksService::SOURCE_SYNC`.
+ , source: { validIf: () => false }
, guid: { required: true }
, url: { requiredIf: b => [ BookmarkSyncUtils.KINDS.BOOKMARK
, BookmarkSyncUtils.KINDS.MICROSUMMARY
, BookmarkSyncUtils.KINDS.QUERY ].includes(b.kind)
, validIf: b => [ BookmarkSyncUtils.KINDS.BOOKMARK
, BookmarkSyncUtils.KINDS.MICROSUMMARY
, BookmarkSyncUtils.KINDS.QUERY ].includes(b.kind) }
, parentGuid: { required: true }
@@ -847,16 +872,17 @@ function validateNewBookmark(info) {
, site: { validIf: b => b.kind == BookmarkSyncUtils.KINDS.LIVEMARK }
});
// Sync doesn't track modification times, so use the default.
let time = new Date();
insertInfo.dateAdded = insertInfo.lastModified = time;
insertInfo.type = getTypeForKind(insertInfo.kind);
+ insertInfo.source = SOURCE_SYNC;
return insertInfo;
}
function findAnnoItems(anno, val) {
let annos = PlacesUtils.annotations;
return annos.getItemsWithAnnotation(anno, {}).filter(id =>
annos.getItemAnnotation(id, anno) == val);
@@ -869,36 +895,37 @@ var tagItem = Task.async(function (item,
// Remove leading and trailing whitespace, then filter out empty tags.
let newTags = tags.map(tag => tag.trim()).filter(Boolean);
// Removing the last tagged item will also remove the tag. To preserve
// tag IDs, we temporarily tag a dummy URI, ensuring the tags exist.
let dummyURI = PlacesUtils.toURI("about:weave#BStore_tagURI");
let bookmarkURI = PlacesUtils.toURI(item.url.href);
- PlacesUtils.tagging.tagURI(dummyURI, newTags);
- PlacesUtils.tagging.untagURI(bookmarkURI, null);
- PlacesUtils.tagging.tagURI(bookmarkURI, newTags);
- PlacesUtils.tagging.untagURI(dummyURI, null);
+ PlacesUtils.tagging.tagURI(dummyURI, newTags, SOURCE_SYNC);
+ PlacesUtils.tagging.untagURI(bookmarkURI, null, SOURCE_SYNC);
+ PlacesUtils.tagging.tagURI(bookmarkURI, newTags, SOURCE_SYNC);
+ PlacesUtils.tagging.untagURI(dummyURI, null, SOURCE_SYNC);
return newTags;
});
// `PlacesUtils.bookmarks.update` checks if we've supplied enough properties,
// but doesn't know about additional Sync record properties. We check this to
// avoid having it throw in case we only pass Sync-specific properties, like
// `{ guid, tags }`.
function shouldUpdateBookmark(updateInfo) {
let propsToUpdate = 0;
for (let prop in PlacesUtils.BOOKMARK_VALIDATORS) {
if (!updateInfo.hasOwnProperty(prop)) {
continue;
}
- // We should have at least one more property, in addition to `guid`.
- if (++propsToUpdate >= 2) {
+ // We should have at least one more property, in addition to `guid` and
+ // `source`.
+ if (++propsToUpdate >= 3) {
return true;
}
}
return false;
}
var getTagFolder = Task.async(function* (tag) {
let db = yield PlacesUtils.promiseDBConnection();
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -101,17 +101,17 @@ function notify(observers, notification,
/**
* Sends a keyword change notification.
*
* @param url
* the url to notify about.
* @param keyword
* The keyword to notify, or empty string if a keyword was removed.
*/
-function* notifyKeywordChange(url, keyword) {
+function* notifyKeywordChange(url, keyword, source) {
// Notify bookmarks about the removal.
let bookmarks = [];
yield PlacesUtils.bookmarks.fetch({ url }, b => bookmarks.push(b));
// We don't want to yield in the gIgnoreKeywordNotifications section.
for (let bookmark of bookmarks) {
bookmark.id = yield PlacesUtils.promiseItemId(bookmark.guid);
bookmark.parentId = yield PlacesUtils.promiseItemId(bookmark.parentGuid);
}
@@ -119,17 +119,17 @@ function* notifyKeywordChange(url, keywo
gIgnoreKeywordNotifications = true;
for (let bookmark of bookmarks) {
notify(observers, "onItemChanged", [ bookmark.id, "keyword", false,
keyword,
bookmark.lastModified * 1000,
bookmark.type,
bookmark.parentId,
bookmark.guid, bookmark.parentGuid,
- ""
+ "", source
]);
}
gIgnoreKeywordNotifications = false;
}
/**
* Serializes the given node in JSON format.
*
@@ -245,17 +245,19 @@ const BOOKMARK_VALIDATORS = Object.freez
(val instanceof Ci.nsIURI && val.spec.length <= DB_URL_LENGTH_MAX) ||
(val instanceof URL && val.href.length <= DB_URL_LENGTH_MAX)
).call(this, v);
if (typeof(v) === "string")
return new URL(v);
if (v instanceof Ci.nsIURI)
return new URL(v.spec);
return v;
- }
+ },
+ source: simpleValidateFunc(v => Number.isInteger(v) &&
+ Object.values(PlacesUtils.bookmarks.SOURCES).includes(v)),
});
// Sync bookmark records can contain additional properties.
const SYNC_BOOKMARK_VALIDATORS = Object.freeze(Object.assign({
// Sync uses kinds instead of types, which distinguish between livemarks
// and smart bookmarks.
kind: simpleValidateFunc(v => typeof v == "string" &&
Object.values(PlacesSyncUtils.bookmarks.KINDS).includes(v)),
@@ -1058,29 +1060,29 @@ this.PlacesUtils = {
* Annotate an item with a batch of annotations.
* @param aItemId
* The identifier of the item for which annotations are to be set
* @param aAnnotations
* Array of objects, each containing the following properties:
* name, flags, expires.
* If the value for an annotation is not set it will be removed.
*/
- setAnnotationsForItem: function PU_setAnnotationsForItem(aItemId, aAnnos) {
+ setAnnotationsForItem: function PU_setAnnotationsForItem(aItemId, aAnnos, aSource) {
var annosvc = this.annotations;
aAnnos.forEach(function(anno) {
if (anno.value === undefined || anno.value === null) {
- annosvc.removeItemAnnotation(aItemId, anno.name);
+ annosvc.removeItemAnnotation(aItemId, anno.name, aSource);
}
else {
let flags = ("flags" in anno) ? anno.flags : 0;
let expires = ("expires" in anno) ?
anno.expires : Ci.nsIAnnotationService.EXPIRE_NEVER;
annosvc.setItemAnnotation(aItemId, anno.name, anno.value, flags,
- expires);
+ expires, aSource);
}
});
},
// Identifier getters for special folders.
// You should use these everywhere PlacesUtils is available to avoid XPCOM
// traversal just to get roots' ids.
get placesRootId() {
@@ -2198,17 +2200,18 @@ var Keywords = {
if (!("keyword" in keywordEntry) || !keywordEntry.keyword ||
typeof(keywordEntry.keyword) != "string")
throw new Error("Invalid keyword");
if (("postData" in keywordEntry) && keywordEntry.postData &&
typeof(keywordEntry.postData) != "string")
throw new Error("Invalid POST data");
if (!("url" in keywordEntry))
throw new Error("undefined is not a valid URL");
- let { keyword, url } = keywordEntry;
+ let { keyword, url,
+ source = Ci.nsINavBookmarksService.SOURCE_DEFAULT } = keywordEntry;
keyword = keyword.trim().toLowerCase();
let postData = keywordEntry.postData || null;
// This also checks href for validity
url = new URL(url);
return PlacesUtils.withConnectionWrapper("Keywords.insert", Task.async(function*(db) {
let cache = yield gKeywordsCachePromise;
@@ -2226,17 +2229,17 @@ var Keywords = {
// trigger.
if (oldEntry) {
yield db.executeCached(
`UPDATE moz_keywords
SET place_id = (SELECT id FROM moz_places WHERE url_hash = hash(:url) AND url = :url),
post_data = :post_data
WHERE keyword = :keyword
`, { url: url.href, keyword: keyword, post_data: postData });
- yield notifyKeywordChange(oldEntry.url.href, "");
+ yield notifyKeywordChange(oldEntry.url.href, "", source);
} else {
// An entry for the given page could be missing, in such a case we need to
// create it. The IGNORE conflict can trigger on `guid`.
yield db.executeCached(
`INSERT OR IGNORE INTO moz_places (url, url_hash, rev_host, hidden, frecency, guid)
VALUES (:url, hash(:url), :rev_host, 0, :frecency,
IFNULL((SELECT guid FROM moz_places WHERE url_hash = hash(:url) AND url = :url),
GENERATE_GUID()))
@@ -2246,45 +2249,52 @@ var Keywords = {
`INSERT INTO moz_keywords (keyword, place_id, post_data)
VALUES (:keyword, (SELECT id FROM moz_places WHERE url_hash = hash(:url) AND url = :url), :post_data)
`, { url: url.href, keyword: keyword, post_data: postData });
}
cache.set(keyword, { keyword, url, postData });
// In any case, notify about the new keyword.
- yield notifyKeywordChange(url.href, keyword);
+ yield notifyKeywordChange(url.href, keyword, source);
}.bind(this))
);
},
/**
* Removes a keyword.
*
* @param keyword
* The keyword to remove.
* @return {Promise}
* @resolves when the removal is complete.
*/
- remove(keyword) {
- if (!keyword || typeof(keyword) != "string")
+ remove(keywordOrEntry) {
+ if (typeof(keywordOrEntry) == "string")
+ keywordOrEntry = { keyword: keywordOrEntry };
+
+ if (keywordOrEntry === null || typeof(keywordOrEntry) != "object" ||
+ !keywordOrEntry.keyword || typeof keywordOrEntry.keyword != "string")
throw new Error("Invalid keyword");
- keyword = keyword.trim().toLowerCase();
+
+ let { keyword,
+ source = Ci.nsINavBookmarksService.SOURCE_DEFAULT } = keywordOrEntry;
+ keyword = keywordOrEntry.keyword.trim().toLowerCase();
return PlacesUtils.withConnectionWrapper("Keywords.remove", Task.async(function*(db) {
let cache = yield gKeywordsCachePromise;
if (!cache.has(keyword))
return;
let { url } = cache.get(keyword);
cache.delete(keyword);
yield db.execute(`DELETE FROM moz_keywords WHERE keyword = :keyword`,
{ keyword });
// Notify bookmarks about the removal.
- yield notifyKeywordChange(url.href, "");
+ yield notifyKeywordChange(url.href, "", source);
}.bind(this))) ;
}
};
// Set by the keywords API to distinguish notifications fired by the old API.
// Once the old API will be gone, we can remove this and stop observing.
var gIgnoreKeywordNotifications = false;
--- a/toolkit/components/places/nsAnnotationService.cpp
+++ b/toolkit/components/places/nsAnnotationService.cpp
@@ -258,17 +258,18 @@ nsAnnotationService::SetPageAnnotation(n
}
NS_IMETHODIMP
nsAnnotationService::SetItemAnnotation(int64_t aItemId,
const nsACString& aName,
nsIVariant* aValue,
int32_t aFlags,
- uint16_t aExpiration)
+ uint16_t aExpiration,
+ uint16_t aSource)
{
PROFILER_LABEL("AnnotationService", "SetItemAnnotation",
js::ProfileEntry::Category::OTHER);
NS_ENSURE_ARG_MIN(aItemId, 1);
NS_ENSURE_ARG(aValue);
if (aExpiration == EXPIRE_WITH_HISTORY)
@@ -285,59 +286,59 @@ nsAnnotationService::SetItemAnnotation(i
case nsIDataType::VTYPE_UINT16:
case nsIDataType::VTYPE_INT32:
case nsIDataType::VTYPE_UINT32:
case nsIDataType::VTYPE_BOOL: {
int32_t valueInt;
rv = aValue->GetAsInt32(&valueInt);
if (NS_SUCCEEDED(rv)) {
NS_ENSURE_SUCCESS(rv, rv);
- rv = SetItemAnnotationInt32(aItemId, aName, valueInt, aFlags, aExpiration);
+ rv = SetItemAnnotationInt32(aItemId, aName, valueInt, aFlags, aExpiration, aSource);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// Fall through int64_t case otherwise.
MOZ_FALLTHROUGH;
}
case nsIDataType::VTYPE_INT64:
case nsIDataType::VTYPE_UINT64: {
int64_t valueLong;
rv = aValue->GetAsInt64(&valueLong);
if (NS_SUCCEEDED(rv)) {
NS_ENSURE_SUCCESS(rv, rv);
- rv = SetItemAnnotationInt64(aItemId, aName, valueLong, aFlags, aExpiration);
+ rv = SetItemAnnotationInt64(aItemId, aName, valueLong, aFlags, aExpiration, aSource);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// Fall through double case otherwise.
MOZ_FALLTHROUGH;
}
case nsIDataType::VTYPE_FLOAT:
case nsIDataType::VTYPE_DOUBLE: {
double valueDouble;
rv = aValue->GetAsDouble(&valueDouble);
NS_ENSURE_SUCCESS(rv, rv);
- rv = SetItemAnnotationDouble(aItemId, aName, valueDouble, aFlags, aExpiration);
+ rv = SetItemAnnotationDouble(aItemId, aName, valueDouble, aFlags, aExpiration, aSource);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
case nsIDataType::VTYPE_CHAR:
case nsIDataType::VTYPE_WCHAR:
case nsIDataType::VTYPE_DOMSTRING:
case nsIDataType::VTYPE_CHAR_STR:
case nsIDataType::VTYPE_WCHAR_STR:
case nsIDataType::VTYPE_STRING_SIZE_IS:
case nsIDataType::VTYPE_WSTRING_SIZE_IS:
case nsIDataType::VTYPE_UTF8STRING:
case nsIDataType::VTYPE_CSTRING:
case nsIDataType::VTYPE_ASTRING: {
nsAutoString stringValue;
rv = aValue->GetAsAString(stringValue);
NS_ENSURE_SUCCESS(rv, rv);
- rv = SetItemAnnotationString(aItemId, aName, stringValue, aFlags, aExpiration);
+ rv = SetItemAnnotationString(aItemId, aName, stringValue, aFlags, aExpiration, aSource);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
}
return NS_ERROR_NOT_IMPLEMENTED;
}
@@ -361,28 +362,29 @@ nsAnnotationService::SetPageAnnotationSt
}
NS_IMETHODIMP
nsAnnotationService::SetItemAnnotationString(int64_t aItemId,
const nsACString& aName,
const nsAString& aValue,
int32_t aFlags,
- uint16_t aExpiration)
+ uint16_t aExpiration,
+ uint16_t aSource)
{
NS_ENSURE_ARG_MIN(aItemId, 1);
if (aExpiration == EXPIRE_WITH_HISTORY)
return NS_ERROR_INVALID_ARG;
nsresult rv = SetAnnotationStringInternal(nullptr, aItemId, aName, aValue,
aFlags, aExpiration);
NS_ENSURE_SUCCESS(rv, rv);
- NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName));
+ NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName, aSource));
return NS_OK;
}
nsresult
nsAnnotationService::SetAnnotationInt32Internal(nsIURI* aURI,
int64_t aItemId,
@@ -431,28 +433,29 @@ nsAnnotationService::SetPageAnnotationIn
}
NS_IMETHODIMP
nsAnnotationService::SetItemAnnotationInt32(int64_t aItemId,
const nsACString& aName,
int32_t aValue,
int32_t aFlags,
- uint16_t aExpiration)
+ uint16_t aExpiration,
+ uint16_t aSource)
{
NS_ENSURE_ARG_MIN(aItemId, 1);
if (aExpiration == EXPIRE_WITH_HISTORY)
return NS_ERROR_INVALID_ARG;
nsresult rv = SetAnnotationInt32Internal(nullptr, aItemId, aName, aValue,
aFlags, aExpiration);
NS_ENSURE_SUCCESS(rv, rv);
- NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName));
+ NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName, aSource));
return NS_OK;
}
nsresult
nsAnnotationService::SetAnnotationInt64Internal(nsIURI* aURI,
int64_t aItemId,
@@ -501,28 +504,29 @@ nsAnnotationService::SetPageAnnotationIn
}
NS_IMETHODIMP
nsAnnotationService::SetItemAnnotationInt64(int64_t aItemId,
const nsACString& aName,
int64_t aValue,
int32_t aFlags,
- uint16_t aExpiration)
+ uint16_t aExpiration,
+ uint16_t aSource)
{
NS_ENSURE_ARG_MIN(aItemId, 1);
if (aExpiration == EXPIRE_WITH_HISTORY)
return NS_ERROR_INVALID_ARG;
nsresult rv = SetAnnotationInt64Internal(nullptr, aItemId, aName, aValue,
aFlags, aExpiration);
NS_ENSURE_SUCCESS(rv, rv);
- NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName));
+ NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName, aSource));
return NS_OK;
}
nsresult
nsAnnotationService::SetAnnotationDoubleInternal(nsIURI* aURI,
int64_t aItemId,
@@ -571,28 +575,29 @@ nsAnnotationService::SetPageAnnotationDo
}
NS_IMETHODIMP
nsAnnotationService::SetItemAnnotationDouble(int64_t aItemId,
const nsACString& aName,
double aValue,
int32_t aFlags,
- uint16_t aExpiration)
+ uint16_t aExpiration,
+ uint16_t aSource)
{
NS_ENSURE_ARG_MIN(aItemId, 1);
if (aExpiration == EXPIRE_WITH_HISTORY)
return NS_ERROR_INVALID_ARG;
nsresult rv = SetAnnotationDoubleInternal(nullptr, aItemId, aName, aValue,
aFlags, aExpiration);
NS_ENSURE_SUCCESS(rv, rv);
- NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName));
+ NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName, aSource));
return NS_OK;
}
NS_IMETHODIMP
nsAnnotationService::GetPageAnnotationString(nsIURI* aURI,
const nsACString& aName,
nsAString& _retval)
@@ -1407,24 +1412,25 @@ nsAnnotationService::RemovePageAnnotatio
NOTIFY_ANNOS_OBSERVERS(OnPageAnnotationRemoved(aURI, aName));
return NS_OK;
}
NS_IMETHODIMP
nsAnnotationService::RemoveItemAnnotation(int64_t aItemId,
- const nsACString& aName)
+ const nsACString& aName,
+ uint16_t aSource)
{
NS_ENSURE_ARG_MIN(aItemId, 1);
nsresult rv = RemoveAnnotationInternal(nullptr, aItemId, aName);
NS_ENSURE_SUCCESS(rv, rv);
- NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationRemoved(aItemId, aName));
+ NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationRemoved(aItemId, aName, aSource));
return NS_OK;
}
NS_IMETHODIMP
nsAnnotationService::RemovePageAnnotations(nsIURI* aURI)
{
@@ -1447,34 +1453,36 @@ nsAnnotationService::RemovePageAnnotatio
// Update observers
NOTIFY_ANNOS_OBSERVERS(OnPageAnnotationRemoved(aURI, EmptyCString()));
return NS_OK;
}
NS_IMETHODIMP
-nsAnnotationService::RemoveItemAnnotations(int64_t aItemId)
+nsAnnotationService::RemoveItemAnnotations(int64_t aItemId,
+ uint16_t aSource)
{
NS_ENSURE_ARG_MIN(aItemId, 1);
// Should this be precompiled or a getter?
nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
"DELETE FROM moz_items_annos WHERE item_id = :item_id"
);
NS_ENSURE_STATE(statement);
mozStorageStatementScoper scoper(statement);
nsresult rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
- NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationRemoved(aItemId, EmptyCString()));
+ NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationRemoved(aItemId, EmptyCString(),
+ aSource));
return NS_OK;
}
/**
* @note If we use annotations for some standard items like GeckoFlags, it
* might be a good idea to blacklist these standard annotations from this
@@ -1560,17 +1568,18 @@ nsAnnotationService::CopyPageAnnotations
return NS_OK;
}
NS_IMETHODIMP
nsAnnotationService::CopyItemAnnotations(int64_t aSourceItemId,
int64_t aDestItemId,
- bool aOverwriteDest)
+ bool aOverwriteDest,
+ uint16_t aSource)
{
NS_ENSURE_ARG_MIN(aSourceItemId, 1);
NS_ENSURE_ARG_MIN(aDestItemId, 1);
mozStorageTransaction transaction(mDB->MainConn(), false);
nsCOMPtr<mozIStorageStatement> sourceStmt = mDB->GetStatement(
"SELECT n.id, n.name, a2.id "
@@ -1608,17 +1617,17 @@ nsAnnotationService::CopyItemAnnotations
nsAutoCString annoName;
rv = sourceStmt->GetUTF8String(1, annoName);
NS_ENSURE_SUCCESS(rv, rv);
int64_t annoExistsOnDest = sourceStmt->AsInt64(2);
if (annoExistsOnDest) {
if (!aOverwriteDest)
continue;
- rv = RemoveItemAnnotation(aDestItemId, annoName);
+ rv = RemoveItemAnnotation(aDestItemId, annoName, aSource);
NS_ENSURE_SUCCESS(rv, rv);
}
// Copy the annotation.
mozStorageStatementScoper scoper(copyStmt);
rv = copyStmt->BindInt64ByName(NS_LITERAL_CSTRING("dest_item_id"), aDestItemId);
NS_ENSURE_SUCCESS(rv, rv);
rv = copyStmt->BindInt64ByName(NS_LITERAL_CSTRING("source_item_id"), aSourceItemId);
@@ -1626,17 +1635,17 @@ nsAnnotationService::CopyItemAnnotations
rv = copyStmt->BindInt64ByName(NS_LITERAL_CSTRING("name_id"), annoNameID);
NS_ENSURE_SUCCESS(rv, rv);
rv = copyStmt->BindInt64ByName(NS_LITERAL_CSTRING("date"), PR_Now());
NS_ENSURE_SUCCESS(rv, rv);
rv = copyStmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
- NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aDestItemId, annoName));
+ NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aDestItemId, annoName, aSource));
}
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
--- a/toolkit/components/places/nsIAnnotationService.idl
+++ b/toolkit/components/places/nsIAnnotationService.idl
@@ -14,27 +14,29 @@ interface nsIAnnotationObserver : nsISup
{
/**
* Called when an annotation value is set. It could be a new annotation,
* or it could be a new value for an existing annotation.
*/
void onPageAnnotationSet(in nsIURI aPage,
in AUTF8String aName);
void onItemAnnotationSet(in long long aItemId,
- in AUTF8String aName);
+ in AUTF8String aName,
+ in unsigned short aSource);
/**
* Called when an annotation is deleted. If aName is empty, then ALL
* annotations for the given URI have been deleted. This is not called when
* annotations are expired (normally happens when the app exits).
*/
void onPageAnnotationRemoved(in nsIURI aURI,
in AUTF8String aName);
void onItemAnnotationRemoved(in long long aItemId,
- in AUTF8String aName);
+ in AUTF8String aName,
+ in unsigned short aSource);
};
[scriptable, uuid(D4CDAAB1-8EEC-47A8-B420-AD7CB333056A)]
interface nsIAnnotationService : nsISupports
{
/**
* Valid values for aExpiration, which sets the expiration policy for your
* annotation. The times for the days, weeks and months policies are
@@ -102,16 +104,20 @@ interface nsIAnnotationService : nsISupp
* user has no way of knowing it, potentially violating their privacy
* expectations about actions such as "Clear history".
* If there is an important annotation that the user or extension wants to
* keep, you should add a bookmark for the page and use an EXPIRE_NEVER
* annotation. This will ensure the annotation exists until the item is
* removed by the user.
* See EXPIRE_* constants above for further information.
*
+ * For item annotations, aSource should be a change source constant from
+ * nsINavBookmarksService::SOURCE_*, and defaults to SOURCE_DEFAULT if
+ * omitted.
+ *
* The annotation "favicon" is special. Favicons are stored in the favicon
* service, but are special cased in the protocol handler so they look like
* annotations. Do not set favicons using this service, it will not work.
*
* Only C++ consumers may use the type-specific methods.
*
* @throws NS_ERROR_ILLEGAL_VALUE if the page or the bookmark doesn't exist.
*/
@@ -119,82 +125,87 @@ interface nsIAnnotationService : nsISupp
in AUTF8String aName,
in nsIVariant aValue,
in long aFlags,
in unsigned short aExpiration);
void setItemAnnotation(in long long aItemId,
in AUTF8String aName,
in nsIVariant aValue,
in long aFlags,
- in unsigned short aExpiration);
+ in unsigned short aExpiration,
+ [optional] in unsigned short aSource);
/**
* @throws NS_ERROR_ILLEGAL_VALUE if the page or the bookmark doesn't exist.
*/
[noscript] void setPageAnnotationString(in nsIURI aURI,
in AUTF8String aName,
in AString aValue,
in long aFlags,
in unsigned short aExpiration);
[noscript] void setItemAnnotationString(in long long aItemId,
in AUTF8String aName,
in AString aValue,
in long aFlags,
- in unsigned short aExpiration);
+ in unsigned short aExpiration,
+ [optional] in unsigned short aSource);
/**
* Sets an annotation just like setAnnotationString, but takes an Int32 as
* input.
*
* @throws NS_ERROR_ILLEGAL_VALUE if the page or the bookmark doesn't exist.
*/
[noscript] void setPageAnnotationInt32(in nsIURI aURI,
in AUTF8String aName,
in long aValue,
in long aFlags,
in unsigned short aExpiration);
[noscript] void setItemAnnotationInt32(in long long aItemId,
in AUTF8String aName,
in long aValue,
in long aFlags,
- in unsigned short aExpiration);
+ in unsigned short aExpiration,
+ [optional] in unsigned short aSource);
/**
* Sets an annotation just like setAnnotationString, but takes an Int64 as
* input.
*
* @throws NS_ERROR_ILLEGAL_VALUE if the page or the bookmark doesn't exist.
*/
[noscript] void setPageAnnotationInt64(in nsIURI aURI,
in AUTF8String aName,
in long long aValue,
in long aFlags,
in unsigned short aExpiration);
[noscript] void setItemAnnotationInt64(in long long aItemId,
in AUTF8String aName,
in long long aValue,
in long aFlags,
- in unsigned short aExpiration);
+ in unsigned short aExpiration,
+ [optional] in unsigned short aSource);
/**
* Sets an annotation just like setAnnotationString, but takes a double as
* input.
*
* @throws NS_ERROR_ILLEGAL_VALUE if the page or the bookmark doesn't exist.
*/
[noscript] void setPageAnnotationDouble(in nsIURI aURI,
in AUTF8String aName,
in double aValue,
in long aFlags,
in unsigned short aExpiration);
[noscript] void setItemAnnotationDouble(in long long aItemId,
in AUTF8String aName,
in double aValue,
in long aFlags,
- in unsigned short aExpiration);
+ in unsigned short aExpiration,
+ [optional] in unsigned short aSource);
/**
* Retrieves the value of a given annotation. Throws an error if the
* annotation does not exist. C++ consumers may use the type-specific
* methods.
*
* The type-specific methods throw if the given annotation is set in
* a different type.
@@ -322,41 +333,44 @@ interface nsIAnnotationService : nsISupp
/**
* Removes a specific annotation. Succeeds even if the annotation is
* not found.
*/
void removePageAnnotation(in nsIURI aURI,
in AUTF8String aName);
void removeItemAnnotation(in long long aItemId,
- in AUTF8String aName);
+ in AUTF8String aName,
+ [optional] in unsigned short aSource);
/**
* Removes all annotations for the given page/item.
* We may want some other similar functions to get annotations with given
* flags (once we have flags defined).
*/
void removePageAnnotations(in nsIURI aURI);
- void removeItemAnnotations(in long long aItemId);
+ void removeItemAnnotations(in long long aItemId,
+ [optional] in unsigned short aSource);
/**
* Copies all annotations from the source to the destination URI/item. If
* the destination already has an annotation with the same name as one on
* the source, it will be overwritten if aOverwriteDest is set. Otherwise,
* the original annotation will be preferred.
*
* All the source annotations will stay as-is. If you don't want them
* any more, use removePageAnnotations on that URI.
*/
void copyPageAnnotations(in nsIURI aSourceURI,
in nsIURI aDestURI,
in boolean aOverwriteDest);
void copyItemAnnotations(in long long aSourceItemId,
in long long aDestItemId,
- in boolean aOverwriteDest);
+ in boolean aOverwriteDest,
+ [optional] in unsigned short aSource);
/**
* Adds an annotation observer. The annotation service will keep an owning
* reference to the observer object.
*/
void addObserver(in nsIAnnotationObserver aObserver);
--- a/toolkit/components/places/nsINavBookmarksService.idl
+++ b/toolkit/components/places/nsINavBookmarksService.idl
@@ -51,26 +51,30 @@ interface nsINavBookmarkObserver : nsISu
* @param aTitle
* The title of the added item.
* @param aDateAdded
* The stored date added value, in microseconds from the epoch.
* @param aGuid
* The unique ID associated with the item.
* @param aParentGuid
* The unique ID associated with the item's parent.
+ * @param aSource
+ * A change source constant from nsINavBookmarksService::SOURCE_*,
+ * passed to the method that notifies the observer.
*/
void onItemAdded(in long long aItemId,
in long long aParentId,
in long aIndex,
in unsigned short aItemType,
in nsIURI aURI,
in AUTF8String aTitle,
in PRTime aDateAdded,
in ACString aGuid,
- in ACString aParentGuid);
+ in ACString aParentGuid,
+ in unsigned short aSource);
/**
* Notifies that an item was removed. Called after the actual remove took
* place.
* When an item is removed, all the items following it in the same folder
* will have their index shifted down, but no additional notifications will
* be sent.
*
@@ -83,24 +87,28 @@ interface nsINavBookmarkObserver : nsISu
* @param aItemType
* The type of the item to be removed (see TYPE_* constants below).
* @param aURI
* The URI of the added item if it was TYPE_BOOKMARK, null otherwise.
* @param aGuid
* The unique ID associated with the item.
* @param aParentGuid
* The unique ID associated with the item's parent.
+ * @param aSource
+ * A change source constant from nsINavBookmarksService::SOURCE_*,
+ * passed to the method that notifies the observer.
*/
void onItemRemoved(in long long aItemId,
in long long aParentId,
in long aIndex,
in unsigned short aItemType,
in nsIURI aURI,
in ACString aGuid,
- in ACString aParentGuid);
+ in ACString aParentGuid,
+ in unsigned short aSource);
/**
* Notifies that an item's information has changed. This will be called
* whenever any attributes like "title" are changed.
*
* @param aItemId
* The id of the item that was changed.
* @param aProperty
@@ -120,16 +128,19 @@ interface nsINavBookmarkObserver : nsISu
* The id of the folder containing the item.
* @param aGuid
* The unique ID associated with the item.
* @param aParentGuid
* The unique ID associated with the item's parent.
* @param aOldValue
* For certain properties, this is set to the new value of the
* property (see the list below).
+ * @param aSource
+ * A change source constant from nsINavBookmarksService::SOURCE_*,
+ * passed to the method that notifies the observer.
*
* @note List of values that may be associated with properties:
* aProperty | aNewValue
* =====================================================================
* cleartime | Empty string (all visits to this item were removed).
* title | The new title.
* favicon | The "moz-anno" URL of the new favicon.
* uri | new URL.
@@ -151,17 +162,18 @@ interface nsINavBookmarkObserver : nsISu
in ACString aProperty,
in boolean aIsAnnotationProperty,
in AUTF8String aNewValue,
in PRTime aLastModified,
in unsigned short aItemType,
in long long aParentId,
in ACString aGuid,
in ACString aParentGuid,
- in AUTF8String aOldValue);
+ in AUTF8String aOldValue,
+ in unsigned short aSource);
/**
* Notifies that the item was visited. Can be invoked only for TYPE_BOOKMARK
* items.
*
* @param aItemId
* The id of the bookmark that was visited.
* @param aVisitId
@@ -213,26 +225,30 @@ interface nsINavBookmarkObserver : nsISu
* @param aItemType
* The type of the item to be removed (see TYPE_* constants below).
* @param aGuid
* The unique ID associated with the item.
* @param aOldParentGuid
* The unique ID associated with the old item's parent.
* @param aNewParentGuid
* The unique ID associated with the new item's parent.
+ * @param aSource
+ * A change source constant from nsINavBookmarksService::SOURCE_*,
+ * passed to the method that notifies the observer.
*/
void onItemMoved(in long long aItemId,
in long long aOldParentId,
in long aOldIndex,
in long long aNewParentId,
in long aNewIndex,
in unsigned short aItemType,
in ACString aGuid,
in ACString aOldParentGuid,
- in ACString aNewParentGuid);
+ in ACString aNewParentGuid,
+ in unsigned short aSource);
};
/**
* The BookmarksService interface provides methods for managing bookmarked
* history items. Bookmarks consist of a set of user-customizable
* folders. A URI in history can be contained in one or more such folders.
*/
@@ -273,124 +289,163 @@ interface nsINavBookmarksService : nsISu
const unsigned short TYPE_BOOKMARK = 1;
const unsigned short TYPE_FOLDER = 2;
const unsigned short TYPE_SEPARATOR = 3;
// Dynamic containers are deprecated and unsupported.
// This const exists just to avoid reusing the value.
const unsigned short TYPE_DYNAMIC_CONTAINER = 4;
+ // Change source constants. These are used to distinguish changes made by
+ // Sync and bookmarks import from other Places consumers, though they can
+ // be extended to support other callers. Sources are passed as optional
+ // parameters to methods used by Sync, and forwarded to observers.
+ const unsigned short SOURCE_DEFAULT = 0;
+ const unsigned short SOURCE_SYNC = 1;
+ const unsigned short SOURCE_IMPORT = 2;
+ const unsigned short SOURCE_IMPORT_REPLACE = 3;
+
/**
* Inserts a child bookmark into the given folder.
*
* @param aParentId
* The id of the parent folder
* @param aURI
* The URI to insert
* @param aIndex
* The index to insert at, or DEFAULT_INDEX to append
* @param aTitle
* The title for the new bookmark
* @param [optional] aGuid
* The GUID to be set for the new item. If not set, a new GUID is
* generated. Unless you've a very sound reason, such as an undo
* manager implementation, do not pass this argument.
+ * @param [optional] aSource
+ * The change source. This is forwarded to all bookmark observers,
+ * allowing them to distinguish between insertions from different
+ * callers. Defaults to SOURCE_DEFAULT if omitted.
* @return The ID of the newly-created bookmark.
*
* @note aTitle will be truncated to TITLE_LENGTH_MAX and
* aURI will be truncated to URI_LENGTH_MAX.
* @throws if aGuid is malformed.
*/
long long insertBookmark(in long long aParentId, in nsIURI aURI,
in long aIndex, in AUTF8String aTitle,
- [optional] in ACString aGuid);
+ [optional] in ACString aGuid,
+ [optional] in unsigned short aSource);
/**
* Removes a child item. Used to delete a bookmark or separator.
* @param aItemId
* The child item to remove
+ * @param [optional] aSource
+ * The change source, forwarded to all bookmark observers. Defaults
+ * to SOURCE_DEFAULT.
*/
- void removeItem(in long long aItemId);
+ void removeItem(in long long aItemId, [optional] in unsigned short aSource);
/**
* Creates a new child folder and inserts it under the given parent.
* @param aParentFolder
* The id of the parent folder
* @param aName
* The name of the new folder
* @param aIndex
* The index to insert at, or DEFAULT_INDEX to append
* @param [optional] aGuid
* The GUID to be set for the new item. If not set, a new GUID is
* generated. Unless you've a very sound reason, such as an undo
* manager implementation, do not pass this argument.
+ * @param [optional] aSource
+ * The change source, forwarded to all bookmark observers. Defaults
+ * to SOURCE_DEFAULT.
* @return The ID of the newly-inserted folder.
* @throws if aGuid is malformed.
*/
long long createFolder(in long long aParentFolder, in AUTF8String name,
in long index,
- [optional] in ACString aGuid);
+ [optional] in ACString aGuid,
+ [optional] in unsigned short aSource);
/**
* Gets an undo-able transaction for removing a folder from the bookmarks
* tree.
* @param aItemId
* The id of the folder to remove.
+ * @param [optional] aSource
+ * The change source, forwarded to all bookmark observers. Defaults
+ * to SOURCE_DEFAULT.
* @return An object implementing nsITransaction that can be used to undo
* or redo the action.
*
* This method exists because complex delete->undo operations rely on
* recreated folders to have the same ID they had before they were deleted,
* so that any other items deleted in different transactions can be
* re-inserted correctly. This provides a safe encapsulation of this
* functionality without exposing the ability to recreate folders with
* specific IDs (potentially dangerous if abused by other code!) in the
* public API.
*/
- nsITransaction getRemoveFolderTransaction(in long long aItemId);
+ nsITransaction getRemoveFolderTransaction(in long long aItemId,
+ [optional] in unsigned short aSource);
/**
* Convenience function for container services. Removes
* all children of the given folder.
* @param aItemId
* The id of the folder to remove children from.
+ * @param [optional] aSource
+ * The change source, forwarded to all bookmark observers. Defaults
+ * to SOURCE_DEFAULT.
*/
- void removeFolderChildren(in long long aItemId);
+ void removeFolderChildren(in long long aItemId,
+ [optional] in unsigned short aSource);
/**
* Moves an item to a different container, preserving its contents.
* @param aItemId
* The id of the item to move
* @param aNewParentId
* The id of the new parent
* @param aIndex
* The index under aNewParent, or DEFAULT_INDEX to append
+ * @param [optional] aSource
+ * The change source, forwarded to all bookmark observers. Defaults
+ * to SOURCE_DEFAULT.
*
* NOTE: When moving down in the same container we take into account the
* removal of the original item. If you want to move from index X to
* index Y > X you must use moveItem(id, folder, Y + 1)
*/
- void moveItem(in long long aItemId, in long long aNewParentId, in long aIndex);
+ void moveItem(in long long aItemId,
+ in long long aNewParentId,
+ in long aIndex,
+ [optional] in unsigned short aSource);
/**
* Inserts a bookmark separator into the given folder at the given index.
* The separator can be removed using removeChildAt().
* @param aParentId
* The id of the parent folder
* @param aIndex
* The separator's index under folder, or DEFAULT_INDEX to append
* @param [optional] aGuid
* The GUID to be set for the new item. If not set, a new GUID is
* generated. Unless you've a very sound reason, such as an undo
* manager implementation, do not pass this argument.
+ * @param [optional] aSource
+ * The change source, forwarded to all bookmark observers. Defaults
+ * to SOURCE_DEFAULT.
* @return The ID of the new separator.
* @throws if aGuid is malformed.
*/
long long insertSeparator(in long long aParentId, in long aIndex,
- [optional] in ACString aGuid);
+ [optional] in ACString aGuid,
+ [optional] in unsigned short aSource);
/**
* Get the itemId given the containing folder and the index.
* @param aParentId
* The id of the diret parent folder of the item
* @param aIndex
* The index of the item within the parent folder.
* Pass DEFAULT_INDEX for the last item.
@@ -399,20 +454,24 @@ interface nsINavBookmarksService : nsISu
long long getIdForItemAt(in long long aParentId, in long aIndex);
/**
* Set the title for an item.
* @param aItemId
* The id of the item whose title should be updated.
* @param aTitle
* The new title for the bookmark.
+ * @param [optional] aSource
+ * The change source, forwarded to all bookmark observers. Defaults
+ * to SOURCE_DEFAULT.
*
* @note aTitle will be truncated to TITLE_LENGTH_MAX.
*/
- void setItemTitle(in long long aItemId, in AUTF8String aTitle);
+ void setItemTitle(in long long aItemId, in AUTF8String aTitle,
+ [optional] in unsigned short aSource);
/**
* Get the title for an item.
*
* If no item title is available it will return a void string (null in JS).
*
* @param aItemId
* The id of the item whose title should be retrieved
@@ -423,18 +482,23 @@ interface nsINavBookmarksService : nsISu
/**
* Set the date added time for an item.
*
* @param aItemId
* the id of the item whose date added time should be updated.
* @param aDateAdded
* the new date added value in microseconds. Note that it is rounded
* down to milliseconds precision.
+ * @param [optional] aSource
+ * The change source, forwarded to all bookmark observers. Defaults
+ * to SOURCE_DEFAULT.
*/
- void setItemDateAdded(in long long aItemId, in PRTime aDateAdded);
+ void setItemDateAdded(in long long aItemId,
+ in PRTime aDateAdded,
+ [optional] in unsigned short aSource);
/**
* Get the date added time for an item.
*
* @param aItemId
* the id of the item whose date added time should be retrieved.
*
* @return the date added value in microseconds.
@@ -444,23 +508,28 @@ interface nsINavBookmarksService : nsISu
/**
* Set the last modified time for an item.
*
* @param aItemId
* the id of the item whose last modified time should be updated.
* @param aLastModified
* the new last modified value in microseconds. Note that it is
* rounded down to milliseconds precision.
+ * @param [optional] aSource
+ * The change source, forwarded to all bookmark observers. Defaults
+ * to SOURCE_DEFAULT.
*
* @note This is the only method that will send an itemChanged notification
* for the property. lastModified will still be updated in
* any other method that changes an item property, but we will send
* the corresponding itemChanged notification instead.
*/
- void setItemLastModified(in long long aItemId, in PRTime aLastModified);
+ void setItemLastModified(in long long aItemId,
+ in PRTime aLastModified,
+ [optional] in unsigned short aSource);
/**
* Get the last modified time for an item.
*
* @param aItemId
* the id of the item whose last modified time should be retrieved.
*
* @return the date added value in microseconds.
@@ -487,20 +556,24 @@ interface nsINavBookmarksService : nsISu
* the indices will become corrupted.
*
* WARNING: This is API is intended for scenarios such as folder sorting,
* where the caller manages the indices of *all* items in the folder.
* You must always ensure each index is unique after a reordering.
*
* @param aItemId The id of the item to modify
* @param aNewIndex The new index
+ * @param aSource The optional change source, forwarded to all bookmark
+ * observers. Defaults to SOURCE_DEFAULT.
*
* @throws If aNewIndex is out of bounds.
*/
- void setItemIndex(in long long aItemId, in long aNewIndex);
+ void setItemIndex(in long long aItemId,
+ in long aNewIndex,
+ [optional] in unsigned short aSource);
/**
* Get an item's type (bookmark, separator, folder).
* The type is one of the TYPE_* constants defined above.
*/
unsigned short getItemType(in long long aItemId);
/**
@@ -524,17 +597,19 @@ interface nsINavBookmarksService : nsISu
*/
nsIURI getBookmarkedURIFor(in nsIURI aURI);
/**
* Change the bookmarked URI for a bookmark.
* This changes which "place" the bookmark points at,
* which means all annotations, etc are carried along.
*/
- void changeBookmarkURI(in long long aItemId, in nsIURI aNewURI);
+ void changeBookmarkURI(in long long aItemId,
+ in nsIURI aNewURI,
+ [optional] in unsigned short aSource);
/**
* Get the parent folder's id for an item.
*/
long long getFolderIdForItem(in long long aItemId);
/**
* Returns the list of bookmark ids that contain the given URI.
@@ -545,17 +620,19 @@ interface nsINavBookmarksService : nsISu
/**
* Associates the given keyword with the given bookmark.
*
* Use an empty keyword to clear the keyword associated with the URI.
* In both of these cases, succeeds but does nothing if the URL/keyword is not found.
*
* @deprecated Use PlacesUtils.keywords.insert() API instead.
*/
- void setKeywordForBookmark(in long long aItemId, in AString aKeyword);
+ void setKeywordForBookmark(in long long aItemId,
+ in AString aKeyword,
+ [optional] in unsigned short aSource);
/**
* Retrieves the keyword for the given bookmark. Will be void string
* (null in JS) if no such keyword is found.
*
* @deprecated Use PlacesUtils.keywords.fetch() API instead.
*/
AString getKeywordForBookmark(in long long aItemId);
--- a/toolkit/components/places/nsITaggingService.idl
+++ b/toolkit/components/places/nsITaggingService.idl
@@ -24,31 +24,41 @@ interface nsITaggingService : nsISupport
* ignored.
*
* @param aURI
* the URL to tag.
* @param aTags
* Array of tags to set for the given URL. Each element within the
* array can be either a tag name (non-empty string) or a concrete
* itemId of a tag container.
+ * @param [optional] aSource
+ * A change source constant from nsINavBookmarksService::SOURCE_*.
+ * Defaults to SOURCE_DEFAULT if omitted.
*/
- void tagURI(in nsIURI aURI, in nsIVariant aTags);
+ void tagURI(in nsIURI aURI,
+ in nsIVariant aTags,
+ [optional] in unsigned short aSource);
/**
* Removes tags from a URL. Tags from aTags which are not set for the
* given URL are ignored.
*
* @param aURI
* the URL to un-tag.
* @param aTags
* Array of tags to unset. Pass null to remove all tags from the given
* url. Each element within the array can be either a tag name
* (non-empty string) or a concrete itemId of a tag container.
+ * @param [optional] aSource
+ * A change source constant from nsINavBookmarksService::SOURCE_*.
+ * Defaults to SOURCE_DEFAULT if omitted.
*/
- void untagURI(in nsIURI aURI, in nsIVariant aTags);
+ void untagURI(in nsIURI aURI,
+ in nsIVariant aTags,
+ [optional] in unsigned short aSource);
/**
* Retrieves all URLs tagged with the given tag.
*
* @param aTag
* tag name
* @returns Array of uris tagged with aTag.
*/
--- a/toolkit/components/places/nsLivemarkService.js
+++ b/toolkit/components/places/nsLivemarkService.js
@@ -203,17 +203,18 @@ LivemarkService.prototype = {
// Create a new livemark.
let folder = yield PlacesUtils.bookmarks.insert({
type: PlacesUtils.bookmarks.TYPE_FOLDER,
parentGuid: aLivemarkInfo.parentGuid,
title: aLivemarkInfo.title,
index: aLivemarkInfo.index,
guid: aLivemarkInfo.guid,
- dateAdded: toDate(aLivemarkInfo.dateAdded) || toDate(aLivemarkInfo.lastModified)
+ dateAdded: toDate(aLivemarkInfo.dateAdded) || toDate(aLivemarkInfo.lastModified),
+ source: aLivemarkInfo.source,
});
// Set feed and site URI annotations.
let id = yield PlacesUtils.promiseItemId(folder.guid);
// Create the internal Livemark object.
let livemark = new Livemark({ id
, title: folder.title
--- a/toolkit/components/places/nsNavBookmarks.cpp
+++ b/toolkit/components/places/nsNavBookmarks.cpp
@@ -326,16 +326,17 @@ nsNavBookmarks::InsertBookmarkInDB(int64
int64_t aParentId,
int32_t aIndex,
const nsACString& aTitle,
PRTime aDateAdded,
PRTime aLastModified,
const nsACString& aParentGuid,
int64_t aGrandParentId,
nsIURI* aURI,
+ uint16_t aSource,
int64_t* _itemId,
nsACString& _guid)
{
// Check for a valid itemId.
MOZ_ASSERT(_itemId && (*_itemId == -1 || *_itemId > 0));
// Check for a valid placeId.
MOZ_ASSERT(aPlaceId && (aPlaceId == -1 || aPlaceId > 0));
@@ -463,16 +464,17 @@ nsNavBookmarks::InsertBookmarkInDB(int64
NS_IMETHODIMP
nsNavBookmarks::InsertBookmark(int64_t aFolder,
nsIURI* aURI,
int32_t aIndex,
const nsACString& aTitle,
const nsACString& aGUID,
+ uint16_t aSource,
int64_t* aNewBookmarkId)
{
NS_ENSURE_ARG(aURI);
NS_ENSURE_ARG_POINTER(aNewBookmarkId);
NS_ENSURE_ARG_MIN(aIndex, nsINavBookmarksService::DEFAULT_INDEX);
if (!aGUID.IsEmpty() && !IsValidGUID(aGUID))
return NS_ERROR_INVALID_ARG;
@@ -505,33 +507,33 @@ nsNavBookmarks::InsertBookmark(int64_t a
*aNewBookmarkId = -1;
PRTime dateAdded = RoundedPRNow();
nsAutoCString guid(aGUID);
nsCString title;
TruncateTitle(aTitle, title);
rv = InsertBookmarkInDB(placeId, BOOKMARK, aFolder, index, title, dateAdded,
- 0, folderGuid, grandParentId, aURI,
+ 0, folderGuid, grandParentId, aURI, aSource,
aNewBookmarkId, guid);
NS_ENSURE_SUCCESS(rv, rv);
// If not a tag, recalculate frecency for this entry, since it changed.
if (grandParentId != mTagsRoot) {
rv = history->UpdateFrecency(placeId);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
nsINavBookmarkObserver,
OnItemAdded(*aNewBookmarkId, aFolder, index, TYPE_BOOKMARK,
- aURI, title, dateAdded, guid, folderGuid));
+ aURI, title, dateAdded, guid, folderGuid, aSource));
// If the bookmark has been added to a tag container, notify all
// bookmark-folder result nodes which contain a bookmark for the new
// bookmark's url.
if (grandParentId == mTagsRoot) {
// Notify a tags change to all bookmarks for this URI.
nsTArray<BookmarkData> bookmarks;
rv = GetBookmarksForURI(aURI, bookmarks);
@@ -547,26 +549,27 @@ nsNavBookmarks::InsertBookmark(int64_t a
NS_LITERAL_CSTRING("tags"),
false,
EmptyCString(),
bookmarks[i].lastModified,
TYPE_BOOKMARK,
bookmarks[i].parentId,
bookmarks[i].guid,
bookmarks[i].parentGuid,
- EmptyCString()));
+ EmptyCString(),
+ aSource));
}
}
return NS_OK;
}
NS_IMETHODIMP
-nsNavBookmarks::RemoveItem(int64_t aItemId)
+nsNavBookmarks::RemoveItem(int64_t aItemId, uint16_t aSource)
{
PROFILER_LABEL("nsNavBookmarks", "RemoveItem",
js::ProfileEntry::Category::OTHER);
NS_ENSURE_ARG(!IsRoot(aItemId));
BookmarkData bookmark;
nsresult rv = FetchItemInfo(aItemId, bookmark);
@@ -574,23 +577,23 @@ nsNavBookmarks::RemoveItem(int64_t aItem
mozStorageTransaction transaction(mDB->MainConn(), false);
// First, if not a tag, remove item annotations.
if (bookmark.parentId != mTagsRoot &&
bookmark.grandParentId != mTagsRoot) {
nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
- rv = annosvc->RemoveItemAnnotations(bookmark.id);
+ rv = annosvc->RemoveItemAnnotations(bookmark.id, aSource);
NS_ENSURE_SUCCESS(rv, rv);
}
if (bookmark.type == TYPE_FOLDER) {
// Remove all of the folder's children.
- rv = RemoveFolderChildren(bookmark.id);
+ rv = RemoveFolderChildren(bookmark.id, aSource);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
"DELETE FROM moz_bookmarks WHERE id = :item_id"
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
@@ -633,17 +636,18 @@ nsNavBookmarks::RemoveItem(int64_t aItem
NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
nsINavBookmarkObserver,
OnItemRemoved(bookmark.id,
bookmark.parentId,
bookmark.position,
bookmark.type,
uri,
bookmark.guid,
- bookmark.parentGuid));
+ bookmark.parentGuid,
+ aSource));
if (bookmark.type == TYPE_BOOKMARK && bookmark.grandParentId == mTagsRoot &&
uri) {
// If the removed bookmark was child of a tag container, notify a tags
// change to all bookmarks for this URI.
nsTArray<BookmarkData> bookmarks;
rv = GetBookmarksForURI(uri, bookmarks);
NS_ENSURE_SUCCESS(rv, rv);
@@ -655,43 +659,44 @@ nsNavBookmarks::RemoveItem(int64_t aItem
NS_LITERAL_CSTRING("tags"),
false,
EmptyCString(),
bookmarks[i].lastModified,
TYPE_BOOKMARK,
bookmarks[i].parentId,
bookmarks[i].guid,
bookmarks[i].parentGuid,
- EmptyCString()));
+ EmptyCString(),
+ aSource));
}
}
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::CreateFolder(int64_t aParent, const nsACString& aName,
int32_t aIndex, const nsACString& aGUID,
- int64_t* aNewFolder)
+ uint16_t aSource, int64_t* aNewFolder)
{
// NOTE: aParent can be null for root creation, so not checked
NS_ENSURE_ARG_POINTER(aNewFolder);
if (!aGUID.IsEmpty() && !IsValidGUID(aGUID))
return NS_ERROR_INVALID_ARG;
// CreateContainerWithID returns the index of the new folder, but that's not
// used here. To avoid any risk of corrupting data should this function
// be changed, we'll use a local variable to hold it. The true argument
// will cause notifications to be sent to bookmark observers.
int32_t localIndex = aIndex;
nsresult rv = CreateContainerWithID(-1, aParent, aName, true, &localIndex,
- aGUID, aNewFolder);
+ aGUID, aSource, aNewFolder);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
bool nsNavBookmarks::IsLivemark(int64_t aFolderId)
{
nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
NS_ENSURE_TRUE(annosvc, false);
@@ -705,16 +710,17 @@ bool nsNavBookmarks::IsLivemark(int64_t
nsresult
nsNavBookmarks::CreateContainerWithID(int64_t aItemId,
int64_t aParent,
const nsACString& aTitle,
bool aIsBookmarkFolder,
int32_t* aIndex,
const nsACString& aGUID,
+ uint16_t aSource,
int64_t* aNewFolder)
{
NS_ENSURE_ARG_MIN(*aIndex, nsINavBookmarksService::DEFAULT_INDEX);
// Get the correct index for insertion. This also ensures the parent exists.
int32_t index, folderCount;
int64_t grandParentId;
nsAutoCString folderGuid;
@@ -736,36 +742,38 @@ nsNavBookmarks::CreateContainerWithID(in
*aNewFolder = aItemId;
PRTime dateAdded = RoundedPRNow();
nsAutoCString guid(aGUID);
nsCString title;
TruncateTitle(aTitle, title);
rv = InsertBookmarkInDB(-1, FOLDER, aParent, index,
title, dateAdded, 0, folderGuid, grandParentId,
- nullptr, aNewFolder, guid);
+ nullptr, aSource, aNewFolder, guid);
NS_ENSURE_SUCCESS(rv, rv);
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
nsINavBookmarkObserver,
OnItemAdded(*aNewFolder, aParent, index, FOLDER,
- nullptr, title, dateAdded, guid, folderGuid));
+ nullptr, title, dateAdded, guid, folderGuid,
+ aSource));
*aIndex = index;
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::InsertSeparator(int64_t aParent,
int32_t aIndex,
const nsACString& aGUID,
+ uint16_t aSource,
int64_t* aNewItemId)
{
NS_ENSURE_ARG_MIN(aParent, 1);
NS_ENSURE_ARG_MIN(aIndex, nsINavBookmarksService::DEFAULT_INDEX);
NS_ENSURE_ARG_POINTER(aNewItemId);
if (!aGUID.IsEmpty() && !IsValidGUID(aGUID))
return NS_ERROR_INVALID_ARG;
@@ -792,27 +800,28 @@ nsNavBookmarks::InsertSeparator(int64_t
*aNewItemId = -1;
// Set a NULL title rather than an empty string.
nsCString voidString;
voidString.SetIsVoid(true);
nsAutoCString guid(aGUID);
PRTime dateAdded = RoundedPRNow();
rv = InsertBookmarkInDB(-1, SEPARATOR, aParent, index, voidString, dateAdded,
- 0, folderGuid, grandParentId, nullptr,
+ 0, folderGuid, grandParentId, nullptr, aSource,
aNewItemId, guid);
NS_ENSURE_SUCCESS(rv, rv);
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
nsINavBookmarkObserver,
OnItemAdded(*aNewItemId, aParent, index, TYPE_SEPARATOR,
- nullptr, voidString, dateAdded, guid, folderGuid));
+ nullptr, voidString, dateAdded, guid, folderGuid,
+ aSource));
return NS_OK;
}
nsresult
nsNavBookmarks::GetLastChildId(int64_t aFolderId, int64_t* aItemId)
{
@@ -879,26 +888,27 @@ nsNavBookmarks::GetIdForItemAt(int64_t a
}
}
return NS_OK;
}
NS_IMPL_ISUPPORTS(nsNavBookmarks::RemoveFolderTransaction, nsITransaction)
NS_IMETHODIMP
-nsNavBookmarks::GetRemoveFolderTransaction(int64_t aFolderId, nsITransaction** aResult)
+nsNavBookmarks::GetRemoveFolderTransaction(int64_t aFolderId, uint16_t aSource,
+ nsITransaction** aResult)
{
NS_ENSURE_ARG_MIN(aFolderId, 1);
NS_ENSURE_ARG_POINTER(aResult);
// Create and initialize a RemoveFolderTransaction object that can be used to
// recreate the folder safely later.
RemoveFolderTransaction* rft =
- new RemoveFolderTransaction(aFolderId);
+ new RemoveFolderTransaction(aFolderId, aSource);
if (!rft)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aResult = rft);
return NS_OK;
}
@@ -1021,17 +1031,17 @@ nsNavBookmarks::GetDescendantChildren(in
}
}
return NS_OK;
}
NS_IMETHODIMP
-nsNavBookmarks::RemoveFolderChildren(int64_t aFolderId)
+nsNavBookmarks::RemoveFolderChildren(int64_t aFolderId, uint16_t aSource)
{
PROFILER_LABEL("nsNavBookmarks", "RemoveFolderChilder",
js::ProfileEntry::Category::OTHER);
NS_ENSURE_ARG_MIN(aFolderId, 1);
NS_ENSURE_ARG(aFolderId != mRoot);
BookmarkData folder;
@@ -1111,17 +1121,18 @@ nsNavBookmarks::RemoveFolderChildren(int
NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
nsINavBookmarkObserver,
OnItemRemoved(child.id,
child.parentId,
child.position,
child.type,
uri,
child.guid,
- child.parentGuid));
+ child.parentGuid,
+ aSource));
if (child.type == TYPE_BOOKMARK && child.grandParentId == mTagsRoot &&
uri) {
// If the removed bookmark was a child of a tag container, notify all
// bookmark-folder result nodes which contain a bookmark for the removed
// bookmark's url.
nsTArray<BookmarkData> bookmarks;
rv = GetBookmarksForURI(uri, bookmarks);
@@ -1134,27 +1145,31 @@ nsNavBookmarks::RemoveFolderChildren(int
NS_LITERAL_CSTRING("tags"),
false,
EmptyCString(),
bookmarks[i].lastModified,
TYPE_BOOKMARK,
bookmarks[i].parentId,
bookmarks[i].guid,
bookmarks[i].parentGuid,
- EmptyCString()));
+ EmptyCString(),
+ aSource));
}
}
}
return NS_OK;
}
NS_IMETHODIMP
-nsNavBookmarks::MoveItem(int64_t aItemId, int64_t aNewParent, int32_t aIndex)
+nsNavBookmarks::MoveItem(int64_t aItemId,
+ int64_t aNewParent,
+ int32_t aIndex,
+ uint16_t aSource)
{
NS_ENSURE_ARG(!IsRoot(aItemId));
NS_ENSURE_ARG_MIN(aItemId, 1);
NS_ENSURE_ARG_MIN(aNewParent, 1);
// -1 is append, but no other negative number is allowed.
NS_ENSURE_ARG_MIN(aIndex, -1);
// Disallow making an item its own parent.
NS_ENSURE_ARG(aItemId != aNewParent);
@@ -1276,17 +1291,18 @@ nsNavBookmarks::MoveItem(int64_t aItemId
OnItemMoved(bookmark.id,
bookmark.parentId,
bookmark.position,
aNewParent,
newIndex,
bookmark.type,
bookmark.guid,
bookmark.parentGuid,
- newParentGuid));
+ newParentGuid,
+ aSource));
return NS_OK;
}
nsresult
nsNavBookmarks::FetchItemInfo(int64_t aItemId,
BookmarkData& _bookmark)
{
// LEFT JOIN since not all bookmarks have an associated place.
@@ -1390,17 +1406,18 @@ nsNavBookmarks::SetItemDateInternal(enum
// note, we are not notifying the observers
// that the item has changed.
return NS_OK;
}
NS_IMETHODIMP
-nsNavBookmarks::SetItemDateAdded(int64_t aItemId, PRTime aDateAdded)
+nsNavBookmarks::SetItemDateAdded(int64_t aItemId, PRTime aDateAdded,
+ uint16_t aSource)
{
NS_ENSURE_ARG_MIN(aItemId, 1);
BookmarkData bookmark;
nsresult rv = FetchItemInfo(aItemId, bookmark);
NS_ENSURE_SUCCESS(rv, rv);
// Round here so that we notify with the right value.
@@ -1416,17 +1433,18 @@ nsNavBookmarks::SetItemDateAdded(int64_t
NS_LITERAL_CSTRING("dateAdded"),
false,
nsPrintfCString("%lld", bookmark.dateAdded),
bookmark.dateAdded,
bookmark.type,
bookmark.parentId,
bookmark.guid,
bookmark.parentGuid,
- EmptyCString()));
+ EmptyCString(),
+ aSource));
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::GetItemDateAdded(int64_t aItemId, PRTime* _dateAdded)
{
NS_ENSURE_ARG_MIN(aItemId, 1);
@@ -1437,17 +1455,18 @@ nsNavBookmarks::GetItemDateAdded(int64_t
NS_ENSURE_SUCCESS(rv, rv);
*_dateAdded = bookmark.dateAdded;
return NS_OK;
}
NS_IMETHODIMP
-nsNavBookmarks::SetItemLastModified(int64_t aItemId, PRTime aLastModified)
+nsNavBookmarks::SetItemLastModified(int64_t aItemId, PRTime aLastModified,
+ uint16_t aSource)
{
NS_ENSURE_ARG_MIN(aItemId, 1);
BookmarkData bookmark;
nsresult rv = FetchItemInfo(aItemId, bookmark);
NS_ENSURE_SUCCESS(rv, rv);
// Round here so that we notify with the right value.
@@ -1463,17 +1482,18 @@ nsNavBookmarks::SetItemLastModified(int6
NS_LITERAL_CSTRING("lastModified"),
false,
nsPrintfCString("%lld", bookmark.lastModified),
bookmark.lastModified,
bookmark.type,
bookmark.parentId,
bookmark.guid,
bookmark.parentGuid,
- EmptyCString()));
+ EmptyCString(),
+ aSource));
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::GetItemLastModified(int64_t aItemId, PRTime* _lastModified)
{
NS_ENSURE_ARG_MIN(aItemId, 1);
@@ -1484,17 +1504,18 @@ nsNavBookmarks::GetItemLastModified(int6
NS_ENSURE_SUCCESS(rv, rv);
*_lastModified = bookmark.lastModified;
return NS_OK;
}
NS_IMETHODIMP
-nsNavBookmarks::SetItemTitle(int64_t aItemId, const nsACString& aTitle)
+nsNavBookmarks::SetItemTitle(int64_t aItemId, const nsACString& aTitle,
+ uint16_t aSource)
{
NS_ENSURE_ARG_MIN(aItemId, 1);
BookmarkData bookmark;
nsresult rv = FetchItemInfo(aItemId, bookmark);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
@@ -1532,17 +1553,18 @@ nsNavBookmarks::SetItemTitle(int64_t aIt
NS_LITERAL_CSTRING("title"),
false,
title,
bookmark.lastModified,
bookmark.type,
bookmark.parentId,
bookmark.guid,
bookmark.parentGuid,
- EmptyCString()));
+ EmptyCString(),
+ aSource));
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::GetItemTitle(int64_t aItemId,
nsACString& _title)
{
@@ -1951,17 +1973,18 @@ nsNavBookmarks::GetBookmarkedURIFor(nsIU
}
// If there is no bookmarked origin, we will just return null.
return NS_OK;
}
NS_IMETHODIMP
-nsNavBookmarks::ChangeBookmarkURI(int64_t aBookmarkId, nsIURI* aNewURI)
+nsNavBookmarks::ChangeBookmarkURI(int64_t aBookmarkId, nsIURI* aNewURI,
+ uint16_t aSource)
{
NS_ENSURE_ARG_MIN(aBookmarkId, 1);
NS_ENSURE_ARG(aNewURI);
BookmarkData bookmark;
nsresult rv = FetchItemInfo(aBookmarkId, bookmark);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_ARG(bookmark.type == TYPE_BOOKMARK);
@@ -2016,17 +2039,18 @@ nsNavBookmarks::ChangeBookmarkURI(int64_
NS_LITERAL_CSTRING("uri"),
false,
spec,
bookmark.lastModified,
bookmark.type,
bookmark.parentId,
bookmark.guid,
bookmark.parentGuid,
- bookmark.url));
+ bookmark.url,
+ aSource));
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::GetFolderIdForItem(int64_t aItemId, int64_t* _parentId)
{
NS_ENSURE_ARG_MIN(aItemId, 1);
@@ -2188,17 +2212,19 @@ nsNavBookmarks::GetItemIndex(int64_t aIt
return NS_OK;
}
*_index = bookmark.position;
return NS_OK;
}
NS_IMETHODIMP
-nsNavBookmarks::SetItemIndex(int64_t aItemId, int32_t aNewIndex)
+nsNavBookmarks::SetItemIndex(int64_t aItemId,
+ int32_t aNewIndex,
+ uint16_t aSource)
{
NS_ENSURE_ARG_MIN(aItemId, 1);
NS_ENSURE_ARG_MIN(aNewIndex, 0);
BookmarkData bookmark;
nsresult rv = FetchItemInfo(aItemId, bookmark);
NS_ENSURE_SUCCESS(rv, rv);
@@ -2231,25 +2257,27 @@ nsNavBookmarks::SetItemIndex(int64_t aIt
OnItemMoved(bookmark.id,
bookmark.parentId,
bookmark.position,
bookmark.parentId,
aNewIndex,
bookmark.type,
bookmark.guid,
bookmark.parentGuid,
- bookmark.parentGuid));
+ bookmark.parentGuid,
+ aSource));
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::SetKeywordForBookmark(int64_t aBookmarkId,
- const nsAString& aUserCasedKeyword)
+ const nsAString& aUserCasedKeyword,
+ uint16_t aSource)
{
NS_ENSURE_ARG_MIN(aBookmarkId, 1);
// This also ensures the bookmark is valid.
BookmarkData bookmark;
nsresult rv = FetchItemInfo(aBookmarkId, bookmark);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> uri;
@@ -2311,17 +2339,18 @@ nsNavBookmarks::SetKeywordForBookmark(in
NS_LITERAL_CSTRING("keyword"),
false,
EmptyCString(),
bookmarks[i].lastModified,
TYPE_BOOKMARK,
bookmarks[i].parentId,
bookmarks[i].guid,
bookmarks[i].parentGuid,
- EmptyCString()));
+ EmptyCString(),
+ aSource));
}
return NS_OK;
}
// A keyword can only be associated to a single URI. Check if the requested
// keyword was already associated, in such a case we will need to notify about
// the change.
@@ -2364,17 +2393,18 @@ nsNavBookmarks::SetKeywordForBookmark(in
NS_LITERAL_CSTRING("keyword"),
false,
EmptyCString(),
bookmarks[i].lastModified,
TYPE_BOOKMARK,
bookmarks[i].parentId,
bookmarks[i].guid,
bookmarks[i].parentGuid,
- EmptyCString()));
+ EmptyCString(),
+ aSource));
}
stmt = mDB->GetStatement(
"UPDATE moz_keywords SET place_id = :place_id WHERE keyword = :keyword"
);
NS_ENSURE_STATE(stmt);
}
else {
@@ -2404,17 +2434,18 @@ nsNavBookmarks::SetKeywordForBookmark(in
NS_LITERAL_CSTRING("keyword"),
false,
NS_ConvertUTF16toUTF8(keyword),
bookmarks[i].lastModified,
TYPE_BOOKMARK,
bookmarks[i].parentId,
bookmarks[i].guid,
bookmarks[i].parentGuid,
- EmptyCString()));
+ EmptyCString(),
+ aSource));
}
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::GetKeywordForBookmark(int64_t aBookmarkId, nsAString& aKeyword)
@@ -2604,17 +2635,22 @@ nsNavBookmarks::NotifyItemChanged(const
aData.property,
aData.isAnnotation,
aData.newValue,
aData.bookmark.lastModified,
aData.bookmark.type,
aData.bookmark.parentId,
aData.bookmark.guid,
aData.bookmark.parentGuid,
- aData.oldValue));
+ aData.oldValue,
+ // We specify the default source here because
+ // this method is only called for history
+ // visits, and we don't track sources in
+ // history.
+ SOURCE_DEFAULT));
}
////////////////////////////////////////////////////////////////////////////////
//// nsIObserver
NS_IMETHODIMP
nsNavBookmarks::Observe(nsISupports *aSubject, const char *aTopic,
const char16_t *aData)
@@ -2824,17 +2860,18 @@ nsNavBookmarks::OnDeleteVisits(nsIURI* a
NS_IMETHODIMP
nsNavBookmarks::OnPageAnnotationSet(nsIURI* aPage, const nsACString& aName)
{
return NS_OK;
}
NS_IMETHODIMP
-nsNavBookmarks::OnItemAnnotationSet(int64_t aItemId, const nsACString& aName)
+nsNavBookmarks::OnItemAnnotationSet(int64_t aItemId, const nsACString& aName,
+ uint16_t aSource)
{
BookmarkData bookmark;
nsresult rv = FetchItemInfo(aItemId, bookmark);
NS_ENSURE_SUCCESS(rv, rv);
bookmark.lastModified = RoundedPRNow();
rv = SetItemDateInternal(LAST_MODIFIED, bookmark.id, bookmark.lastModified);
NS_ENSURE_SUCCESS(rv, rv);
@@ -2845,29 +2882,31 @@ nsNavBookmarks::OnItemAnnotationSet(int6
aName,
true,
EmptyCString(),
bookmark.lastModified,
bookmark.type,
bookmark.parentId,
bookmark.guid,
bookmark.parentGuid,
- EmptyCString()));
+ EmptyCString(),
+ aSource));
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::OnPageAnnotationRemoved(nsIURI* aPage, const nsACString& aName)
{
return NS_OK;
}
NS_IMETHODIMP
-nsNavBookmarks::OnItemAnnotationRemoved(int64_t aItemId, const nsACString& aName)
+nsNavBookmarks::OnItemAnnotationRemoved(int64_t aItemId, const nsACString& aName,
+ uint16_t aSource)
{
// As of now this is doing the same as OnItemAnnotationSet, so just forward
// the call.
- nsresult rv = OnItemAnnotationSet(aItemId, aName);
+ nsresult rv = OnItemAnnotationSet(aItemId, aName, aSource);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
--- a/toolkit/components/places/nsNavBookmarks.h
+++ b/toolkit/components/places/nsNavBookmarks.h
@@ -161,16 +161,17 @@ public:
* @note If aFolder is -1, uses the autoincrement id for folder index.
* @note aTitle will be truncated to TITLE_LENGTH_MAX
*/
nsresult CreateContainerWithID(int64_t aId, int64_t aParent,
const nsACString& aTitle,
bool aIsBookmarkFolder,
int32_t* aIndex,
const nsACString& aGUID,
+ uint16_t aSource,
int64_t* aNewFolder);
/**
* Fetches information about the specified id from the database.
*
* @param aItemId
* Id of the item to fetch information for.
* @param aBookmark
@@ -332,16 +333,17 @@ private:
int64_t aParentId,
int32_t aIndex,
const nsACString& aTitle,
PRTime aDateAdded,
PRTime aLastModified,
const nsACString& aParentGuid,
int64_t aGrandParentId,
nsIURI* aURI,
+ uint16_t aSource,
int64_t* _itemId,
nsACString& _guid);
/**
* TArray version of getBookmarksIdForURI for ease of use in C++ code.
* Pass in a reference to a TArray; it will get filled with the
* resulting list of bookmark IDs.
*
@@ -358,41 +360,45 @@ private:
nsresult GetBookmarksForURI(nsIURI* aURI,
nsTArray<BookmarkData>& _bookmarks);
int64_t RecursiveFindRedirectedBookmark(int64_t aPlaceId);
class RemoveFolderTransaction final : public nsITransaction {
public:
- explicit RemoveFolderTransaction(int64_t aID) : mID(aID) {}
+ RemoveFolderTransaction(int64_t aID, uint16_t aSource)
+ : mID(aID)
+ , mSource(aSource)
+ {}
NS_DECL_ISUPPORTS
NS_IMETHOD DoTransaction() override {
nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
BookmarkData folder;
nsresult rv = bookmarks->FetchItemInfo(mID, folder);
// TODO (Bug 656935): store the BookmarkData struct instead.
mParent = folder.parentId;
mIndex = folder.position;
rv = bookmarks->GetItemTitle(mID, mTitle);
NS_ENSURE_SUCCESS(rv, rv);
- return bookmarks->RemoveItem(mID);
+ return bookmarks->RemoveItem(mID, mSource);
}
NS_IMETHOD UndoTransaction() override {
nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
int64_t newFolder;
return bookmarks->CreateContainerWithID(mID, mParent, mTitle, true,
- &mIndex, EmptyCString(), &newFolder);
+ &mIndex, EmptyCString(),
+ mSource, &newFolder);
}
NS_IMETHOD RedoTransaction() override {
return DoTransaction();
}
NS_IMETHOD GetIsTransient(bool* aResult) override {
*aResult = false;
@@ -403,16 +409,17 @@ private:
*aResult = false;
return NS_OK;
}
private:
~RemoveFolderTransaction() {}
int64_t mID;
+ uint16_t mSource;
int64_t mParent;
nsCString mTitle;
int32_t mIndex;
};
// Used to enable and disable the observer notifications.
bool mCanNotify;
nsCategoryCache<nsINavBookmarkObserver> mCacheObservers;
--- a/toolkit/components/places/nsNavHistoryResult.cpp
+++ b/toolkit/components/places/nsNavHistoryResult.cpp
@@ -2841,17 +2841,18 @@ NS_IMETHODIMP
nsNavHistoryQueryResultNode::OnItemAdded(int64_t aItemId,
int64_t aParentId,
int32_t aIndex,
uint16_t aItemType,
nsIURI* aURI,
const nsACString& aTitle,
PRTime aDateAdded,
const nsACString& aGUID,
- const nsACString& aParentGUID)
+ const nsACString& aParentGUID,
+ uint16_t aSource)
{
if (aItemType == nsINavBookmarksService::TYPE_BOOKMARK &&
mLiveUpdate != QUERYUPDATE_SIMPLE && mLiveUpdate != QUERYUPDATE_TIME) {
nsresult rv = Refresh();
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
@@ -2859,17 +2860,18 @@ nsNavHistoryQueryResultNode::OnItemAdded
NS_IMETHODIMP
nsNavHistoryQueryResultNode::OnItemRemoved(int64_t aItemId,
int64_t aParentId,
int32_t aIndex,
uint16_t aItemType,
nsIURI* aURI,
const nsACString& aGUID,
- const nsACString& aParentGUID)
+ const nsACString& aParentGUID,
+ uint16_t aSource)
{
if (aItemType == nsINavBookmarksService::TYPE_BOOKMARK &&
mLiveUpdate != QUERYUPDATE_SIMPLE && mLiveUpdate != QUERYUPDATE_TIME) {
nsresult rv = Refresh();
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
@@ -2880,17 +2882,18 @@ nsNavHistoryQueryResultNode::OnItemChang
const nsACString& aProperty,
bool aIsAnnotationProperty,
const nsACString& aNewValue,
PRTime aLastModified,
uint16_t aItemType,
int64_t aParentId,
const nsACString& aGUID,
const nsACString& aParentGUID,
- const nsACString& aOldValue)
+ const nsACString& aOldValue,
+ uint16_t aSource)
{
// History observers should not get OnItemChanged
// but should get the corresponding history notifications instead.
// For bookmark queries, "all bookmark" observers should get OnItemChanged.
// For example, when a title of a bookmark changes, we want that to refresh.
if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS) {
switch (aItemType) {
@@ -2927,17 +2930,17 @@ nsNavHistoryQueryResultNode::OnItemChang
NS_ENSURE_SUCCESS(rv, rv);
}
}
return nsNavHistoryResultNode::OnItemChanged(aItemId, aProperty,
aIsAnnotationProperty,
aNewValue, aLastModified,
aItemType, aParentId, aGUID,
- aParentGUID, aOldValue);
+ aParentGUID, aOldValue, aSource);
}
NS_IMETHODIMP
nsNavHistoryQueryResultNode::OnItemVisited(int64_t aItemId,
int64_t aVisitId,
PRTime aTime,
uint32_t aTransitionType,
nsIURI* aURI,
@@ -2957,17 +2960,18 @@ NS_IMETHODIMP
nsNavHistoryQueryResultNode::OnItemMoved(int64_t aFolder,
int64_t aOldParent,
int32_t aOldIndex,
int64_t aNewParent,
int32_t aNewIndex,
uint16_t aItemType,
const nsACString& aGUID,
const nsACString& aOldParentGUID,
- const nsACString& aNewParentGUID)
+ const nsACString& aNewParentGUID,
+ uint16_t aSource)
{
// 1. The query cannot be affected by the item's position
// 2. For the time being, we cannot optimize this not to update
// queries which are not restricted to some folders, due to way
// sub-queries are updated (see Refresh)
if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS &&
aItemType != nsINavBookmarksService::TYPE_SEPARATOR &&
aOldParent != aNewParent) {
@@ -3528,17 +3532,18 @@ NS_IMETHODIMP
nsNavHistoryFolderResultNode::OnItemAdded(int64_t aItemId,
int64_t aParentFolder,
int32_t aIndex,
uint16_t aItemType,
nsIURI* aURI,
const nsACString& aTitle,
PRTime aDateAdded,
const nsACString& aGUID,
- const nsACString& aParentGUID)
+ const nsACString& aParentGUID,
+ uint16_t aSource)
{
MOZ_ASSERT(aParentFolder == mTargetFolderItemId, "Got wrong bookmark update");
RESTART_AND_RETURN_IF_ASYNC_PENDING();
{
uint32_t index;
nsNavHistoryResultNode* node = FindChildById(aItemId, &index);
@@ -3633,17 +3638,18 @@ nsNavHistoryFolderResultNode::OnItemAdde
NS_IMETHODIMP
nsNavHistoryFolderResultNode::OnItemRemoved(int64_t aItemId,
int64_t aParentFolder,
int32_t aIndex,
uint16_t aItemType,
nsIURI* aURI,
const nsACString& aGUID,
- const nsACString& aParentGUID)
+ const nsACString& aParentGUID,
+ uint16_t aSource)
{
// Folder shortcuts should not be notified removal of the target folder.
MOZ_ASSERT_IF(mItemId != mTargetFolderItemId, aItemId != mTargetFolderItemId);
// Concrete folders should not be notified their own removal.
// Note aItemId may equal mItemId for recursive folder shortcuts.
MOZ_ASSERT_IF(mItemId == mTargetFolderItemId, aItemId != mItemId);
// In any case though, here we only care about the children removal.
@@ -3693,17 +3699,18 @@ nsNavHistoryResultNode::OnItemChanged(in
const nsACString& aProperty,
bool aIsAnnotationProperty,
const nsACString& aNewValue,
PRTime aLastModified,
uint16_t aItemType,
int64_t aParentId,
const nsACString& aGUID,
const nsACString& aParentGUID,
- const nsACString& aOldValue)
+ const nsACString& aOldValue,
+ uint16_t aSource)
{
if (aItemId != mItemId)
return NS_OK;
mLastModified = aLastModified;
nsNavHistoryResult* result = GetResult();
NS_ENSURE_STATE(result);
@@ -3784,25 +3791,26 @@ nsNavHistoryFolderResultNode::OnItemChan
const nsACString& aProperty,
bool aIsAnnotationProperty,
const nsACString& aNewValue,
PRTime aLastModified,
uint16_t aItemType,
int64_t aParentId,
const nsACString& aGUID,
const nsACString& aParentGUID,
- const nsACString& aOldValue)
+ const nsACString& aOldValue,
+ uint16_t aSource)
{
RESTART_AND_RETURN_IF_ASYNC_PENDING();
return nsNavHistoryResultNode::OnItemChanged(aItemId, aProperty,
aIsAnnotationProperty,
aNewValue, aLastModified,
aItemType, aParentId, aGUID,
- aParentGUID, aOldValue);
+ aParentGUID, aOldValue, aSource);
}
/**
* Updates visit count and last visit time and refreshes.
*/
NS_IMETHODIMP
nsNavHistoryFolderResultNode::OnItemVisited(int64_t aItemId,
int64_t aVisitId,
@@ -3883,17 +3891,18 @@ NS_IMETHODIMP
nsNavHistoryFolderResultNode::OnItemMoved(int64_t aItemId,
int64_t aOldParent,
int32_t aOldIndex,
int64_t aNewParent,
int32_t aNewIndex,
uint16_t aItemType,
const nsACString& aGUID,
const nsACString& aOldParentGUID,
- const nsACString& aNewParentGUID)
+ const nsACString& aNewParentGUID,
+ uint16_t aSource)
{
NS_ASSERTION(aOldParent == mTargetFolderItemId || aNewParent == mTargetFolderItemId,
"Got a bookmark message that doesn't belong to us");
RESTART_AND_RETURN_IF_ASYNC_PENDING();
uint32_t index;
nsNavHistoryResultNode* node = FindChildById(aItemId, &index);
@@ -3945,22 +3954,22 @@ nsNavHistoryFolderResultNode::OnItemMove
NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = bookmarks->GetBookmarkURI(aItemId, getter_AddRefs(itemURI));
NS_ENSURE_SUCCESS(rv, rv);
rv = bookmarks->GetItemTitle(aItemId, itemTitle);
NS_ENSURE_SUCCESS(rv, rv);
}
if (aOldParent == mTargetFolderItemId) {
OnItemRemoved(aItemId, aOldParent, aOldIndex, aItemType, itemURI,
- aGUID, aOldParentGUID);
+ aGUID, aOldParentGUID, nsINavBookmarksService::SOURCE_DEFAULT);
}
if (aNewParent == mTargetFolderItemId) {
OnItemAdded(aItemId, aNewParent, aNewIndex, aItemType, itemURI, itemTitle,
RoundedPRNow(), // This is a dummy dateAdded, not the real value.
- aGUID, aNewParentGUID);
+ aGUID, aNewParentGUID, nsINavBookmarksService::SOURCE_DEFAULT);
}
}
return NS_OK;
}
/**
* Separator nodes do not hold any data.
@@ -4429,78 +4438,81 @@ NS_IMETHODIMP
nsNavHistoryResult::OnItemAdded(int64_t aItemId,
int64_t aParentId,
int32_t aIndex,
uint16_t aItemType,
nsIURI* aURI,
const nsACString& aTitle,
PRTime aDateAdded,
const nsACString& aGUID,
- const nsACString& aParentGUID)
+ const nsACString& aParentGUID,
+ uint16_t aSource)
{
NS_ENSURE_ARG(aItemType != nsINavBookmarksService::TYPE_BOOKMARK ||
aURI);
ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aParentId,
OnItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded,
- aGUID, aParentGUID)
+ aGUID, aParentGUID, aSource)
);
ENUMERATE_HISTORY_OBSERVERS(
OnItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded,
- aGUID, aParentGUID)
+ aGUID, aParentGUID, aSource)
);
ENUMERATE_ALL_BOOKMARKS_OBSERVERS(
OnItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded,
- aGUID, aParentGUID)
+ aGUID, aParentGUID, aSource)
);
return NS_OK;
}
NS_IMETHODIMP
nsNavHistoryResult::OnItemRemoved(int64_t aItemId,
int64_t aParentId,
int32_t aIndex,
uint16_t aItemType,
nsIURI* aURI,
const nsACString& aGUID,
- const nsACString& aParentGUID)
+ const nsACString& aParentGUID,
+ uint16_t aSource)
{
NS_ENSURE_ARG(aItemType != nsINavBookmarksService::TYPE_BOOKMARK ||
aURI);
ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aParentId,
OnItemRemoved(aItemId, aParentId, aIndex, aItemType, aURI, aGUID,
- aParentGUID));
+ aParentGUID, aSource));
ENUMERATE_ALL_BOOKMARKS_OBSERVERS(
OnItemRemoved(aItemId, aParentId, aIndex, aItemType, aURI, aGUID,
- aParentGUID));
+ aParentGUID, aSource));
ENUMERATE_HISTORY_OBSERVERS(
OnItemRemoved(aItemId, aParentId, aIndex, aItemType, aURI, aGUID,
- aParentGUID));
+ aParentGUID, aSource));
return NS_OK;
}
NS_IMETHODIMP
nsNavHistoryResult::OnItemChanged(int64_t aItemId,
const nsACString &aProperty,
bool aIsAnnotationProperty,
const nsACString &aNewValue,
PRTime aLastModified,
uint16_t aItemType,
int64_t aParentId,
const nsACString& aGUID,
const nsACString& aParentGUID,
- const nsACString& aOldValue)
+ const nsACString& aOldValue,
+ uint16_t aSource)
{
ENUMERATE_ALL_BOOKMARKS_OBSERVERS(
OnItemChanged(aItemId, aProperty, aIsAnnotationProperty, aNewValue,
aLastModified, aItemType, aParentId, aGUID, aParentGUID,
- aOldValue));
+ aOldValue, aSource));
// Note: folder-nodes set their own bookmark observer only once they're
// opened, meaning we cannot optimize this code path for changes done to
// folder-nodes.
FolderObserverList* list = BookmarkFolderObserversForId(aParentId, false);
if (!list)
return NS_OK;
@@ -4514,17 +4526,17 @@ nsNavHistoryResult::OnItemChanged(int64_
// if ExcludeItems is true we don't update non visible items
bool excludeItems = (mRootNode->mOptions->ExcludeItems()) ||
folder->mOptions->ExcludeItems();
if (node &&
(!excludeItems || !(node->IsURI() || node->IsSeparator())) &&
folder->StartIncrementalUpdate()) {
node->OnItemChanged(aItemId, aProperty, aIsAnnotationProperty,
aNewValue, aLastModified, aItemType, aParentId,
- aGUID, aParentGUID, aOldValue);
+ aGUID, aParentGUID, aOldValue, aSource);
}
}
}
// Note: we do NOT call history observers in this case. This notification is
// the same as other history notification, except that here we know the item
// is a bookmark. History observers will handle the history notification
// instead.
@@ -4565,35 +4577,36 @@ NS_IMETHODIMP
nsNavHistoryResult::OnItemMoved(int64_t aItemId,
int64_t aOldParent,
int32_t aOldIndex,
int64_t aNewParent,
int32_t aNewIndex,
uint16_t aItemType,
const nsACString& aGUID,
const nsACString& aOldParentGUID,
- const nsACString& aNewParentGUID)
+ const nsACString& aNewParentGUID,
+ uint16_t aSource)
{
ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aOldParent,
OnItemMoved(aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex,
- aItemType, aGUID, aOldParentGUID, aNewParentGUID));
+ aItemType, aGUID, aOldParentGUID, aNewParentGUID, aSource));
if (aNewParent != aOldParent) {
ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aNewParent,
OnItemMoved(aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex,
- aItemType, aGUID, aOldParentGUID, aNewParentGUID));
+ aItemType, aGUID, aOldParentGUID, aNewParentGUID, aSource));
}
ENUMERATE_ALL_BOOKMARKS_OBSERVERS(OnItemMoved(aItemId, aOldParent, aOldIndex,
aNewParent, aNewIndex,
aItemType, aGUID,
aOldParentGUID,
- aNewParentGUID));
+ aNewParentGUID, aSource));
ENUMERATE_HISTORY_OBSERVERS(OnItemMoved(aItemId, aOldParent, aOldIndex,
aNewParent, aNewIndex, aItemType,
aGUID, aOldParentGUID,
- aNewParentGUID));
+ aNewParentGUID, aSource));
return NS_OK;
}
NS_IMETHODIMP
nsNavHistoryResult::OnVisit(nsIURI* aURI, int64_t aVisitId, PRTime aTime,
int64_t aSessionId, int64_t aReferringId,
uint32_t aTransitionType, const nsACString& aGUID,
--- a/toolkit/components/places/nsNavHistoryResult.h
+++ b/toolkit/components/places/nsNavHistoryResult.h
@@ -286,17 +286,18 @@ public:
const nsACString &aProperty,
bool aIsAnnotationProperty,
const nsACString &aValue,
PRTime aNewLastModified,
uint16_t aItemType,
int64_t aParentId,
const nsACString& aGUID,
const nsACString& aParentGUID,
- const nsACString &aOldValue);
+ const nsACString &aOldValue,
+ uint16_t aSource);
protected:
virtual ~nsNavHistoryResultNode() {}
public:
nsNavHistoryResult* GetResult();
nsNavHistoryQueryOptions* GetGeneratingOptions();
--- a/toolkit/components/places/nsTaggingService.js
+++ b/toolkit/components/places/nsTaggingService.js
@@ -27,21 +27,24 @@ function TaggingService() {
}
TaggingService.prototype = {
/**
* Creates a tag container under the tags-root with the given name.
*
* @param aTagName
* the name for the new tag.
+ * @param aSource
+ * a change source constant from nsINavBookmarksService::SOURCE_*.
* @returns the id of the new tag container.
*/
- _createTag: function TS__createTag(aTagName) {
+ _createTag: function TS__createTag(aTagName, aSource) {
var newFolderId = PlacesUtils.bookmarks.createFolder(
- PlacesUtils.tagsFolderId, aTagName, PlacesUtils.bookmarks.DEFAULT_INDEX
+ PlacesUtils.tagsFolderId, aTagName, PlacesUtils.bookmarks.DEFAULT_INDEX,
+ /* aGuid */ null, aSource
);
// Add the folder to our local cache, so we can avoid doing this in the
// observer that would have to check itemType.
this._tagFolders[newFolderId] = aTagName;
return newFolderId;
},
@@ -130,46 +133,47 @@ TaggingService.prototype = {
else {
throw Cr.NS_ERROR_INVALID_ARG;
}
return tag;
});
},
// nsITaggingService
- tagURI: function TS_tagURI(aURI, aTags)
+ tagURI: function TS_tagURI(aURI, aTags, aSource)
{
if (!aURI || !aTags || !Array.isArray(aTags)) {
throw Cr.NS_ERROR_INVALID_ARG;
}
// This also does some input validation.
let tags = this._convertInputMixedTagsArray(aTags, true);
let taggingFunction = () => {
for (let tag of tags) {
if (tag.id == -1) {
// Tag does not exist yet, create it.
- this._createTag(tag.name);
+ this._createTag(tag.name, aSource);
}
if (this._getItemIdForTaggedURI(aURI, tag.name) == -1) {
// The provided URI is not yet tagged, add a tag for it.
// Note that bookmarks under tag containers must have null titles.
PlacesUtils.bookmarks.insertBookmark(
- tag.id, aURI, PlacesUtils.bookmarks.DEFAULT_INDEX, null
+ tag.id, aURI, PlacesUtils.bookmarks.DEFAULT_INDEX,
+ /* aTitle */ null, /* aGuid */ null, aSource
);
}
// Try to preserve user's tag name casing.
// Rename the tag container so the Places view matches the most-recent
// user-typed value.
if (PlacesUtils.bookmarks.getItemTitle(tag.id) != tag.name) {
// this._tagFolders is updated by the bookmarks observer.
- PlacesUtils.bookmarks.setItemTitle(tag.id, tag.name);
+ PlacesUtils.bookmarks.setItemTitle(tag.id, tag.name, aSource);
}
}
};
// Use a batch only if creating more than 2 tags.
if (tags.length < 3) {
taggingFunction();
} else {
@@ -177,18 +181,20 @@ TaggingService.prototype = {
}
},
/**
* Removes the tag container from the tags root if the given tag is empty.
*
* @param aTagId
* the itemId of the tag element under the tags root
+ * @param aSource
+ * a change source constant from nsINavBookmarksService::SOURCE_*
*/
- _removeTagIfEmpty: function TS__removeTagIfEmpty(aTagId) {
+ _removeTagIfEmpty: function TS__removeTagIfEmpty(aTagId, aSource) {
let count = 0;
let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
.DBConnection;
let stmt = db.createStatement(
`SELECT count(*) AS count FROM moz_bookmarks
WHERE parent = :tag_id`
);
stmt.params.tag_id = aTagId;
@@ -197,22 +203,22 @@ TaggingService.prototype = {
count = stmt.row.count;
}
}
finally {
stmt.finalize();
}
if (count == 0) {
- PlacesUtils.bookmarks.removeItem(aTagId);
+ PlacesUtils.bookmarks.removeItem(aTagId, aSource);
}
},
// nsITaggingService
- untagURI: function TS_untagURI(aURI, aTags)
+ untagURI: function TS_untagURI(aURI, aTags, aSource)
{
if (!aURI || (aTags && !Array.isArray(aTags))) {
throw Cr.NS_ERROR_INVALID_ARG;
}
if (!aTags) {
// Passing null should clear all tags for aURI, see the IDL.
// XXXmano: write a perf-sensitive version of this code path...
@@ -230,17 +236,17 @@ TaggingService.prototype = {
let untaggingFunction = () => {
for (let tag of tags) {
if (tag.id != -1) {
// A tag could exist.
let itemId = this._getItemIdForTaggedURI(aURI, tag.name);
if (itemId != -1) {
// There is a tagged item.
- PlacesUtils.bookmarks.removeItem(itemId);
+ PlacesUtils.bookmarks.removeItem(itemId, aSource);
}
}
}
};
// Use a batch only if creating more than 2 tags.
if (tags.length < 3) {
untaggingFunction();
@@ -409,36 +415,37 @@ TaggingService.prototype = {
if (aFolderId != PlacesUtils.tagsFolderId ||
aItemType != PlacesUtils.bookmarks.TYPE_FOLDER)
return;
this._tagFolders[aItemId] = aTitle;
},
onItemRemoved: function TS_onItemRemoved(aItemId, aFolderId, aIndex,
- aItemType, aURI) {
+ aItemType, aURI, aGuid, aParentGuid,
+ aSource) {
// Item is a tag folder.
if (aFolderId == PlacesUtils.tagsFolderId && this._tagFolders[aItemId]) {
delete this._tagFolders[aItemId];
}
// Item is a bookmark that was removed from a non-tag folder.
else if (aURI && !this._tagFolders[aFolderId]) {
// If the only bookmark items now associated with the bookmark's URI are
// contained in tag folders, the URI is no longer properly bookmarked, so
// untag it.
let itemIds = this._getTaggedItemIdsIfUnbookmarkedURI(aURI);
for (let i = 0; i < itemIds.length; i++) {
try {
- PlacesUtils.bookmarks.removeItem(itemIds[i]);
+ PlacesUtils.bookmarks.removeItem(itemIds[i], aSource);
} catch (ex) {}
}
}
// Item is a tag entry. If this was the last entry for this tag, remove it.
else if (aURI && this._tagFolders[aFolderId]) {
- this._removeTagIfEmpty(aFolderId);
+ this._removeTagIfEmpty(aFolderId, aSource);
}
},
onItemChanged: function TS_onItemChanged(aItemId, aProperty,
aIsAnnotationProperty, aNewValue,
aLastModified, aItemType) {
if (aProperty == "title" && this._tagFolders[aItemId])
this._tagFolders[aItemId] = aNewValue;
--- a/toolkit/components/places/tests/bookmarks/test_bookmarks_notifications.js
+++ b/toolkit/components/places/tests/bookmarks/test_bookmarks_notifications.js
@@ -5,73 +5,78 @@ add_task(function* insert_separator_noti
let observer = expectNotifications();
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
parentGuid: PlacesUtils.bookmarks.unfiledGuid});
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
observer.check([ { name: "onItemAdded",
arguments: [ itemId, parentId, bm.index, bm.type,
null, null, bm.dateAdded,
- bm.guid, bm.parentGuid ] }
+ bm.guid, bm.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(function* insert_folder_notification() {
let observer = expectNotifications();
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
title: "a folder" });
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
observer.check([ { name: "onItemAdded",
arguments: [ itemId, parentId, bm.index, bm.type,
null, bm.title, bm.dateAdded,
- bm.guid, bm.parentGuid ] }
+ bm.guid, bm.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(function* insert_folder_notitle_notification() {
let observer = expectNotifications();
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER,
parentGuid: PlacesUtils.bookmarks.unfiledGuid });
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
observer.check([ { name: "onItemAdded",
arguments: [ itemId, parentId, bm.index, bm.type,
null, null, bm.dateAdded,
- bm.guid, bm.parentGuid ] }
+ bm.guid, bm.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(function* insert_bookmark_notification() {
let observer = expectNotifications();
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: new URL("http://example.com/"),
title: "a bookmark" });
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
observer.check([ { name: "onItemAdded",
arguments: [ itemId, parentId, bm.index, bm.type,
bm.url, bm.title, bm.dateAdded,
- bm.guid, bm.parentGuid ] }
+ bm.guid, bm.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(function* insert_bookmark_notitle_notification() {
let observer = expectNotifications();
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: new URL("http://example.com/") });
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
observer.check([ { name: "onItemAdded",
arguments: [ itemId, parentId, bm.index, bm.type,
bm.url, null, bm.dateAdded,
- bm.guid, bm.parentGuid ] }
+ bm.guid, bm.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(function* insert_bookmark_tag_notification() {
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: new URL("http://tag.example.com/") });
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
@@ -85,72 +90,76 @@ add_task(function* insert_bookmark_tag_n
parentGuid: tagFolder.guid,
url: new URL("http://tag.example.com/") });
let tagId = yield PlacesUtils.promiseItemId(tag.guid);
let tagParentId = yield PlacesUtils.promiseItemId(tag.parentGuid);
observer.check([ { name: "onItemAdded",
arguments: [ tagId, tagParentId, tag.index, tag.type,
tag.url, null, tag.dateAdded,
- tag.guid, tag.parentGuid ] },
+ tag.guid, tag.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemChanged",
arguments: [ itemId, "tags", false, "",
bm.lastModified, bm.type, parentId,
- bm.guid, bm.parentGuid, "" ] }
+ bm.guid, bm.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(function* update_bookmark_lastModified() {
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: new URL("http://lastmod.example.com/") });
let observer = expectNotifications();
bm = yield PlacesUtils.bookmarks.update({ guid: bm.guid,
lastModified: new Date() });
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
observer.check([ { name: "onItemChanged",
arguments: [ itemId, "lastModified", false,
`${bm.lastModified * 1000}`, bm.lastModified,
- bm.type, parentId, bm.guid, bm.parentGuid, "" ] }
+ bm.type, parentId, bm.guid, bm.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(function* update_bookmark_title() {
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: new URL("http://title.example.com/") });
let observer = expectNotifications();
bm = yield PlacesUtils.bookmarks.update({ guid: bm.guid,
title: "new title" });
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
observer.check([ { name: "onItemChanged",
arguments: [ itemId, "title", false, bm.title,
bm.lastModified, bm.type, parentId, bm.guid,
- bm.parentGuid, "" ] }
+ bm.parentGuid, "", Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(function* update_bookmark_uri() {
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: new URL("http://url.example.com/") });
let observer = expectNotifications();
bm = yield PlacesUtils.bookmarks.update({ guid: bm.guid,
url: "http://mozilla.org/" });
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
observer.check([ { name: "onItemChanged",
arguments: [ itemId, "uri", false, bm.url.href,
bm.lastModified, bm.type, parentId, bm.guid,
- bm.parentGuid, "http://url.example.com/" ] }
+ bm.parentGuid, "http://url.example.com/",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(function* update_move_same_folder() {
// Ensure there are at least two items in place (others test do so for us,
// but we don't have to depend on that).
let sep = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
parentGuid: PlacesUtils.bookmarks.unfiledGuid });
@@ -163,29 +172,31 @@ add_task(function* update_move_same_fold
let observer = expectNotifications();
bm = yield PlacesUtils.bookmarks.update({ guid: bm.guid,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
index: 0 });
Assert.equal(bm.index, 0);
observer.check([ { name: "onItemMoved",
arguments: [ bmItemId, bmParentId, bmOldIndex, bmParentId, bm.index,
- bm.type, bm.guid, bm.parentGuid, bm.parentGuid ] }
+ bm.type, bm.guid, bm.parentGuid, bm.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
// Test that we get the right index for DEFAULT_INDEX input.
bmOldIndex = 0;
observer = expectNotifications();
bm = yield PlacesUtils.bookmarks.update({ guid: bm.guid,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
index: PlacesUtils.bookmarks.DEFAULT_INDEX });
Assert.ok(bm.index > 0);
observer.check([ { name: "onItemMoved",
arguments: [ bmItemId, bmParentId, bmOldIndex, bmParentId, bm.index,
- bm.type, bm.guid, bm.parentGuid, bm.parentGuid ] }
+ bm.type, bm.guid, bm.parentGuid, bm.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(function* update_move_different_folder() {
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: new URL("http://move.example.com/") });
let folder = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER,
@@ -199,48 +210,51 @@ add_task(function* update_move_different
parentGuid: folder.guid,
index: PlacesUtils.bookmarks.DEFAULT_INDEX });
Assert.equal(bm.index, 0);
let bmNewParentId = yield PlacesUtils.promiseItemId(folder.guid);
observer.check([ { name: "onItemMoved",
arguments: [ bmItemId, bmOldParentId, bmOldIndex, bmNewParentId,
bm.index, bm.type, bm.guid,
PlacesUtils.bookmarks.unfiledGuid,
- bm.parentGuid ] }
+ bm.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(function* remove_bookmark() {
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: new URL("http://remove.example.com/") });
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
let observer = expectNotifications();
bm = yield PlacesUtils.bookmarks.remove(bm.guid);
// TODO (Bug 653910): onItemAnnotationRemoved notified even if there were no
// annotations.
observer.check([ { name: "onItemRemoved",
arguments: [ itemId, parentId, bm.index, bm.type, bm.url,
- bm.guid, bm.parentGuid ] }
+ bm.guid, bm.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(function* remove_folder() {
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER,
parentGuid: PlacesUtils.bookmarks.unfiledGuid });
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
let parentId = yield PlacesUtils.promiseItemId(bm.parentGuid);
let observer = expectNotifications();
bm = yield PlacesUtils.bookmarks.remove(bm.guid);
observer.check([ { name: "onItemRemoved",
arguments: [ itemId, parentId, bm.index, bm.type, null,
- bm.guid, bm.parentGuid ] }
+ bm.guid, bm.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(function* remove_bookmark_tag_notification() {
let bm = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: new URL("http://untag.example.com/") });
let itemId = yield PlacesUtils.promiseItemId(bm.guid);
@@ -255,21 +269,23 @@ add_task(function* remove_bookmark_tag_n
let tagId = yield PlacesUtils.promiseItemId(tag.guid);
let tagParentId = yield PlacesUtils.promiseItemId(tag.parentGuid);
let observer = expectNotifications();
let removed = yield PlacesUtils.bookmarks.remove(tag.guid);
observer.check([ { name: "onItemRemoved",
arguments: [ tagId, tagParentId, tag.index, tag.type,
- tag.url, tag.guid, tag.parentGuid ] },
+ tag.url, tag.guid, tag.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemChanged",
arguments: [ itemId, "tags", false, "",
bm.lastModified, bm.type, parentId,
- bm.guid, bm.parentGuid, "" ] }
+ bm.guid, bm.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(function* remove_folder_notification() {
let folder1 = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER,
parentGuid: PlacesUtils.bookmarks.unfiledGuid });
let folder1Id = yield PlacesUtils.promiseItemId(folder1.guid);
let folder1ParentId = yield PlacesUtils.promiseItemId(folder1.parentGuid);
@@ -288,28 +304,32 @@ add_task(function* remove_folder_notific
url: new URL("http://example.com/") });
let bm2ItemId = yield PlacesUtils.promiseItemId(bm2.guid);
let observer = expectNotifications();
yield PlacesUtils.bookmarks.remove(folder1.guid);
observer.check([ { name: "onItemRemoved",
arguments: [ bm2ItemId, folder2Id, bm2.index, bm2.type,
- bm2.url, bm2.guid, bm2.parentGuid ] },
+ bm2.url, bm2.guid, bm2.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemRemoved",
arguments: [ folder2Id, folder1Id, folder2.index,
folder2.type, null, folder2.guid,
- folder2.parentGuid ] },
+ folder2.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemRemoved",
arguments: [ bmItemId, folder1Id, bm.index, bm.type,
- bm.url, bm.guid, bm.parentGuid ] },
+ bm.url, bm.guid, bm.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemRemoved",
arguments: [ folder1Id, folder1ParentId, folder1.index,
folder1.type, null, folder1.guid,
- folder1.parentGuid ] }
+ folder1.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(function* eraseEverything_notification() {
// Let's start from a clean situation.
yield PlacesUtils.bookmarks.eraseEverything();
let folder1 = yield PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER,
@@ -341,34 +361,39 @@ add_task(function* eraseEverything_notif
let menuBmParentId = yield PlacesUtils.promiseItemId(menuBm.parentGuid);
let observer = expectNotifications();
yield PlacesUtils.bookmarks.eraseEverything();
observer.check([ { name: "onItemRemoved",
arguments: [ folder2Id, folder2ParentId, folder2.index,
folder2.type, null, folder2.guid,
- folder2.parentGuid ] },
+ folder2.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemRemoved",
arguments: [ itemId, parentId, bm.index, bm.type,
- bm.url, bm.guid, bm.parentGuid ] },
+ bm.url, bm.guid, bm.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemRemoved",
arguments: [ folder1Id, folder1ParentId, folder1.index,
folder1.type, null, folder1.guid,
- folder1.parentGuid ] },
+ folder1.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemRemoved",
arguments: [ menuBmId, menuBmParentId,
menuBm.index, menuBm.type,
menuBm.url, menuBm.guid,
- menuBm.parentGuid ] },
+ menuBm.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemRemoved",
arguments: [ toolbarBmId, toolbarBmParentId,
toolbarBm.index, toolbarBm.type,
toolbarBm.url, toolbarBm.guid,
- toolbarBm.parentGuid ] }
+ toolbarBm.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(function* reorder_notification() {
let bookmarks = [
{ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
url: "http://example1.com/",
parentGuid: PlacesUtils.bookmarks.unfiledGuid
@@ -408,17 +433,18 @@ add_task(function* reorder_notification(
arguments: [ childId,
PlacesUtils.unfiledBookmarksFolderId,
child.index,
PlacesUtils.unfiledBookmarksFolderId,
i,
child.type,
child.guid,
child.parentGuid,
- child.parentGuid
+ child.parentGuid,
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT
] });
}
observer.check(expectedNotifications);
});
function expectNotifications() {
let notifications = [];
let observer = new Proxy(NavBookmarkObserver, {
--- a/toolkit/components/places/tests/bookmarks/test_keywords.js
+++ b/toolkit/components/places/tests/bookmarks/test_keywords.js
@@ -92,17 +92,18 @@ add_task(function* test_addBookmarkAndKe
"test");
PlacesUtils.bookmarks.setKeywordForBookmark(itemId, "keyword");
let bookmark = yield PlacesUtils.bookmarks.fetch({ url: URI1 });
observer.check([ { name: "onItemChanged",
arguments: [ itemId, "keyword", false, "keyword",
bookmark.lastModified, bookmark.type,
(yield PlacesUtils.promiseItemId(bookmark.parentGuid)),
- bookmark.guid, bookmark.parentGuid, "" ] }
+ bookmark.guid, bookmark.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
yield PlacesTestUtils.promiseAsyncUpdates();
check_keyword(URI1, "keyword");
Assert.equal((yield foreign_count(URI1)), fc + 2); // + 1 bookmark + 1 keyword
yield PlacesTestUtils.promiseAsyncUpdates();
yield check_orphans();
@@ -143,22 +144,24 @@ add_task(function* test_sameKeywordDiffe
let bookmark1 = yield PlacesUtils.bookmarks.fetch({ url: URI1 });
let bookmark2 = yield PlacesUtils.bookmarks.fetch({ url: URI2 });
observer.check([ { name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmark1.guid)),
"keyword", false, "",
bookmark1.lastModified, bookmark1.type,
(yield PlacesUtils.promiseItemId(bookmark1.parentGuid)),
- bookmark1.guid, bookmark1.parentGuid, "" ] },
+ bookmark1.guid, bookmark1.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemChanged",
arguments: [ itemId, "keyword", false, "keyword",
bookmark2.lastModified, bookmark2.type,
(yield PlacesUtils.promiseItemId(bookmark2.parentGuid)),
- bookmark2.guid, bookmark2.parentGuid, "" ] }
+ bookmark2.guid, bookmark2.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
yield PlacesTestUtils.promiseAsyncUpdates();
// The keyword should have been "moved" to the new URI.
check_keyword(URI1, null);
Assert.equal((yield foreign_count(URI1)), fc1 - 1); // - 1 keyword
check_keyword(URI2, "keyword");
Assert.equal((yield foreign_count(URI2)), fc2 + 2); // + 1 bookmark + 1 keyword
@@ -182,23 +185,25 @@ add_task(function* test_sameURIDifferent
let bookmarks = [];
yield PlacesUtils.bookmarks.fetch({ url: URI2 }, bookmark => bookmarks.push(bookmark));
observer.check([ { name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmarks[0].guid)),
"keyword", false, "keyword2",
bookmarks[0].lastModified, bookmarks[0].type,
(yield PlacesUtils.promiseItemId(bookmarks[0].parentGuid)),
- bookmarks[0].guid, bookmarks[0].parentGuid, "" ] },
+ bookmarks[0].guid, bookmarks[0].parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmarks[1].guid)),
"keyword", false, "keyword2",
bookmarks[1].lastModified, bookmarks[1].type,
(yield PlacesUtils.promiseItemId(bookmarks[1].parentGuid)),
- bookmarks[1].guid, bookmarks[1].parentGuid, "" ] }
+ bookmarks[1].guid, bookmarks[1].parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
yield PlacesTestUtils.promiseAsyncUpdates();
check_keyword(URI2, "keyword2");
Assert.equal((yield foreign_count(URI2)), fc + 2); // + 1 bookmark + 1 keyword
yield PlacesTestUtils.promiseAsyncUpdates();
check_orphans();
@@ -238,29 +243,32 @@ add_task(function* test_unsetKeyword() {
let bookmarks = [];
yield PlacesUtils.bookmarks.fetch({ url: URI2 }, bookmark => bookmarks.push(bookmark));
do_print(bookmarks.length);
observer.check([ { name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmarks[0].guid)),
"keyword", false, "",
bookmarks[0].lastModified, bookmarks[0].type,
(yield PlacesUtils.promiseItemId(bookmarks[0].parentGuid)),
- bookmarks[0].guid, bookmarks[0].parentGuid, "" ] },
+ bookmarks[0].guid, bookmarks[0].parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmarks[1].guid)),
"keyword", false, "",
bookmarks[1].lastModified, bookmarks[1].type,
(yield PlacesUtils.promiseItemId(bookmarks[1].parentGuid)),
- bookmarks[1].guid, bookmarks[1].parentGuid, "" ] },
+ bookmarks[1].guid, bookmarks[1].parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmarks[2].guid)),
"keyword", false, "",
bookmarks[2].lastModified, bookmarks[2].type,
(yield PlacesUtils.promiseItemId(bookmarks[2].parentGuid)),
- bookmarks[2].guid, bookmarks[2].parentGuid, "" ] }
+ bookmarks[2].guid, bookmarks[2].parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
check_keyword(URI1, null);
check_keyword(URI2, null);
Assert.equal((yield foreign_count(URI2)), fc - 1); // + 1 bookmark - 2 keyword
yield PlacesTestUtils.promiseAsyncUpdates();
check_orphans();
@@ -281,17 +289,18 @@ add_task(function* test_addRemoveBookmar
let parentId = yield PlacesUtils.promiseItemId(bookmark.parentGuid);
PlacesUtils.bookmarks.removeItem(itemId);
observer.check([ { name: "onItemChanged",
arguments: [ itemId,
"keyword", false, "keyword",
bookmark.lastModified, bookmark.type,
parentId,
- bookmark.guid, bookmark.parentGuid, "" ] }
+ bookmark.guid, bookmark.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
check_keyword(URI3, null);
// Don't check the foreign count since the process is async.
// The new test_keywords.js in unit is checking this though.
yield PlacesTestUtils.promiseAsyncUpdates();
check_orphans();
--- a/toolkit/components/places/tests/bookmarks/test_nsINavBookmarkObserver.js
+++ b/toolkit/components/places/tests/bookmarks/test_nsINavBookmarkObserver.js
@@ -71,16 +71,17 @@ add_test(function onItemAdded_bookmark()
{ name: "parentId", check: v => v === PlacesUtils.unfiledBookmarksFolderId },
{ name: "index", check: v => v === 0 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK },
{ name: "uri", check: v => v instanceof Ci.nsIURI && v.equals(uri) },
{ name: "title", check: v => v === TITLE },
{ name: "dateAdded", check: v => typeof(v) == "number" && v > 0 },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
];
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
uri, PlacesUtils.bookmarks.DEFAULT_INDEX,
TITLE);
});
add_test(function onItemAdded_separator() {
@@ -91,16 +92,17 @@ add_test(function onItemAdded_separator(
{ name: "parentId", check: v => v === PlacesUtils.unfiledBookmarksFolderId },
{ name: "index", check: v => v === 1 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_SEPARATOR },
{ name: "uri", check: v => v === null },
{ name: "title", check: v => v === null },
{ name: "dateAdded", check: v => typeof(v) == "number" && v > 0 },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
];
PlacesUtils.bookmarks.insertSeparator(PlacesUtils.unfiledBookmarksFolderId,
PlacesUtils.bookmarks.DEFAULT_INDEX);
});
add_test(function onItemAdded_folder() {
const TITLE = "Folder 1";
@@ -111,16 +113,17 @@ add_test(function onItemAdded_folder() {
{ name: "parentId", check: v => v === PlacesUtils.unfiledBookmarksFolderId },
{ name: "index", check: v => v === 2 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_FOLDER },
{ name: "uri", check: v => v === null },
{ name: "title", check: v => v === TITLE },
{ name: "dateAdded", check: v => typeof(v) == "number" && v > 0 },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
];
PlacesUtils.bookmarks.createFolder(PlacesUtils.unfiledBookmarksFolderId,
TITLE,
PlacesUtils.bookmarks.DEFAULT_INDEX);
});
add_test(function onItemChanged_title_bookmark() {
@@ -135,16 +138,17 @@ add_test(function onItemChanged_title_bo
{ name: "isAnno", check: v => v === false },
{ name: "newValue", check: v => v === TITLE },
{ name: "lastModified", check: v => typeof(v) == "number" && v > 0 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK },
{ name: "parentId", check: v => v === PlacesUtils.unfiledBookmarksFolderId },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "oldValue", check: v => typeof(v) == "string" },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
];
PlacesUtils.bookmarks.setItemTitle(id, TITLE);
});
add_test(function onItemChanged_tags_bookmark() {
let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.unfiledBookmarksFolderId, 0);
let uri = PlacesUtils.bookmarks.getBookmarkURI(id);
@@ -157,74 +161,80 @@ add_test(function onItemChanged_tags_boo
{ name: "parentId", check: v => v === PlacesUtils.tagsFolderId },
{ name: "index", check: v => v === 0 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_FOLDER },
{ name: "uri", check: v => v === null },
{ name: "title", check: v => v === TAG },
{ name: "dateAdded", check: v => typeof(v) == "number" && v > 0 },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
{ name: "onItemAdded", // This is the tag.
args: [
{ name: "itemId", check: v => typeof(v) == "number" && v > 0 },
{ name: "parentId", check: v => typeof(v) == "number" && v > 0 },
{ name: "index", check: v => v === 0 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK },
{ name: "uri", check: v => v instanceof Ci.nsIURI && v.equals(uri) },
{ name: "title", check: v => v === null },
{ name: "dateAdded", check: v => typeof(v) == "number" && v > 0 },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
{ name: "onItemChanged",
args: [
{ name: "itemId", check: v => typeof(v) == "number" && v > 0 },
{ name: "property", check: v => v === "tags" },
{ name: "isAnno", check: v => v === false },
{ name: "newValue", check: v => v === "" },
{ name: "lastModified", check: v => typeof(v) == "number" && v > 0 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK },
{ name: "parentId", check: v => v === PlacesUtils.unfiledBookmarksFolderId },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "oldValue", check: v => typeof(v) == "string" },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
{ name: "onItemRemoved", // This is the tag.
args: [
{ name: "itemId", check: v => typeof(v) == "number" && v > 0 },
{ name: "parentId", check: v => typeof(v) == "number" && v > 0 },
{ name: "index", check: v => v === 0 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK },
{ name: "uri", check: v => v instanceof Ci.nsIURI && v.equals(uri) },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
{ name: "onItemRemoved", // This is the tag folder.
args: [
{ name: "itemId", check: v => typeof(v) == "number" && v > 0 },
{ name: "parentId", check: v => v === PlacesUtils.tagsFolderId },
{ name: "index", check: v => v === 0 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_FOLDER },
{ name: "uri", check: v => v === null },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
{ name: "onItemChanged",
args: [
{ name: "itemId", check: v => typeof(v) == "number" && v > 0 },
{ name: "property", check: v => v === "tags" },
{ name: "isAnno", check: v => v === false },
{ name: "newValue", check: v => v === "" },
{ name: "lastModified", check: v => typeof(v) == "number" && v > 0 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK },
{ name: "parentId", check: v => v === PlacesUtils.unfiledBookmarksFolderId },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "oldValue", check: v => typeof(v) == "string" },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
];
PlacesUtils.tagging.tagURI(uri, [TAG]);
PlacesUtils.tagging.untagURI(uri, [TAG]);
});
add_test(function onItemMoved_bookmark() {
let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.unfiledBookmarksFolderId, 0);
@@ -236,28 +246,30 @@ add_test(function onItemMoved_bookmark()
{ name: "oldParentId", check: v => v === PlacesUtils.unfiledBookmarksFolderId },
{ name: "oldIndex", check: v => v === 0 },
{ name: "newParentId", check: v => v === PlacesUtils.toolbarFolderId },
{ name: "newIndex", check: v => v === 0 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "oldParentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "newParentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
{ name: "onItemMoved",
args: [
{ name: "itemId", check: v => typeof(v) == "number" && v > 0 },
{ name: "oldParentId", check: v => v === PlacesUtils.toolbarFolderId },
{ name: "oldIndex", check: v => v === 0 },
{ name: "newParentId", check: v => v === PlacesUtils.unfiledBookmarksFolderId },
{ name: "newIndex", check: v => v === 0 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "oldParentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "newParentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
];
PlacesUtils.bookmarks.moveItem(id, PlacesUtils.toolbarFolderId, 0);
PlacesUtils.bookmarks.moveItem(id, PlacesUtils.unfiledBookmarksFolderId, 0);
});
add_test(function onItemMoved_bookmark() {
let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.unfiledBookmarksFolderId, 0);
@@ -289,26 +301,28 @@ add_test(function onItemRemoved_bookmark
{ name: "isAnno", check: v => v === true },
{ name: "newValue", check: v => v === "" },
{ name: "lastModified", check: v => typeof(v) == "number" && v > 0 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK },
{ name: "parentId", check: v => v === PlacesUtils.unfiledBookmarksFolderId },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "oldValue", check: v => typeof(v) == "string" },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
{ name: "onItemRemoved",
args: [
{ name: "itemId", check: v => typeof(v) == "number" && v > 0 },
{ name: "parentId", check: v => v === PlacesUtils.unfiledBookmarksFolderId },
{ name: "index", check: v => v === 0 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK },
{ name: "uri", check: v => v instanceof Ci.nsIURI && v.equals(uri) },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
];
PlacesUtils.bookmarks.removeItem(id);
});
add_test(function onItemRemoved_separator() {
let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.unfiledBookmarksFolderId, 0);
gBookmarksObserver.expected = [
@@ -319,26 +333,28 @@ add_test(function onItemRemoved_separato
{ name: "isAnno", check: v => v === true },
{ name: "newValue", check: v => v === "" },
{ name: "lastModified", check: v => typeof(v) == "number" && v > 0 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_SEPARATOR },
{ name: "parentId", check: v => typeof(v) == "number" && v > 0 },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "oldValue", check: v => typeof(v) == "string" },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
{ name: "onItemRemoved",
args: [
{ name: "itemId", check: v => typeof(v) == "number" && v > 0 },
{ name: "parentId", check: v => typeof(v) == "number" && v > 0 },
{ name: "index", check: v => v === 0 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_SEPARATOR },
{ name: "uri", check: v => v === null },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
];
PlacesUtils.bookmarks.removeItem(id);
});
add_test(function onItemRemoved_folder() {
let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.unfiledBookmarksFolderId, 0);
const TITLE = "Folder 2";
@@ -350,26 +366,28 @@ add_test(function onItemRemoved_folder()
{ name: "isAnno", check: v => v === true },
{ name: "newValue", check: v => v === "" },
{ name: "lastModified", check: v => typeof(v) == "number" && v > 0 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_FOLDER },
{ name: "parentId", check: v => typeof(v) == "number" && v > 0 },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "oldValue", check: v => typeof(v) == "string" },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
{ name: "onItemRemoved",
args: [
{ name: "itemId", check: v => typeof(v) == "number" && v > 0 },
{ name: "parentId", check: v => typeof(v) == "number" && v > 0 },
{ name: "index", check: v => v === 0 },
{ name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_FOLDER },
{ name: "uri", check: v => v === null },
{ name: "guid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && /^[a-zA-Z0-9\-_]{12}$/.test(v) },
+ { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
];
PlacesUtils.bookmarks.removeItem(id);
});
function run_test() {
PlacesUtils.bookmarks.addObserver(gBookmarksObserver, false);
run_next_test();
--- a/toolkit/components/places/tests/unit/test_keywords.js
+++ b/toolkit/components/places/tests/unit/test_keywords.js
@@ -180,31 +180,33 @@ add_task(function* test_addBookmarkAndKe
let observer = expectBookmarkNotifications();
yield PlacesUtils.keywords.insert({ keyword: "keyword", url: "http://example.com/" });
observer.check([{ name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmark.guid)),
"keyword", false, "keyword",
bookmark.lastModified, bookmark.type,
(yield PlacesUtils.promiseItemId(bookmark.parentGuid)),
- bookmark.guid, bookmark.parentGuid, "" ] } ]);
+ bookmark.guid, bookmark.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] } ]);
yield check_keyword(true, "http://example.com/", "keyword");
Assert.equal((yield foreign_count("http://example.com/")), fc + 2); // +1 bookmark +1 keyword
// Now remove the keyword.
observer = expectBookmarkNotifications();
yield PlacesUtils.keywords.remove("keyword");
observer.check([{ name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmark.guid)),
"keyword", false, "",
bookmark.lastModified, bookmark.type,
(yield PlacesUtils.promiseItemId(bookmark.parentGuid)),
- bookmark.guid, bookmark.parentGuid, "" ] } ]);
+ bookmark.guid, bookmark.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] } ]);
yield check_keyword(false, "http://example.com/", "keyword");
Assert.equal((yield foreign_count("http://example.com/")), fc + 1); // -1 keyword
// Add again the keyword, then remove the bookmark.
yield PlacesUtils.keywords.insert({ keyword: "keyword", url: "http://example.com/" });
observer = expectBookmarkNotifications();
@@ -307,38 +309,41 @@ add_task(function* test_sameKeywordDiffe
let observer = expectBookmarkNotifications();
yield PlacesUtils.keywords.insert({ keyword: "keyword", url: "http://example2.com/" });
observer.check([{ name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmark1.guid)),
"keyword", false, "",
bookmark1.lastModified, bookmark1.type,
(yield PlacesUtils.promiseItemId(bookmark1.parentGuid)),
- bookmark1.guid, bookmark1.parentGuid, "" ] },
+ bookmark1.guid, bookmark1.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmark2.guid)),
"keyword", false, "keyword",
bookmark2.lastModified, bookmark2.type,
(yield PlacesUtils.promiseItemId(bookmark2.parentGuid)),
- bookmark2.guid, bookmark2.parentGuid, "" ] } ]);
+ bookmark2.guid, bookmark2.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] } ]);
yield check_keyword(false, "http://example1.com/", "keyword");
Assert.equal((yield foreign_count("http://example1.com/")), fc1 + 1); // -1 keyword
yield check_keyword(true, "http://example2.com/", "keyword");
Assert.equal((yield foreign_count("http://example2.com/")), fc2 + 2); // +1 keyword
// Now remove the keyword.
observer = expectBookmarkNotifications();
yield PlacesUtils.keywords.remove("keyword");
observer.check([{ name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmark2.guid)),
"keyword", false, "",
bookmark2.lastModified, bookmark2.type,
(yield PlacesUtils.promiseItemId(bookmark2.parentGuid)),
- bookmark2.guid, bookmark2.parentGuid, "" ] } ]);
+ bookmark2.guid, bookmark2.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] } ]);
yield check_keyword(false, "http://example1.com/", "keyword");
yield check_keyword(false, "http://example2.com/", "keyword");
Assert.equal((yield foreign_count("http://example1.com/")), fc1 + 1);
Assert.equal((yield foreign_count("http://example2.com/")), fc2 + 1); // -1 keyword
yield PlacesUtils.bookmarks.remove(bookmark1);
yield PlacesUtils.bookmarks.remove(bookmark2);
@@ -360,29 +365,31 @@ add_task(function* test_sameURIDifferent
yield check_keyword(true, "http://example.com/", "keyword");
Assert.equal((yield foreign_count("http://example.com/")), fc + 2); // +1 bookmark +1 keyword
observer.check([{ name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmark.guid)),
"keyword", false, "keyword",
bookmark.lastModified, bookmark.type,
(yield PlacesUtils.promiseItemId(bookmark.parentGuid)),
- bookmark.guid, bookmark.parentGuid, "" ] } ]);
+ bookmark.guid, bookmark.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] } ]);
observer = expectBookmarkNotifications();
yield PlacesUtils.keywords.insert({ keyword: "keyword2", url: "http://example.com/" });
yield check_keyword(true, "http://example.com/", "keyword");
yield check_keyword(true, "http://example.com/", "keyword2");
Assert.equal((yield foreign_count("http://example.com/")), fc + 3); // +1 keyword
observer.check([{ name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmark.guid)),
"keyword", false, "keyword2",
bookmark.lastModified, bookmark.type,
(yield PlacesUtils.promiseItemId(bookmark.parentGuid)),
- bookmark.guid, bookmark.parentGuid, "" ] } ]);
+ bookmark.guid, bookmark.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] } ]);
// Add a third keyword.
yield PlacesUtils.keywords.insert({ keyword: "keyword3", url: "http://example.com/" });
yield check_keyword(true, "http://example.com/", "keyword");
yield check_keyword(true, "http://example.com/", "keyword2");
yield check_keyword(true, "http://example.com/", "keyword3");
Assert.equal((yield foreign_count("http://example.com/")), fc + 4); // +1 keyword
@@ -392,17 +399,18 @@ add_task(function* test_sameURIDifferent
yield check_keyword(false, "http://example.com/", "keyword");
yield check_keyword(true, "http://example.com/", "keyword2");
yield check_keyword(true, "http://example.com/", "keyword3");
observer.check([{ name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmark.guid)),
"keyword", false, "",
bookmark.lastModified, bookmark.type,
(yield PlacesUtils.promiseItemId(bookmark.parentGuid)),
- bookmark.guid, bookmark.parentGuid, "" ] } ]);
+ bookmark.guid, bookmark.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] } ]);
Assert.equal((yield foreign_count("http://example.com/")), fc + 3); // -1 keyword
// Now remove the bookmark.
yield PlacesUtils.bookmarks.remove(bookmark);
while ((yield foreign_count("http://example.com/")));
yield check_keyword(false, "http://example.com/", "keyword");
yield check_keyword(false, "http://example.com/", "keyword2");
yield check_keyword(false, "http://example.com/", "keyword3");
@@ -424,40 +432,44 @@ add_task(function* test_deleteKeywordMul
yield check_keyword(true, "http://example.com/", "keyword");
Assert.equal((yield foreign_count("http://example.com/")), fc + 3); // +2 bookmark +1 keyword
observer.check([{ name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmark2.guid)),
"keyword", false, "keyword",
bookmark2.lastModified, bookmark2.type,
(yield PlacesUtils.promiseItemId(bookmark2.parentGuid)),
- bookmark2.guid, bookmark2.parentGuid, "" ] },
+ bookmark2.guid, bookmark2.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmark1.guid)),
"keyword", false, "keyword",
bookmark1.lastModified, bookmark1.type,
(yield PlacesUtils.promiseItemId(bookmark1.parentGuid)),
- bookmark1.guid, bookmark1.parentGuid, "" ] } ]);
+ bookmark1.guid, bookmark1.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] } ]);
observer = expectBookmarkNotifications();
yield PlacesUtils.keywords.remove("keyword");
yield check_keyword(false, "http://example.com/", "keyword");
Assert.equal((yield foreign_count("http://example.com/")), fc + 2); // -1 keyword
observer.check([{ name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmark2.guid)),
"keyword", false, "",
bookmark2.lastModified, bookmark2.type,
(yield PlacesUtils.promiseItemId(bookmark2.parentGuid)),
- bookmark2.guid, bookmark2.parentGuid, "" ] },
+ bookmark2.guid, bookmark2.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemChanged",
arguments: [ (yield PlacesUtils.promiseItemId(bookmark1.guid)),
"keyword", false, "",
bookmark1.lastModified, bookmark1.type,
(yield PlacesUtils.promiseItemId(bookmark1.parentGuid)),
- bookmark1.guid, bookmark1.parentGuid, "" ] } ]);
+ bookmark1.guid, bookmark1.parentGuid, "",
+ Ci.nsINavBookmarksService.SOURCE_DEFAULT ] } ]);
// Now remove the bookmarks.
yield PlacesUtils.bookmarks.remove(bookmark1);
yield PlacesUtils.bookmarks.remove(bookmark2);
Assert.equal((yield foreign_count("http://example.com/")), fc); // -2 bookmarks
check_no_orphans();
});