--- a/services/sync/tests/unit/test_bookmark_places_query_rewriting.js
+++ b/services/sync/tests/unit/test_bookmark_places_query_rewriting.js
@@ -16,38 +16,25 @@ function makeTagRecord(id, uri) {
tagRecord.bmkUri = uri;
tagRecord.title = "tagtag";
tagRecord.folderName = "bar";
tagRecord.parentid = PlacesUtils.bookmarks.toolbarGuid;
return tagRecord;
}
add_task(async function run_test() {
-
let uri = "place:folder=499&type=7&queryType=1";
let tagRecord = makeTagRecord("abcdefabcdef", uri);
_("Type: " + tagRecord.type);
_("Folder name: " + tagRecord.folderName);
await store.applyIncoming(tagRecord);
- let tagID = -1;
- let db = await PlacesUtils.promiseDBConnection();
- let rows = await db.execute(`
- SELECT id FROM moz_bookmarks
- WHERE parent = :tagsFolderId AND
- title = :title`,
- { tagsFolderId: PlacesUtils.tagsFolderId,
- title: "bar" });
- equal(rows.length, 1);
- tagID = rows[0].getResultByName("id");
-
- _("Tag ID: " + tagID);
let insertedRecord = await store.createRecord("abcdefabcdef", "bookmarks");
- Assert.equal(insertedRecord.bmkUri, uri.replace("499", tagID));
+ Assert.equal(insertedRecord.bmkUri, "place:tag=bar");
_("... but not if the type is wrong.");
let wrongTypeURI = "place:folder=499&type=2&queryType=1";
let wrongTypeRecord = makeTagRecord("fedcbafedcba", wrongTypeURI);
await store.applyIncoming(wrongTypeRecord);
insertedRecord = await store.createRecord("fedcbafedcba", "bookmarks");
Assert.equal(insertedRecord.bmkUri, wrongTypeURI);
--- a/toolkit/components/places/PlacesSyncUtils.jsm
+++ b/toolkit/components/places/PlacesSyncUtils.jsm
@@ -1397,40 +1397,39 @@ var GUIDMissing = async function(guid) {
} catch (ex) {
if (ex.message == "no item found for the given GUID") {
return true;
}
throw ex;
}
};
-// Tag queries use a `place:` URL that refers to the tag folder ID. When we
-// apply a synced tag query from a remote client, we need to update the URL to
-// point to the local tag folder.
-async function updateTagQueryFolder(db, info) {
+// Legacy tag queries may use a `place:` URL that refers to the tag folder ID.
+// When we apply a synced tag query from a remote client, we need to update the
+// URL to point to the local tag.
+function updateTagQueryFolder(db, info) {
if (info.kind != BookmarkSyncUtils.KINDS.QUERY || !info.folder || !info.url ||
info.url.protocol != "place:") {
return info;
}
let params = new URLSearchParams(info.url.pathname);
let type = +params.get("type");
-
if (type != Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_CONTENTS) {
return info;
}
- let id = await getOrCreateTagFolder(db, info.folder);
- BookmarkSyncLog.debug(`updateTagQueryFolder: Tag query folder: ${
- info.folder} = ${id}`);
+ BookmarkSyncLog.debug(`updateTagQueryFolder: Tag query folder: ${info.folder}`);
- // Rewrite the query to reference the new ID.
- params.set("folder", id);
+ // Rewrite the query to directly reference the tag.
+ params.delete("queryType");
+ params.delete("type");
+ params.delete("folder");
+ params.set("tag", info.folder);
info.url = new URL(info.url.protocol + params);
-
return info;
}
async function annotateOrphan(item, requestedParentRecordId) {
let guid = BookmarkSyncUtils.recordIdToGuid(item.recordId);
let itemId = await PlacesUtils.promiseItemId(guid);
PlacesUtils.annotations.setItemAnnotation(itemId,
BookmarkSyncUtils.SYNC_PARENT_ANNO, requestedParentRecordId, 0,
@@ -1959,45 +1958,16 @@ function tagItem(item, tags) {
// but doesn't know about additional livemark properties. We check this to avoid
// having it throw in case we only pass properties like `{ guid, feedURI }`.
function shouldUpdateBookmark(bookmarkInfo) {
return bookmarkInfo.hasOwnProperty("parentGuid") ||
bookmarkInfo.hasOwnProperty("title") ||
bookmarkInfo.hasOwnProperty("url");
}
-// Returns the folder ID for `tag`, or `null` if the tag doesn't exist.
-async function getTagFolder(db, tag) {
- let results = await db.executeCached(`
- SELECT id
- FROM moz_bookmarks
- WHERE type = :type AND
- parent = :tagsFolderId AND
- title = :tag`,
- { type: PlacesUtils.bookmarks.TYPE_FOLDER,
- tagsFolderId: PlacesUtils.tagsFolderId, tag });
- return results.length ? results[0].getResultByName("id") : null;
-}
-
-// Returns the folder ID for `tag`, creating one if it doesn't exist.
-async function getOrCreateTagFolder(db, tag) {
- let id = await getTagFolder(db, tag);
- if (id) {
- return id;
- }
- // Create the tag if it doesn't exist.
- let item = await PlacesUtils.bookmarks.insert({
- type: PlacesUtils.bookmarks.TYPE_FOLDER,
- parentGuid: PlacesUtils.bookmarks.tagsGuid,
- title: tag,
- source: SOURCE_SYNC,
- });
- return PlacesUtils.promiseItemId(item.guid);
-}
-
// Converts a Places bookmark or livemark to a Sync bookmark. This function
// maps Places GUIDs to record IDs and filters out extra Places properties like
// date added, last modified, and index.
async function placesBookmarkToSyncBookmark(db, bookmarkItem) {
let item = {};
for (let prop in bookmarkItem) {
switch (prop) {
@@ -2187,31 +2157,20 @@ async function fetchQueryItem(db, bookma
let item = await placesBookmarkToSyncBookmark(db, bookmarkItem);
let description = await getAnno(db, bookmarkItem.guid,
BookmarkSyncUtils.DESCRIPTION_ANNO);
if (description) {
item.description = description;
}
- let folder = null;
let params = new URLSearchParams(bookmarkItem.url.pathname);
- let tagFolderId = +params.get("folder");
- if (tagFolderId) {
- try {
- let tagFolderGuid = await PlacesUtils.promiseItemGuid(tagFolderId);
- let tagFolder = await PlacesUtils.bookmarks.fetch(tagFolderGuid);
- folder = tagFolder.title;
- } catch (ex) {
- BookmarkSyncLog.warn("fetchQueryItem: Query " + bookmarkItem.url.href +
- " points to nonexistent folder " + tagFolderId, ex);
- }
- }
- if (folder != null) {
- item.folder = folder;
+ let tags = params.getAll("tag");
+ if (tags.length == 1) {
+ item.folder = tags[0];
}
let query = await getAnno(db, bookmarkItem.guid,
BookmarkSyncUtils.SMART_BOOKMARKS_ANNO);
if (query) {
item.query = query;
}
--- a/toolkit/components/places/SyncedBookmarksMirror.jsm
+++ b/toolkit/components/places/SyncedBookmarksMirror.jsm
@@ -1351,64 +1351,46 @@ class SyncedBookmarksMirror {
needsMerge = 0
WHERE needsMerge AND
guid IN (${new Array(chunk.length).fill("?").join(",")})`,
chunk);
}
}
/**
- * Creates local tag folders mentioned in remotely changed tag queries, then
- * rewrites the query URLs in the mirror to point to the new local folders.
+ * Rewrites tag query URLs in the mirror to point to the tag.
*
- * For example, an incoming tag query of, say, "place:type=6&folder=3" means
- * it is a query for whatever tag is defined by the local record with id=3
+ * For example, an incoming tag query of, say, "place:type=7&folder=3" means
+ * it is a query for whatever tag was defined by the local record with id=3
* (and the incoming record has the actual tag in the folderName field).
- * We need to ensure that the URL is adjusted so the folder ID refers to
- * whatever local folder ID represents that tag.
- *
- * This can be removed once bug 1293445 lands.
*/
async rewriteRemoteTagQueries() {
- // Create local tag folders that don't already exist. This fires the
- // `tagLocalPlace` trigger.
- await this.db.execute(`
- INSERT INTO localTags(tag)
- SELECT v.tagFolderName FROM items v
+ let queryRows = await this.db.execute(`
+ SELECT u.id AS urlId, u.url, v.tagFolderName
+ FROM urls u
+ JOIN items v ON v.urlId = u.id
JOIN mergeStates r ON r.mergedGuid = v.guid
WHERE r.valueState = :valueState AND
- v.tagFolderName NOT NULL`,
- { valueState: BookmarkMergeState.TYPE.REMOTE });
-
- let queryRows = await this.db.execute(`
- SELECT u.id AS urlId, u.url, b.id AS newTagFolderId FROM urls u
- JOIN items v ON v.urlId = u.id
- JOIN mergeStates r ON r.mergedGuid = v.guid
- JOIN moz_bookmarks b ON b.title = v.tagFolderName
- JOIN moz_bookmarks p ON p.id = b.parent
- WHERE p.guid = :tagsGuid AND
- r.valueState = :valueState AND
v.kind = :queryKind AND
- v.tagFolderName NOT NULL`,
- { tagsGuid: PlacesUtils.bookmarks.tagsGuid,
- valueState: BookmarkMergeState.TYPE.REMOTE,
- queryKind: SyncedBookmarksMirror.KIND.QUERY });
+ v.tagFolderName NOT NULL AND
+ CAST(get_query_param(substr(u.url, 7), "type") AS INT) = :tagContentsType
+ `, { valueState: BookmarkMergeState.TYPE.REMOTE,
+ queryKind: SyncedBookmarksMirror.KIND.QUERY,
+ tagContentsType: Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_CONTENTS });
let urlsParams = [];
for (let row of queryRows) {
let url = new URL(row.getResultByName("url"));
let tagQueryParams = new URLSearchParams(url.pathname);
- let type = Number(tagQueryParams.get("type"));
- if (type != Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_CONTENTS) {
- continue;
- }
-
- // Rewrite the query URL to point to the new folder.
- let newTagFolderId = row.getResultByName("newTagFolderId");
- tagQueryParams.set("folder", newTagFolderId);
+
+ // Rewrite the query to directly reference the tag.
+ tagQueryParams.delete("queryType");
+ tagQueryParams.delete("type");
+ tagQueryParams.delete("folder");
+ tagQueryParams.set("tag", row.getResultByName("tagFolderName"));
let newURLHref = url.protocol + tagQueryParams;
urlsParams.push({
urlId: row.getResultByName("urlId"),
url: newURLHref,
});
}
@@ -1635,20 +1617,21 @@ class SyncedBookmarksMirror {
UNION ALL
SELECT b.id, s.level + 1 AS level FROM moz_bookmarks b
JOIN syncedItems s ON s.id = b.parent
)
INSERT INTO itemsToUpload(id, guid, syncChangeCounter, parentGuid,
parentTitle, dateAdded, type, title, isQuery,
url, tags, description, loadInSidebar,
smartBookmarkName, keyword, feedURL, siteURL,
- position)
+ position, tagFolderName)
SELECT b.id, b.guid, b.syncChangeCounter, p.guid, p.title,
b.dateAdded / 1000, b.type, b.title,
- IFNULL(SUBSTR(h.url, 1, 6) = 'place:', 0), h.url,
+ IFNULL(SUBSTR(h.url, 1, 6) = 'place:', 0) AS isQuery,
+ h.url,
(SELECT GROUP_CONCAT(t.title, ',') FROM moz_bookmarks e
JOIN moz_bookmarks t ON t.id = e.parent
JOIN moz_bookmarks r ON r.id = t.parent
WHERE b.type = :bookmarkType AND
r.guid = :tagsGuid AND
e.fk = h.id),
(SELECT a.content FROM moz_items_annos a
JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id
@@ -1669,65 +1652,35 @@ class SyncedBookmarksMirror {
WHERE b.type = :folderType AND
a.item_id = b.id AND
n.name = :feedURLAnno),
(SELECT a.content FROM moz_items_annos a
JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id
WHERE b.type = :folderType AND
a.item_id = b.id AND
n.name = :siteURLAnno),
- b.position
+ b.position,
+ (SELECT get_query_param(substr(url, 7), 'tag')
+ WHERE substr(h.url, 1, 6) = 'place:')
FROM moz_bookmarks b
JOIN moz_bookmarks p ON p.id = b.parent
JOIN syncedItems s ON s.id = b.id
LEFT JOIN moz_places h ON h.id = b.fk
LEFT JOIN itemsToWeaklyReupload w ON w.id = b.id
WHERE b.syncChangeCounter >= 1 OR
w.id NOT NULL`,
{ bookmarkType: PlacesUtils.bookmarks.TYPE_BOOKMARK,
tagsGuid: PlacesUtils.bookmarks.tagsGuid,
descriptionAnno: PlacesSyncUtils.bookmarks.DESCRIPTION_ANNO,
sidebarAnno: PlacesSyncUtils.bookmarks.SIDEBAR_ANNO,
smartBookmarkAnno: PlacesSyncUtils.bookmarks.SMART_BOOKMARKS_ANNO,
folderType: PlacesUtils.bookmarks.TYPE_FOLDER,
feedURLAnno: PlacesUtils.LMANNO_FEEDURI,
siteURLAnno: PlacesUtils.LMANNO_SITEURI });
- // Record tag folder names for tag queries. Parsing query URLs one by one
- // is inefficient, but queries aren't common today, and we can remove this
- // logic entirely once bug 1293445 lands.
- let queryRows = await this.db.execute(`
- SELECT id, url FROM itemsToUpload
- WHERE isQuery`);
-
- let tagFolderNameParams = [];
- for (let row of queryRows) {
- let url = new URL(row.getResultByName("url"));
- let tagQueryParams = new URLSearchParams(url.pathname);
- let type = Number(tagQueryParams.get("type"));
- if (type == Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_CONTENTS) {
- continue;
- }
- let tagFolderId = Number(tagQueryParams.get("folder"));
- tagFolderNameParams.push({
- id: row.getResultByName("id"),
- tagFolderId,
- folderType: PlacesUtils.bookmarks.TYPE_FOLDER,
- });
- }
-
- if (tagFolderNameParams.length) {
- await this.db.execute(`
- UPDATE itemsToUpload SET
- tagFolderName = (SELECT b.title FROM moz_bookmarks b
- WHERE b.id = :tagFolderId AND
- b.type = :folderType)
- WHERE id = :id`, tagFolderNameParams);
- }
-
// Record the child GUIDs of locally changed folders, which we use to
// populate the `children` array in the record.
await this.db.execute(`
INSERT INTO structureToUpload(guid, parentId, position)
SELECT b.guid, b.parent, b.position FROM moz_bookmarks b
JOIN itemsToUpload o ON o.id = b.parent`);
// Finally, stage tombstones for deleted items. Ignore conflicts if we have
@@ -2488,17 +2441,17 @@ async function initializeTempMirrorEntit
END`);
// A view of local bookmark tags. Tags, like keywords, are associated with
// URLs, so two bookmarks with the same URL should have the same tags. Unlike
// keywords, one tag may be associated with many different URLs. Tags are also
// different because they're implemented as bookmarks under the hood. Each tag
// is stored as a folder under the tags root, and tagged URLs are stored as
// untitled bookmarks under these folders. This complexity, along with tag
- // query rewriting, can be removed once bug 1293445 lands.
+ // query rewriting, can be removed once bug 424160 lands.
await db.execute(`
CREATE TEMP VIEW localTags(tagEntryId, tagEntryGuid, tagFolderId,
tagFolderGuid, tagEntryPosition, tagEntryType,
tag, placeId) AS
SELECT b.id, b.guid, p.id, p.guid, b.position, b.type, p.title, b.fk
FROM moz_bookmarks b
JOIN moz_bookmarks p ON p.id = b.parent
JOIN moz_bookmarks r ON r.id = p.parent
--- a/toolkit/components/places/tests/sync/test_bookmark_kinds.js
+++ b/toolkit/components/places/tests/sync/test_bookmark_kinds.js
@@ -264,53 +264,59 @@ add_task(async function test_queries() {
info("Set up places");
// create a tag and grab the local folder ID.
let tag = await PlacesUtils.bookmarks.insert({
type: PlacesUtils.bookmarks.TYPE_FOLDER,
parentGuid: PlacesUtils.bookmarks.tagsGuid,
title: "a-tag",
});
- let tagid = await PlacesUtils.promiseItemId(tag.guid);
await PlacesTestUtils.markBookmarksAsSynced();
await PlacesUtils.bookmarks.insertTree({
guid: PlacesUtils.bookmarks.menuGuid,
children: [
{
- // this entry has a folder= query param for a folder that exists.
+ // this entry has a tag= query param for a tag that exists.
guid: "queryAAAAAAA",
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
title: "TAG_QUERY query",
- url: `place:type=6&sort=14&maxResults=10&folder=${tagid}`,
+ url: `place:tag=a-tag&&sort=14&maxResults=10`,
},
{
- // this entry has a folder= query param for a folder that doesn't exist.
+ // this entry has a tag= query param for a tag that doesn't exist.
guid: "queryBBBBBBB",
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
title: "TAG_QUERY query but invalid folder id",
- url: `place:type=6&sort=14&maxResults=10&folder=12345`,
+ url: `place:tag=b-tag&sort=14&maxResults=10`,
},
{
- // this entry has no folder= query param.
+ // this entry has no tag= query param.
guid: "queryCCCCCCC",
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
title: "TAG_QUERY without a folder at all",
- url: "place:type=6&sort=14&maxResults=10",
+ url: "place:sort=14&maxResults=10",
},
-
- ],
+ {
+ // this entry has only a tag= query.
+ guid: "queryDDDDDDD",
+ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
+ title: "TAG_QUERY without a folder at all",
+ url: "place:tag=a-tag",
+ },
+ ],
});
info("Create records to upload");
let changes = await buf.apply();
Assert.strictEqual(changes.queryAAAAAAA.cleartext.folderName, tag.title);
- Assert.strictEqual(changes.queryBBBBBBB.cleartext.folderName, undefined);
+ Assert.strictEqual(changes.queryBBBBBBB.cleartext.folderName, "b-tag");
Assert.strictEqual(changes.queryCCCCCCC.cleartext.folderName, undefined);
+ Assert.strictEqual(changes.queryDDDDDDD.cleartext.folderName, tag.title);
} finally {
await PlacesUtils.bookmarks.eraseEverything();
await PlacesSyncUtils.bookmarks.reset();
}
});
// Bug 632287.
add_task(async function test_mismatched_but_compatible_folder_types() {
--- a/toolkit/components/places/tests/sync/test_sync_utils.js
+++ b/toolkit/components/places/tests/sync/test_sync_utils.js
@@ -1518,66 +1518,66 @@ add_task(async function test_insert_anno
"Should not set sidebar anno for new bookmark");
}
await PlacesUtils.bookmarks.eraseEverything();
await PlacesSyncUtils.bookmarks.reset();
});
add_task(async function test_insert_tag_query() {
- let tagFolder = -1;
-
- info("Insert tag query for new tag");
- {
- deepEqual(PlacesUtils.tagging.allTags, [], "New tag should not exist yet");
- let query = await PlacesSyncUtils.bookmarks.insert({
- kind: "query",
- recordId: makeGuid(),
- parentRecordId: "toolbar",
- url: "place:type=7&folder=90",
- folder: "taggy",
- title: "Tagged stuff",
- });
- notEqual(query.url.href, "place:type=7&folder=90",
- "Tag query URL for new tag should differ");
-
- [, tagFolder] = /\bfolder=(\d+)\b/.exec(query.url.pathname);
- ok(tagFolder > 0, "New tag query URL should contain valid folder");
- deepEqual(PlacesUtils.tagging.allTags, ["taggy"], "New tag should exist");
- }
-
- info("Insert tag query for existing tag");
- {
- let url = "place:type=7&folder=90&maxResults=15";
- let query = await PlacesSyncUtils.bookmarks.insert({
- kind: "query",
- url,
- folder: "taggy",
- title: "Sorted and tagged",
- recordId: makeGuid(),
- parentRecordId: "menu",
- });
- notEqual(query.url.href, url, "Tag query URL for existing tag should differ");
- let params = new URLSearchParams(query.url.pathname);
- equal(params.get("type"), "7", "Should preserve query type");
- equal(params.get("maxResults"), "15", "Should preserve additional params");
- equal(params.get("folder"), tagFolder, "Should update tag folder");
- deepEqual(PlacesUtils.tagging.allTags, ["taggy"], "Should not duplicate existing tags");
- }
-
info("Use the public tagging API to ensure we added the tag correctly");
await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.menuGuid,
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
url: "https://mozilla.org",
title: "Mozilla",
});
PlacesUtils.tagging.tagURI(uri("https://mozilla.org"), ["taggy"]);
assertURLHasTags("https://mozilla.org/", ["taggy"],
- "Should set tags using the tagging API");
+ "Should set tags using the tagging API");
+
+ info("Insert tag query for non existing tag");
+ {
+ let query = await PlacesSyncUtils.bookmarks.insert({
+ kind: "query",
+ recordId: makeGuid(),
+ parentRecordId: "toolbar",
+ url: "place:type=7&folder=90",
+ folder: "nonexisting",
+ title: "Tagged stuff",
+ });
+ let params = new URLSearchParams(query.url.pathname);
+ ok(!params.has("type"), "Should not preserve query type");
+ ok(!params.has("folder"), "Should not preserve folder");
+ equal(params.get("tag"), "nonexisting", "Should add tag");
+ deepEqual(PlacesUtils.tagging.allTags, ["taggy"],
+ "The nonexisting tag should not be added");
+ }
+
+ info("Insert tag query for existing tag");
+ {
+ let url = "place:type=7&folder=90&maxResults=15";
+ let query = await PlacesSyncUtils.bookmarks.insert({
+ kind: "query",
+ recordId: makeGuid(),
+ parentRecordId: "menu",
+ url,
+ folder: "taggy",
+ title: "Sorted and tagged",
+ });
+ let params = new URLSearchParams(query.url.pathname);
+ ok(!params.get("type"), "Should not preserve query type");
+ ok(!params.has("folder"), "Should not preserve folder");
+ equal(params.get("maxResults"), "15", "Should preserve additional params");
+ equal(params.get("tag"), "taggy", "Should add tag");
+ deepEqual(PlacesUtils.tagging.allTags, ["taggy"],
+ "Should not duplicate existing tags");
+ }
+
+
info("Removing the tag should clean up the tag folder");
PlacesUtils.tagging.untagURI(uri("https://mozilla.org"), null);
deepEqual(PlacesUtils.tagging.allTags, [],
"Should remove tag folder once last item is untagged");
await PlacesUtils.bookmarks.eraseEverything();
await PlacesSyncUtils.bookmarks.reset();
@@ -1830,21 +1830,20 @@ add_task(async function test_fetch() {
recordId: makeGuid(),
parentRecordId: folder.recordId,
kind: "separator",
});
let tagQuery = await PlacesSyncUtils.bookmarks.insert({
kind: "query",
recordId: makeGuid(),
parentRecordId: "toolbar",
- url: "place:type=7&folder=90",
+ url: "place:tag=taggy",
folder: "taggy",
title: "Tagged stuff",
});
- let [, tagFolderId] = /\bfolder=(\d+)\b/.exec(tagQuery.url.pathname);
let smartBmk = await PlacesSyncUtils.bookmarks.insert({
kind: "query",
recordId: makeGuid(),
parentRecordId: "toolbar",
url: "place:folder=TOOLBAR",
query: "BookmarksToolbar",
title: "Bookmarks toolbar query",
});
@@ -1900,17 +1899,17 @@ add_task(async function test_fetch() {
}
info("Fetch tag query");
{
let item = await PlacesSyncUtils.bookmarks.fetch(tagQuery.recordId);
deepEqual(Object.keys(item).sort(), ["recordId", "kind", "parentRecordId",
"url", "title", "folder", "parentTitle", "dateAdded"].sort(),
"Should include query-specific properties");
- equal(item.url.href, `place:type=7&folder=${tagFolderId}`, "Should not rewrite outgoing tag queries");
+ equal(item.url.href, `place:tag=taggy`, "Should not rewrite outgoing tag queries");
equal(item.folder, "taggy", "Should return tag name for tag queries");
}
info("Fetch smart bookmark");
{
let item = await PlacesSyncUtils.bookmarks.fetch(smartBmk.recordId);
deepEqual(Object.keys(item).sort(), ["recordId", "kind", "parentRecordId",
"url", "title", "query", "parentTitle", "dateAdded"].sort(),