--- a/browser/components/places/PlacesUIUtils.jsm
+++ b/browser/components/places/PlacesUIUtils.jsm
@@ -1128,25 +1128,25 @@ this.PlacesUIUtils = {
// This is the list of the left pane queries.
let queries = {
"PlacesRoot": { title: "" },
"History": { title: this.getString("OrganizerQueryHistory") },
"Downloads": { title: this.getString("OrganizerQueryDownloads") },
"Tags": { title: this.getString("OrganizerQueryTags") },
"AllBookmarks": { title: this.getString("OrganizerQueryAllBookmarks") },
"BookmarksToolbar":
- { title: null,
+ { title: "",
concreteTitle: PlacesUtils.getString("BookmarksToolbarFolderTitle"),
concreteId: PlacesUtils.toolbarFolderId },
"BookmarksMenu":
- { title: null,
+ { title: "",
concreteTitle: PlacesUtils.getString("BookmarksMenuFolderTitle"),
concreteId: PlacesUtils.bookmarksMenuFolderId },
"UnfiledBookmarks":
- { title: null,
+ { title: "",
concreteTitle: PlacesUtils.getString("OtherBookmarksFolderTitle"),
concreteId: PlacesUtils.unfiledBookmarksFolderId },
};
// All queries but PlacesRoot.
const EXPECTED_QUERY_COUNT = 7;
// Removes an item and associated annotations, ignoring eventual errors.
function safeRemoveItem(aItemId) {
--- a/browser/components/places/tests/browser/browser_bookmarkProperties_readOnlyRoot.js
+++ b/browser/components/places/tests/browser/browser_bookmarkProperties_readOnlyRoot.js
@@ -29,14 +29,14 @@ add_task(async function() {
"Node title is correct");
// Blur the field and ensure root's name has not been changed.
namepicker.blur();
is(namepicker.value,
PlacesUtils.bookmarks.getItemTitle(PlacesUtils.unfiledBookmarksFolderId),
"Root title is correct");
// Check the shortcut's title.
let bookmark = await PlacesUtils.bookmarks.fetch(tree.selectedNode.bookmarkGuid);
- is(bookmark.title, null,
+ is(bookmark.title, "",
"Shortcut title is null");
}
);
});
});
--- a/services/sync/tests/unit/test_bookmark_store.js
+++ b/services/sync/tests/unit/test_bookmark_store.js
@@ -98,17 +98,17 @@ add_test(function test_bookmark_create()
store.applyIncoming(tbrecord);
_("Verify it has been created correctly.");
id = store.idForGUID(tbrecord.id);
do_check_eq(store.GUIDForId(id), tbrecord.id);
do_check_eq(PlacesUtils.bookmarks.getItemType(id),
PlacesUtils.bookmarks.TYPE_BOOKMARK);
do_check_true(PlacesUtils.bookmarks.getBookmarkURI(id).equals(tburi));
- do_check_eq(PlacesUtils.bookmarks.getItemTitle(id), null);
+ do_check_eq(PlacesUtils.bookmarks.getItemTitle(id), "");
let error;
try {
PlacesUtils.annotations.getItemAnnotation(id, "bookmarkProperties/description");
} catch (ex) {
error = ex;
}
do_check_eq(error.result, Cr.NS_ERROR_NOT_AVAILABLE);
do_check_eq(PlacesUtils.bookmarks.getFolderIdForItem(id),
@@ -142,17 +142,17 @@ add_test(function test_bookmark_update()
record.tags = null;
store.applyIncoming(record);
_("Verify that the values have been cleared.");
do_check_throws(function() {
PlacesUtils.annotations.getItemAnnotation(
bmk1_id, "bookmarkProperties/description");
}, Cr.NS_ERROR_NOT_AVAILABLE);
- do_check_eq(PlacesUtils.bookmarks.getItemTitle(bmk1_id), null);
+ do_check_eq(PlacesUtils.bookmarks.getItemTitle(bmk1_id), "");
do_check_eq(PlacesUtils.bookmarks.getKeywordForBookmark(bmk1_id), null);
} finally {
_("Clean up.");
store.wipe();
run_next_test();
}
});
--- a/toolkit/components/places/Bookmarks.jsm
+++ b/toolkit/components/places/Bookmarks.jsm
@@ -30,18 +30,17 @@
* The time at which the item was last modified.
* - type (number)
* The item's type, either TYPE_BOOKMARK, TYPE_FOLDER or TYPE_SEPARATOR.
*
* The following properties are only valid for URLs or folders.
*
* - title (string)
* The item's title, if any. Empty titles and null titles are considered
- * the same, and the property is unset on retrieval in such a case.
- * Titles longer than DB_TITLE_LENGTH_MAX will be truncated.
+ * the same. Titles longer than DB_TITLE_LENGTH_MAX will be truncated.
*
* The following properties are only valid for URLs:
*
* - url (URL, href or nsIURI)
* The item's URL. Note that while input objects can contains either
* an URL object, an href string, or an nsIURI, output objects will always
* contain an URL object.
* An URL cannot be longer than DB_URL_LENGTH_MAX, methods will throw if a
@@ -186,18 +185,20 @@ var Bookmarks = Object.freeze({
modTime = now;
}
let insertInfo = validateBookmarkObject(info,
{ type: { defaultValue: this.TYPE_BOOKMARK },
index: { defaultValue: this.DEFAULT_INDEX },
url: { requiredIf: b => b.type == this.TYPE_BOOKMARK,
validIf: b => b.type == this.TYPE_BOOKMARK },
parentGuid: { required: true },
- title: { validIf: b => [ this.TYPE_BOOKMARK,
- this.TYPE_FOLDER ].includes(b.type) },
+ title: { defaultValue: "",
+ validIf: b => b.type == this.TYPE_BOOKMARK ||
+ b.type == this.TYPE_FOLDER ||
+ b.title === "" },
dateAdded: { defaultValue: addedTime },
lastModified: { defaultValue: modTime,
validIf: b => b.lastModified >= now || (b.dateAdded && b.lastModified >= b.dateAdded) },
source: { defaultValue: this.SOURCES.DEFAULT }
});
return (async () => {
// Ensure the parent exists.
@@ -219,17 +220,17 @@ var Bookmarks = Object.freeze({
// complete we may stop using it.
let uri = item.hasOwnProperty("url") ? PlacesUtils.toURI(item.url) : null;
let itemId = await PlacesUtils.promiseItemId(item.guid);
// Pass tagging information for the observers to skip over these notifications when needed.
let isTagging = parent._parentId == PlacesUtils.tagsFolderId;
let isTagsFolder = parent._id == PlacesUtils.tagsFolderId;
notify(observers, "onItemAdded", [ itemId, parent._id, item.index,
- item.type, uri, item.title || null,
+ item.type, uri, item.title,
PlacesUtils.toPRTime(item.dateAdded), item.guid,
item.parentGuid, item.source ],
{ isTagging: isTagging || isTagsFolder });
// If it's a tag, notify OnItemChanged to all bookmarks for this URL.
if (isTagging) {
for (let entry of (await fetchBookmarksByURL(item))) {
notify(observers, "onItemChanged", [ entry._id, "tags", false, "",
@@ -346,18 +347,20 @@ var Bookmarks = Object.freeze({
// Ensure to use the same date for dateAdded and lastModified, even if
// dateAdded may be imposed by the caller.
let time = (info && info.dateAdded) || fallbackLastAdded;
let insertInfo = validateBookmarkObject(info, {
type: { defaultValue: TYPE_BOOKMARK },
url: { requiredIf: b => b.type == TYPE_BOOKMARK,
validIf: b => b.type == TYPE_BOOKMARK },
parentGuid: { required: true },
- title: { validIf: b => [ TYPE_BOOKMARK,
- TYPE_FOLDER ].includes(b.type) },
+ title: { defaultValue: "",
+ validIf: b => b.type == TYPE_BOOKMARK ||
+ b.type == TYPE_FOLDER ||
+ b.title === "" },
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) },
index: { replaceWith: indexToUse++ },
source: { replaceWith: source },
@@ -465,17 +468,17 @@ var Bookmarks = Object.freeze({
parentId = parent._id;
} else {
// This is a parent folder that's been updated, so we need to
// use the new item id.
parentId = itemIdMap.get(item.parentGuid);
}
notify(observers, "onItemAdded", [ itemId, parentId, item.index,
- item.type, uri, item.title || null,
+ item.type, uri, item.title,
PlacesUtils.toPRTime(item.dateAdded), item.guid,
item.parentGuid, item.source ],
{ isTagging: false });
// Remove non-enumerable properties.
delete item.source;
// Note, annotations for livemark data are deleted from insertInfo
// within appendInsertionInfoForInfoArray, so we won't be duplicating
@@ -1149,17 +1152,18 @@ function notify(observers, notification,
function updateBookmark(info, item, newParent) {
return PlacesUtils.withConnectionWrapper("Bookmarks.jsm: updateBookmark",
async function(db) {
let tuples = new Map();
tuples.set("lastModified", { value: PlacesUtils.toPRTime(info.lastModified) });
if (info.hasOwnProperty("title"))
- tuples.set("title", { value: info.title });
+ tuples.set("title", { value: info.title,
+ fragment: `title = NULLIF(:title, "")` });
if (info.hasOwnProperty("dateAdded"))
tuples.set("dateAdded", { value: PlacesUtils.toPRTime(info.dateAdded) });
await db.executeTransaction(async function() {
let isTagging = item._grandParentId == PlacesUtils.tagsFolderId;
let syncChangeDelta =
PlacesSyncUtils.bookmarks.determineSyncChangeDelta(info.source);
@@ -1289,20 +1293,16 @@ function updateBookmark(info, item, newP
Object.defineProperty(additionalParentInfo, "_parentId",
{ value: newParent._id, enumerable: false });
Object.defineProperty(additionalParentInfo, "_grandParentId",
{ value: newParent._parentId, enumerable: false });
}
let updatedItem = mergeIntoNewObject(item, info, additionalParentInfo);
- // Don't return an empty title to the caller.
- if (updatedItem.hasOwnProperty("title") && updatedItem.title === null)
- delete updatedItem.title;
-
return updatedItem;
});
}
// Insert implementation.
function insertBookmark(item, parent) {
return PlacesUtils.withConnectionWrapper("Bookmarks.jsm: insertBookmark",
@@ -1336,18 +1336,18 @@ function insertBookmark(item, parent) {
PlacesSyncUtils.bookmarks.determineInitialSyncStatus(item.source);
// Insert the bookmark into the database.
await db.executeCached(
`INSERT INTO moz_bookmarks (fk, type, parent, position, title,
dateAdded, lastModified, guid,
syncChangeCounter, syncStatus)
VALUES (CASE WHEN :url ISNULL THEN NULL ELSE (SELECT id FROM moz_places WHERE url_hash = hash(:url) AND url = :url) END,
- :type, :parent, :index, :title, :date_added, :last_modified,
- :guid, :syncChangeCounter, :syncStatus)
+ :type, :parent, :index, NULLIF(:title, ""), :date_added,
+ :last_modified, :guid, :syncChangeCounter, :syncStatus)
`, { url: item.hasOwnProperty("url") ? item.url.href : null,
type: item.type, parent: parent._id, index: item.index,
title: item.title, date_added: PlacesUtils.toPRTime(item.dateAdded),
last_modified: PlacesUtils.toPRTime(item.lastModified), guid: item.guid,
syncChangeCounter: syncChangeDelta, syncStatus });
// Mark all affected separators as changed
await adjustSeparatorsSyncCounter(db, parent._id, item.index + 1, syncChangeDelta);
@@ -1371,20 +1371,16 @@ function insertBookmark(item, parent) {
});
// If not a tag recalculate frecency...
if (item.type == Bookmarks.TYPE_BOOKMARK && !isTagging) {
// ...though we don't wait for the calculation.
updateFrecency(db, [item.url]).catch(Cu.reportError);
}
- // Don't return an empty title to the caller.
- if (item.hasOwnProperty("title") && item.title === null)
- delete item.title;
-
return item;
});
}
/**
* Determines if a bookmark is a Livemark depending on how it is annotated.
*
* @param {Object} node The bookmark node to check.
@@ -1415,17 +1411,17 @@ function insertBookmarkTree(items, sourc
}));
await db.executeCached(
`INSERT INTO moz_bookmarks (fk, type, parent, position, title,
dateAdded, lastModified, guid,
syncChangeCounter, syncStatus)
VALUES (CASE WHEN :url ISNULL THEN NULL ELSE (SELECT id FROM moz_places WHERE url_hash = hash(:url) AND url = :url) END, :type,
(SELECT id FROM moz_bookmarks WHERE guid = :parentGuid),
IFNULL(:index, (SELECT count(*) FROM moz_bookmarks WHERE parent = :rootId)),
- :title, :date_added, :last_modified, :guid,
+ NULLIF(:title, ""), :date_added, :last_modified, :guid,
:syncChangeCounter, :syncStatus)`, items);
await setAncestorsLastModified(db, parent.guid, lastAddedForParent,
syncChangeDelta);
});
// We don't wait for the frecency calculation.
updateFrecency(db, urls, true).catch(Cu.reportError);
@@ -1561,18 +1557,18 @@ async function queryBookmarks(info) {
return PlacesUtils.withConnectionWrapper("Bookmarks.jsm: queryBookmarks",
async function(db) {
// _id, _childCount, _grandParentId and _parentId fields
// are required to be in the result by the converting function
// hence setting them to NULL
let rows = await db.executeCached(
`SELECT b.guid, IFNULL(p.guid, "") AS parentGuid, b.position AS 'index',
- b.dateAdded, b.lastModified, b.type, b.title,
- h.url AS url, b.parent, p.parent,
+ b.dateAdded, b.lastModified, b.type,
+ IFNULL(b.title, "") AS title, h.url AS url, b.parent, p.parent,
NULL AS _id,
NULL AS _childCount,
NULL AS _grandParentId,
NULL AS _parentId,
NULL AS _syncStatus
FROM moz_bookmarks b
LEFT JOIN moz_bookmarks p ON p.id = b.parent
LEFT JOIN moz_places h ON h.id = b.fk
@@ -1586,18 +1582,18 @@ async function queryBookmarks(info) {
// Fetch implementation.
async function fetchBookmark(info, concurrent) {
let query = async function(db) {
let rows = await db.executeCached(
`SELECT b.guid, IFNULL(p.guid, "") AS parentGuid, b.position AS 'index',
- b.dateAdded, b.lastModified, b.type, b.title, h.url AS url,
- b.id AS _id, b.parent AS _parentId,
+ b.dateAdded, b.lastModified, b.type, IFNULL(b.title, "") AS title,
+ h.url AS url, b.id AS _id, b.parent AS _parentId,
(SELECT count(*) FROM moz_bookmarks WHERE parent = b.id) AS _childCount,
p.parent AS _grandParentId, b.syncStatus AS _syncStatus
FROM moz_bookmarks b
LEFT JOIN moz_bookmarks p ON p.id = b.parent
LEFT JOIN moz_places h ON h.id = b.fk
WHERE b.guid = :guid
`, { guid: info.guid });
@@ -1611,18 +1607,18 @@ async function fetchBookmark(info, concu
query);
}
async function fetchBookmarkByPosition(info, concurrent) {
let query = async function(db) {
let index = info.index == Bookmarks.DEFAULT_INDEX ? null : info.index;
let rows = await db.executeCached(
`SELECT b.guid, IFNULL(p.guid, "") AS parentGuid, b.position AS 'index',
- b.dateAdded, b.lastModified, b.type, b.title, h.url AS url,
- b.id AS _id, b.parent AS _parentId,
+ b.dateAdded, b.lastModified, b.type, IFNULL(b.title, "") AS title,
+ h.url AS url, b.id AS _id, b.parent AS _parentId,
(SELECT count(*) FROM moz_bookmarks WHERE parent = b.id) AS _childCount,
p.parent AS _grandParentId, b.syncStatus AS _syncStatus
FROM moz_bookmarks b
LEFT JOIN moz_bookmarks p ON p.id = b.parent
LEFT JOIN moz_places h ON h.id = b.fk
WHERE p.guid = :parentGuid
AND b.position = IFNULL(:index, (SELECT count(*) - 1
FROM moz_bookmarks
@@ -1640,18 +1636,18 @@ async function fetchBookmarkByPosition(i
}
async function fetchBookmarksByURL(info, concurrent) {
let query = async function(db) {
let tagsFolderId = await promiseTagsFolderId();
let rows = await db.executeCached(
`/* do not warn (bug no): not worth to add an index */
SELECT b.guid, IFNULL(p.guid, "") AS parentGuid, b.position AS 'index',
- b.dateAdded, b.lastModified, b.type, b.title, h.url AS url,
- b.id AS _id, b.parent AS _parentId,
+ b.dateAdded, b.lastModified, b.type, IFNULL(b.title, "") AS title,
+ h.url AS url, b.id AS _id, b.parent AS _parentId,
NULL AS _childCount, /* Unused for now */
p.parent AS _grandParentId, b.syncStatus AS _syncStatus
FROM moz_bookmarks b
JOIN moz_bookmarks p ON p.id = b.parent
JOIN moz_places h ON h.id = b.fk
WHERE h.url_hash = hash(:url) AND h.url = :url
AND _grandParentId <> :tagsFolderId
ORDER BY b.lastModified DESC
@@ -1669,18 +1665,19 @@ async function fetchBookmarksByURL(info,
}
function fetchRecentBookmarks(numberOfItems) {
return PlacesUtils.withConnectionWrapper("Bookmarks.jsm: fetchRecentBookmarks",
async function(db) {
let tagsFolderId = await promiseTagsFolderId();
let rows = await db.executeCached(
`SELECT b.guid, IFNULL(p.guid, "") AS parentGuid, b.position AS 'index',
- b.dateAdded, b.lastModified, b.type, b.title, h.url AS url,
- NULL AS _id, NULL AS _parentId, NULL AS _childCount, NULL AS _grandParentId,
+ b.dateAdded, b.lastModified, b.type,
+ IFNULL(b.title, "") AS title, h.url AS url, NULL AS _id,
+ NULL AS _parentId, NULL AS _childCount, NULL AS _grandParentId,
NULL AS _syncStatus
FROM moz_bookmarks b
JOIN moz_bookmarks p ON p.id = b.parent
JOIN moz_places h ON h.id = b.fk
WHERE p.parent <> :tagsFolderId
AND b.type = :type
AND url_hash NOT BETWEEN hash("place", "prefix_lo")
AND hash("place", "prefix_hi")
@@ -1698,18 +1695,18 @@ function fetchRecentBookmarks(numberOfIt
}
function fetchBookmarksByParent(info) {
return PlacesUtils.withConnectionWrapper("Bookmarks.jsm: fetchBookmarksByParent",
async function(db) {
let rows = await db.executeCached(
`SELECT b.guid, IFNULL(p.guid, "") AS parentGuid, b.position AS 'index',
- b.dateAdded, b.lastModified, b.type, b.title, h.url AS url,
- b.id AS _id, b.parent AS _parentId,
+ b.dateAdded, b.lastModified, b.type, IFNULL(b.title, "") AS title,
+ h.url AS url, b.id AS _id, b.parent AS _parentId,
(SELECT count(*) FROM moz_bookmarks WHERE parent = b.id) AS _childCount,
p.parent AS _grandParentId, b.syncStatus AS _syncStatus
FROM moz_bookmarks b
LEFT JOIN moz_bookmarks p ON p.id = b.parent
LEFT JOIN moz_places h ON h.id = b.fk
WHERE p.guid = :parentGuid
ORDER BY b.position ASC
`, { parentGuid: info.parentGuid });
@@ -1983,29 +1980,33 @@ function removeSameValueProperties(dest,
*
* @param rows
* the array of mozIStorageRow objects.
* @return an array of bookmark objects.
*/
function rowsToItemsArray(rows) {
return rows.map(row => {
let item = {};
- for (let prop of ["guid", "index", "type"]) {
+ for (let prop of ["guid", "index", "type", "title"]) {
item[prop] = row.getResultByName(prop);
}
for (let prop of ["dateAdded", "lastModified"]) {
let value = row.getResultByName(prop);
if (value)
item[prop] = PlacesUtils.toDate(value);
}
- for (let prop of ["title", "parentGuid", "url" ]) {
- let val = row.getResultByName(prop);
- if (val)
- item[prop] = prop === "url" ? new URL(val) : val;
+ let parentGuid = row.getResultByName("parentGuid");
+ if (parentGuid) {
+ item.parentGuid = parentGuid;
}
+ let url = row.getResultByName("url");
+ if (url) {
+ item.url = new URL(url);
+ }
+
for (let prop of ["_id", "_parentId", "_childCount", "_grandParentId",
"_syncStatus"]) {
let val = row.getResultByName(prop);
if (val !== null) {
// These properties should not be returned to the API consumer, thus
// they are non-enumerable and removed through Object.assign just before
// the object is returned.
// Configurable is set to support mergeIntoNewObject overwrites.
@@ -2163,18 +2164,19 @@ async function(db, folderGuids, options)
JOIN moz_bookmarks p ON b.parent = p.id
WHERE p.guid = :folderGuid
UNION ALL
SELECT id FROM moz_bookmarks
JOIN descendants ON parent = did
)
SELECT b.id AS _id, b.parent AS _parentId, b.position AS 'index',
b.type, url, b.guid, p.guid AS parentGuid, b.dateAdded,
- b.lastModified, b.title, p.parent AS _grandParentId,
- NULL AS _childCount, b.syncStatus AS _syncStatus
+ b.lastModified, IFNULL(b.title, "") AS title,
+ p.parent AS _grandParentId, NULL AS _childCount,
+ b.syncStatus AS _syncStatus
FROM descendants
JOIN moz_bookmarks b ON did = b.id
JOIN moz_bookmarks p ON p.id = b.parent
LEFT JOIN moz_places h ON b.fk = h.id`, { folderGuid });
itemsRemoved = itemsRemoved.concat(rowsToItemsArray(rows));
await db.executeCached(
--- a/toolkit/components/places/Helpers.cpp
+++ b/toolkit/components/places/Helpers.cpp
@@ -287,16 +287,19 @@ IsValidGUID(const nsACString& aGUID)
return false;
}
return true;
}
void
TruncateTitle(const nsACString& aTitle, nsACString& aTrimmed)
{
+ if (aTitle.IsVoid()) {
+ return;
+ }
aTrimmed = aTitle;
if (aTitle.Length() > TITLE_LENGTH_MAX) {
aTrimmed = StringHead(aTitle, TITLE_LENGTH_MAX);
}
}
PRTime
RoundToMilliseconds(PRTime aTime) {
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -225,20 +225,23 @@ const BOOKMARK_VALIDATORS = Object.freez
v >= PlacesUtils.bookmarks.DEFAULT_INDEX),
dateAdded: simpleValidateFunc(v => v.constructor.name == "Date"),
lastModified: simpleValidateFunc(v => v.constructor.name == "Date"),
type: simpleValidateFunc(v => Number.isInteger(v) &&
[ PlacesUtils.bookmarks.TYPE_BOOKMARK,
PlacesUtils.bookmarks.TYPE_FOLDER,
PlacesUtils.bookmarks.TYPE_SEPARATOR ].includes(v)),
title: v => {
- simpleValidateFunc(val => val === null || typeof(val) == "string").call(this, v);
- if (!v)
- return null;
- return v.slice(0, DB_TITLE_LENGTH_MAX);
+ if (v === null) {
+ return "";
+ }
+ if (typeof(v) == "string") {
+ return v.slice(0, DB_TITLE_LENGTH_MAX);
+ }
+ throw new Error("Invalid title");
},
url: v => {
simpleValidateFunc(val => (typeof(val) == "string" && val.length <= DB_URL_LENGTH_MAX) ||
(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);
@@ -1946,18 +1949,18 @@ this.PlacesUtils = {
FROM moz_bookmarks b1 WHERE b1.guid=:item_guid
UNION ALL
SELECT b2.fk, level + 1, b2.type, b2.id, b2.guid, b2.parent,
descendants.guid, b2.position, b2.title, b2.dateAdded,
b2.lastModified
FROM moz_bookmarks b2
JOIN descendants ON b2.parent = descendants.id AND b2.id <> :tags_folder)
SELECT d.level, d.id, d.guid, d.parent, d.parentGuid, d.type,
- d.position AS [index], d.title, d.dateAdded, d.lastModified,
- h.url, (SELECT icon_url FROM moz_icons i
+ d.position AS [index], IFNULL(d.title, "") AS title, d.dateAdded,
+ d.lastModified, h.url, (SELECT icon_url FROM moz_icons i
JOIN moz_icons_to_pages ON icon_id = i.id
JOIN moz_pages_w_icons pi ON page_id = pi.id
WHERE pi.page_url_hash = hash(h.url) AND pi.page_url = h.url
ORDER BY width DESC LIMIT 1) AS iconuri,
(SELECT GROUP_CONCAT(t.title, ',')
FROM moz_bookmarks b2
JOIN moz_bookmarks t ON t.id = +b2.parent AND t.parent = :tags_folder
WHERE b2.fk = h.id
--- a/toolkit/components/places/nsNavBookmarks.cpp
+++ b/toolkit/components/places/nsNavBookmarks.cpp
@@ -466,18 +466,17 @@ nsNavBookmarks::InsertBookmarkInDB(int64
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"), aItemType);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aParentId);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), aIndex);
NS_ENSURE_SUCCESS(rv, rv);
- // Support NULL titles.
- if (aTitle.IsVoid())
+ if (aTitle.IsEmpty())
rv = stmt->BindNullByName(NS_LITERAL_CSTRING("item_title"));
else
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"), aTitle);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("date_added"), aDateAdded);
NS_ENSURE_SUCCESS(rv, rv);
@@ -550,20 +549,17 @@ nsNavBookmarks::InsertBookmarkInDB(int64
// Mark all affected separators as changed
rv = AdjustSeparatorsSyncCounter(aParentId, aIndex + 1, syncChangeDelta);
NS_ENSURE_SUCCESS(rv, rv);
// Add a cache entry since we know everything about this bookmark.
BookmarkData bookmark;
bookmark.id = *_itemId;
bookmark.guid.Assign(_guid);
- if (aTitle.IsVoid()) {
- bookmark.title.SetIsVoid(true);
- }
- else {
+ if (!aTitle.IsEmpty()) {
bookmark.title.Assign(aTitle);
}
bookmark.position = aIndex;
bookmark.placeId = aPlaceId;
bookmark.parentId = aParentId;
bookmark.type = aItemType;
bookmark.dateAdded = aDateAdded;
if (aLastModified)
@@ -932,31 +928,30 @@ nsNavBookmarks::InsertSeparator(int64_t
else {
index = aIndex;
// Create space for the insertion.
rv = AdjustIndices(aParent, index, INT32_MAX, 1);
NS_ENSURE_SUCCESS(rv, rv);
}
*aNewItemId = -1;
- // Set a NULL title rather than an empty string.
nsAutoCString guid(aGUID);
PRTime dateAdded = RoundedPRNow();
- rv = InsertBookmarkInDB(-1, SEPARATOR, aParent, index, NullCString(), dateAdded,
+ rv = InsertBookmarkInDB(-1, SEPARATOR, aParent, index, EmptyCString(), dateAdded,
0, folderGuid, grandParentId, nullptr, aSource,
aNewItemId, guid);
NS_ENSURE_SUCCESS(rv, rv);
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
NOTIFY_BOOKMARKS_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
DontSkip,
OnItemAdded(*aNewItemId, aParent, index, TYPE_SEPARATOR,
- nullptr, NullCString(), dateAdded, guid,
+ nullptr, EmptyCString(), dateAdded, guid,
folderGuid, aSource));
return NS_OK;
}
nsresult
nsNavBookmarks::GetLastChildId(int64_t aFolderId, int64_t* aItemId)
@@ -1527,23 +1522,21 @@ nsNavBookmarks::FetchItemInfo(int64_t aI
NS_ENSURE_SUCCESS(rv, rv);
if (!hasResult) {
return NS_ERROR_INVALID_ARG;
}
_bookmark.id = aItemId;
rv = stmt->GetUTF8String(1, _bookmark.url);
NS_ENSURE_SUCCESS(rv, rv);
+
bool isNull;
rv = stmt->GetIsNull(2, &isNull);
NS_ENSURE_SUCCESS(rv, rv);
- if (isNull) {
- _bookmark.title.SetIsVoid(true);
- }
- else {
+ if (!isNull) {
rv = stmt->GetUTF8String(2, _bookmark.title);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = stmt->GetInt32(3, &_bookmark.position);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->GetInt64(4, &_bookmark.placeId);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->GetInt64(5, &_bookmark.parentId);
@@ -1976,17 +1969,17 @@ nsNavBookmarks::SetItemTitle(int64_t aIt
TruncateTitle(aTitle, title);
if (isChangingTagFolder) {
// If we're changing the title of a tag folder, bump the change counter
// for all tagged bookmarks. We use a separate code path to avoid a
// transaction for non-tags.
mozStorageTransaction transaction(mDB->MainConn(), false);
- rv = SetItemTitleInternal(bookmark, aTitle, syncChangeDelta);
+ rv = SetItemTitleInternal(bookmark, title, syncChangeDelta);
NS_ENSURE_SUCCESS(rv, rv);
rv = AddSyncChangesForBookmarksInFolder(bookmark.id, syncChangeDelta);
NS_ENSURE_SUCCESS(rv, rv);
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
} else {
@@ -2020,19 +2013,18 @@ nsNavBookmarks::SetItemTitleInternal(Boo
"UPDATE moz_bookmarks SET "
"title = :item_title, lastModified = :date, "
"syncChangeCounter = syncChangeCounter + :delta "
"WHERE id = :item_id"
);
NS_ENSURE_STATE(statement);
mozStorageStatementScoper scoper(statement);
- // Support setting a null title, we support this in insertBookmark.
nsresult rv;
- if (aTitle.IsVoid()) {
+ if (aTitle.IsEmpty()) {
rv = statement->BindNullByName(NS_LITERAL_CSTRING("item_title"));
}
else {
rv = statement->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"),
aTitle);
}
NS_ENSURE_SUCCESS(rv, rv);
aBookmark.lastModified = RoundToMilliseconds(RoundedPRNow());
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -3784,18 +3784,23 @@ nsNavHistory::RowToResult(mozIStorageVal
// URL
nsAutoCString url;
nsresult rv = aRow->GetUTF8String(kGetInfoIndex_URL, url);
NS_ENSURE_SUCCESS(rv, rv);
// title
nsAutoCString title;
- rv = aRow->GetUTF8String(kGetInfoIndex_Title, title);
+ bool isNull;
+ rv = aRow->GetIsNull(kGetInfoIndex_Title, &isNull);
NS_ENSURE_SUCCESS(rv, rv);
+ if (!isNull) {
+ rv = aRow->GetUTF8String(kGetInfoIndex_Title, title);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
uint32_t accessCount = aRow->AsInt32(kGetInfoIndex_VisitCount);
PRTime time = aRow->AsInt64(kGetInfoIndex_VisitDate);
// itemId
int64_t itemId = aRow->AsInt64(kGetInfoIndex_ItemId);
int64_t parentId = -1;
if (itemId == 0) {
@@ -3960,19 +3965,19 @@ nsNavHistory::QueryRowToResult(int64_t i
// At this point the node is set up like a regular folder node. Here
// we make the necessary change to make it a folder shortcut.
resultNode->GetAsFolder()->mTargetFolderItemId = targetFolderId;
resultNode->mItemId = itemId;
nsAutoCString targetFolderGuid(resultNode->GetAsFolder()->mBookmarkGuid);
resultNode->mBookmarkGuid = aBookmarkGuid;
resultNode->GetAsFolder()->mTargetFolderGuid = targetFolderGuid;
- // Use the query item title, unless it's void (in that case use the
+ // Use the query item title, unless it's empty (in that case use the
// concrete folder title).
- if (!aTitle.IsVoid()) {
+ if (!aTitle.IsEmpty()) {
resultNode->mTitle = aTitle;
}
}
}
else {
// This is a regular query.
resultNode = new nsNavHistoryQueryResultNode(aTitle, aTime, queries, options);
resultNode->mItemId = itemId;
--- a/toolkit/components/places/tests/bookmarks/test_bookmarks_fetch.js
+++ b/toolkit/components/places/tests/bookmarks/test_bookmarks_fetch.js
@@ -117,17 +117,17 @@ add_task(async function fetch_bookmar_em
title: "" });
checkBookmarkObject(bm1);
let bm2 = await PlacesUtils.bookmarks.fetch(bm1.guid);
checkBookmarkObject(bm2);
Assert.deepEqual(bm1, bm2);
Assert.equal(bm2.index, 0);
- Assert.ok(!("title" in bm2));
+ Assert.strictEqual(bm2.title, "");
await PlacesUtils.bookmarks.remove(bm1.guid);
});
add_task(async function fetch_folder() {
let bm1 = await PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
type: PlacesUtils.bookmarks.TYPE_FOLDER,
title: "a folder" });
@@ -153,17 +153,17 @@ add_task(async function fetch_folder_emp
title: "" });
checkBookmarkObject(bm1);
let bm2 = await PlacesUtils.bookmarks.fetch(bm1.guid);
checkBookmarkObject(bm2);
Assert.deepEqual(bm1, bm2);
Assert.equal(bm2.index, 0);
- Assert.ok(!("title" in bm2));
+ Assert.strictEqual(bm2.title, "");
await PlacesUtils.bookmarks.remove(bm1.guid);
});
add_task(async function fetch_separator() {
let bm1 = await PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
type: PlacesUtils.bookmarks.TYPE_SEPARATOR });
checkBookmarkObject(bm1);
@@ -172,17 +172,17 @@ add_task(async function fetch_separator(
checkBookmarkObject(bm2);
Assert.deepEqual(bm1, bm2);
Assert.equal(bm2.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
Assert.equal(bm2.index, 0);
Assert.deepEqual(bm2.dateAdded, bm2.lastModified);
Assert.equal(bm2.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
Assert.ok(!("url" in bm2));
- Assert.ok(!("title" in bm2));
+ Assert.strictEqual(bm2.title, "");
await PlacesUtils.bookmarks.remove(bm1.guid);
});
add_task(async function fetch_byposition_nonexisting_parentGuid() {
let bm = await PlacesUtils.bookmarks.fetch({ parentGuid: "123456789012",
index: 0 },
gAccumulator.callback);
--- a/toolkit/components/places/tests/bookmarks/test_bookmarks_insert.js
+++ b/toolkit/components/places/tests/bookmarks/test_bookmarks_insert.js
@@ -112,17 +112,17 @@ add_task(async function create_separator
let bm = await PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
index: PlacesUtils.bookmarks.DEFAULT_INDEX });
checkBookmarkObject(bm);
Assert.equal(bm.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
Assert.equal(bm.index, 1);
Assert.equal(bm.dateAdded, bm.lastModified);
Assert.equal(bm.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
- Assert.ok(!("title" in bm), "title should not be set");
+ Assert.strictEqual(bm.title, "");
});
add_task(async function create_separator_w_title_fail() {
try {
await PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
title: "a separator" });
Assert.ok(false, "Trying to set title for a separator should reject");
@@ -144,17 +144,17 @@ add_task(async function create_separator
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
guid: "123456789012" });
checkBookmarkObject(bm);
Assert.equal(bm.guid, "123456789012");
Assert.equal(bm.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
Assert.equal(bm.index, 2);
Assert.equal(bm.dateAdded, bm.lastModified);
Assert.equal(bm.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
- Assert.ok(!("title" in bm), "title should not be set");
+ Assert.strictEqual(bm.title, "");
});
add_task(async function create_item_given_guid_no_type_fail() {
try {
await PlacesUtils.bookmarks.insert({ parentGuid: "123456789012" });
Assert.ok(false, "Trying to create an item with a given guid but no type should reject");
} catch (ex) {}
});
@@ -163,17 +163,17 @@ add_task(async function create_separator
let bm = await PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
index: 9999 });
checkBookmarkObject(bm);
Assert.equal(bm.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
Assert.equal(bm.index, 3);
Assert.equal(bm.dateAdded, bm.lastModified);
Assert.equal(bm.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
- Assert.ok(!("title" in bm), "title should not be set");
+ Assert.strictEqual(bm.title, "");
});
add_task(async function create_separator_given_dateAdded() {
let time = new Date();
let past = new Date(time - 86400000);
let bm = await PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
dateAdded: past });
@@ -184,17 +184,17 @@ add_task(async function create_separator
add_task(async function create_folder() {
let bm = await PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
type: PlacesUtils.bookmarks.TYPE_FOLDER });
checkBookmarkObject(bm);
Assert.equal(bm.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
Assert.equal(bm.dateAdded, bm.lastModified);
Assert.equal(bm.type, PlacesUtils.bookmarks.TYPE_FOLDER);
- Assert.ok(!("title" in bm), "title should not be set");
+ Assert.strictEqual(bm.title, "");
// And then create a nested folder.
let parentGuid = bm.guid;
bm = await PlacesUtils.bookmarks.insert({ parentGuid,
type: PlacesUtils.bookmarks.TYPE_FOLDER,
title: "a folder" });
checkBookmarkObject(bm);
Assert.equal(bm.parentGuid, parentGuid);
@@ -226,17 +226,17 @@ add_task(async function create_bookmark(
bm = await PlacesUtils.bookmarks.insert({ parentGuid,
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
url: new URL("http://example.com/") });
checkBookmarkObject(bm);
Assert.equal(bm.parentGuid, parentGuid);
Assert.equal(bm.index, 1);
Assert.equal(bm.type, PlacesUtils.bookmarks.TYPE_BOOKMARK);
Assert.equal(bm.url.href, "http://example.com/");
- Assert.ok(!("title" in bm), "title should not be set");
+ Assert.strictEqual(bm.title, "");
});
add_task(async function create_bookmark_frecency() {
let bm = await PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
url: "http://example.com/",
title: "a bookmark" });
checkBookmarkObject(bm);
--- a/toolkit/components/places/tests/bookmarks/test_bookmarks_insertTree.js
+++ b/toolkit/components/places/tests/bookmarks/test_bookmarks_insertTree.js
@@ -124,17 +124,17 @@ add_task(async function create_separator
let [bm] = await PlacesUtils.bookmarks.insertTree({children: [{
type: PlacesUtils.bookmarks.TYPE_SEPARATOR
}], guid: PlacesUtils.bookmarks.unfiledGuid});
checkBookmarkObject(bm);
Assert.equal(bm.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
Assert.equal(bm.index, 0);
Assert.equal(bm.dateAdded, bm.lastModified);
Assert.equal(bm.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
- Assert.ok(!("title" in bm), "title should not be set");
+ Assert.strictEqual(bm.title, "");
});
add_task(async function create_plain_bm() {
let [bm] = await PlacesUtils.bookmarks.insertTree({children: [{
url: "http://www.example.com/",
title: "Test"
}], guid: PlacesUtils.bookmarks.unfiledGuid});
checkBookmarkObject(bm);
--- a/toolkit/components/places/tests/bookmarks/test_bookmarks_notifications.js
+++ b/toolkit/components/places/tests/bookmarks/test_bookmarks_notifications.js
@@ -4,17 +4,17 @@
add_task(async function insert_separator_notification() {
let observer = expectNotifications();
let bm = await PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
parentGuid: PlacesUtils.bookmarks.unfiledGuid});
let itemId = await PlacesUtils.promiseItemId(bm.guid);
let parentId = await PlacesUtils.promiseItemId(bm.parentGuid);
observer.check([ { name: "onItemAdded",
arguments: [ itemId, parentId, bm.index, bm.type,
- null, null, bm.dateAdded * 1000,
+ null, "", bm.dateAdded * 1000,
bm.guid, bm.parentGuid,
Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(async function insert_folder_notification() {
let observer = expectNotifications();
let bm = await PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER,
@@ -29,21 +29,22 @@ add_task(async function insert_folder_no
Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(async function insert_folder_notitle_notification() {
let observer = expectNotifications();
let bm = await PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_FOLDER,
parentGuid: PlacesUtils.bookmarks.unfiledGuid });
+ strictEqual(bm.title, "", "Should return empty string for untitled folder");
let itemId = await PlacesUtils.promiseItemId(bm.guid);
let parentId = await PlacesUtils.promiseItemId(bm.parentGuid);
observer.check([ { name: "onItemAdded",
arguments: [ itemId, parentId, bm.index, bm.type,
- null, null, bm.dateAdded * 1000,
+ null, "", bm.dateAdded * 1000,
bm.guid, bm.parentGuid,
Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(async function insert_bookmark_notification() {
let observer = expectNotifications();
let bm = await PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
@@ -60,21 +61,22 @@ add_task(async function insert_bookmark_
]);
});
add_task(async function insert_bookmark_notitle_notification() {
let observer = expectNotifications();
let bm = await PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
url: new URL("http://example.com/") });
+ strictEqual(bm.title, "", "Should return empty string for untitled bookmark");
let itemId = await PlacesUtils.promiseItemId(bm.guid);
let parentId = await PlacesUtils.promiseItemId(bm.parentGuid);
observer.check([ { name: "onItemAdded",
arguments: [ itemId, parentId, bm.index, bm.type,
- bm.url, null, bm.dateAdded * 1000,
+ bm.url, "", bm.dateAdded * 1000,
bm.guid, bm.parentGuid,
Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
});
add_task(async function insert_bookmark_tag_notification() {
let bm = await PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
@@ -89,17 +91,17 @@ add_task(async function insert_bookmark_
let tag = await PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
parentGuid: tagFolder.guid,
url: new URL("http://tag.example.com/") });
let tagId = await PlacesUtils.promiseItemId(tag.guid);
let tagParentId = await PlacesUtils.promiseItemId(tag.parentGuid);
observer.check([ { name: "onItemAdded",
arguments: [ tagId, tagParentId, tag.index, tag.type,
- tag.url, null, tag.dateAdded * 1000,
+ tag.url, "", tag.dateAdded * 1000,
tag.guid, tag.parentGuid,
Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
{ name: "onItemChanged",
arguments: [ itemId, "tags", false, "",
bm.lastModified * 1000, bm.type, parentId,
bm.guid, bm.parentGuid, "",
Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
]);
@@ -485,33 +487,79 @@ add_task(async function reorder_notifica
child.parentGuid,
child.parentGuid,
Ci.nsINavBookmarksService.SOURCE_DEFAULT
] });
}
observer.check(expectedNotifications);
});
+add_task(async function update_notitle_notification() {
+ let toolbarBmURI = Services.io.newURI("https://example.com");
+ let toolbarBmId =
+ PlacesUtils.bookmarks.insertBookmark(PlacesUtils.toolbarFolderId,
+ toolbarBmURI, 0, "Bookmark");
+ let toolbarBmGuid = await PlacesUtils.promiseItemGuid(toolbarBmId);
+
+ let menuFolder = await PlacesUtils.bookmarks.insert({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ type: PlacesUtils.bookmarks.TYPE_FOLDER,
+ index: 0,
+ title: "Folder"
+ });
+ let menuFolderId = await PlacesUtils.promiseItemId(menuFolder.guid);
+
+ let observer = expectNotifications();
+
+ PlacesUtils.bookmarks.setItemTitle(toolbarBmId, null);
+ strictEqual(PlacesUtils.bookmarks.getItemTitle(toolbarBmId), "",
+ "Legacy API should return empty string for untitled bookmark");
+
+ let updatedMenuBm = await PlacesUtils.bookmarks.update({
+ guid: menuFolder.guid,
+ title: null,
+ });
+ strictEqual(updatedMenuBm.title, "",
+ "Async API should return empty string for untitled bookmark");
+
+ let toolbarBmModified =
+ PlacesUtils.toDate(PlacesUtils.bookmarks.getItemLastModified(toolbarBmId));
+ observer.check([{
+ name: "onItemChanged",
+ arguments: [toolbarBmId, "title", false, "", toolbarBmModified * 1000,
+ PlacesUtils.bookmarks.TYPE_BOOKMARK,
+ PlacesUtils.toolbarFolderId, toolbarBmGuid,
+ PlacesUtils.bookmarks.toolbarGuid,
+ "", PlacesUtils.bookmarks.SOURCES.DEFAULT],
+ }, {
+ name: "onItemChanged",
+ arguments: [menuFolderId, "title", false, "",
+ updatedMenuBm.lastModified * 1000,
+ PlacesUtils.bookmarks.TYPE_FOLDER,
+ PlacesUtils.bookmarksMenuFolderId, menuFolder.guid,
+ PlacesUtils.bookmarks.menuGuid,
+ "", PlacesUtils.bookmarks.SOURCES.DEFAULT],
+ }]);
+});
+
function expectNotifications() {
let notifications = [];
let observer = new Proxy(NavBookmarkObserver, {
get(target, name) {
if (name == "check") {
PlacesUtils.bookmarks.removeObserver(observer);
return expectedNotifications =>
Assert.deepEqual(notifications, expectedNotifications);
}
if (name.startsWith("onItem")) {
return (...origArgs) => {
let args = Array.from(origArgs, arg => {
if (arg && arg instanceof Ci.nsIURI)
return new URL(arg.spec);
- if (arg && typeof(arg) == "number" && arg >= Date.now() * 1000)
- return new Date(parseInt(arg / 1000));
return arg;
});
notifications.push({ name, arguments: args });
}
}
if (name in target)
return target[name];
--- a/toolkit/components/places/tests/bookmarks/test_bookmarks_remove.js
+++ b/toolkit/components/places/tests/bookmarks/test_bookmarks_remove.js
@@ -160,17 +160,17 @@ add_task(async function remove_bookmark_
title: "" });
checkBookmarkObject(bm1);
let bm2 = await PlacesUtils.bookmarks.remove(bm1.guid);
checkBookmarkObject(bm2);
Assert.deepEqual(bm1, bm2);
Assert.equal(bm2.index, 0);
- Assert.ok(!("title" in bm2));
+ Assert.strictEqual(bm2.title, "");
});
add_task(async function remove_folder() {
let bm1 = await PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
type: PlacesUtils.bookmarks.TYPE_FOLDER,
title: "a folder" });
checkBookmarkObject(bm1);
@@ -238,34 +238,34 @@ add_task(async function remove_folder_em
title: "" });
checkBookmarkObject(bm1);
let bm2 = await PlacesUtils.bookmarks.remove(bm1.guid);
checkBookmarkObject(bm2);
Assert.deepEqual(bm1, bm2);
Assert.equal(bm2.index, 0);
- Assert.ok(!("title" in bm2));
+ Assert.strictEqual(bm2.title, "");
});
add_task(async function remove_separator() {
let bm1 = await PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
type: PlacesUtils.bookmarks.TYPE_SEPARATOR });
checkBookmarkObject(bm1);
let bm2 = await PlacesUtils.bookmarks.remove(bm1.guid);
checkBookmarkObject(bm2);
Assert.deepEqual(bm1, bm2);
Assert.equal(bm2.parentGuid, PlacesUtils.bookmarks.unfiledGuid);
Assert.equal(bm2.index, 0);
Assert.deepEqual(bm2.dateAdded, bm2.lastModified);
Assert.equal(bm2.type, PlacesUtils.bookmarks.TYPE_SEPARATOR);
Assert.ok(!("url" in bm2));
- Assert.ok(!("title" in bm2));
+ Assert.strictEqual(bm2.title, "");
});
add_task(async function test_nested_content_fails_when_not_allowed() {
let folder1 = await PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
type: PlacesUtils.bookmarks.TYPE_FOLDER,
title: "a folder" });
await PlacesUtils.bookmarks.insert({ parentGuid: folder1.guid,
type: PlacesUtils.bookmarks.TYPE_FOLDER,
--- a/toolkit/components/places/tests/bookmarks/test_bookmarks_update.js
+++ b/toolkit/components/places/tests/bookmarks/test_bookmarks_update.js
@@ -189,20 +189,20 @@ add_task(async function update_lastModif
Assert.deepEqual(bm.lastModified, yesterday);
bm = await PlacesUtils.bookmarks.update({ guid: bm.guid,
title: "title2" });
Assert.ok(bm.lastModified >= time);
bm = await PlacesUtils.bookmarks.update({ guid: bm.guid,
title: "" });
- Assert.ok(!("title" in bm));
+ Assert.strictEqual(bm.title, "");
bm = await PlacesUtils.bookmarks.fetch(bm.guid);
- Assert.ok(!("title" in bm));
+ Assert.strictEqual(bm.title, "");
});
add_task(async function update_url() {
let bm = await PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
url: "http://example.com/",
title: "title" });
checkBookmarkObject(bm);
--- a/toolkit/components/places/tests/bookmarks/test_nsINavBookmarkObserver.js
+++ b/toolkit/components/places/tests/bookmarks/test_nsINavBookmarkObserver.js
@@ -159,17 +159,17 @@ add_task(async function onItemAdded_sepa
gBookmarksObserver.setup([
{ name: "onItemAdded",
args: [
{ name: "itemId", check: v => typeof(v) == "number" && v > 0 },
{ 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: "title", check: v => v === "" },
{ name: "dateAdded", check: v => typeof(v) == "number" && v > 0 },
{ name: "guid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
{ name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
] },
])]);
PlacesUtils.bookmarks.insertSeparator(PlacesUtils.unfiledBookmarksFolderId,
PlacesUtils.bookmarks.DEFAULT_INDEX);
@@ -254,17 +254,17 @@ add_task(async function onItemChanged_ta
] },
{ 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: "title", check: v => v === "" },
{ name: "dateAdded", check: v => typeof(v) == "number" && v > 0 },
{ name: "guid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
{ name: "parentGuid", check: v => typeof(v) == "string" && GUID_RE.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 },
--- a/toolkit/components/places/tests/queries/test_onlyBookmarked.js
+++ b/toolkit/components/places/tests/queries/test_onlyBookmarked.js
@@ -15,30 +15,33 @@
* results that are in the query set at the top of the testData list, and those
* results MUST be in the same sort order as the items in the resulting query.
*/
var testData = [
// Add a bookmark that should be in the results
{ isBookmark: true,
uri: "http://bookmarked.com/",
+ title: "",
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
isInQuery: true },
// Add a bookmark that should not be in the results
{ isBookmark: true,
uri: "http://bookmarked-elsewhere.com/",
+ title: "",
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
isInQuery: false },
// Add an un-bookmarked visit
{ isVisit: true,
uri: "http://notbookmarked.com/",
+ title: "",
isInQuery: false }
];
add_task(async function test_onlyBookmarked() {
// This function in head_queries.js creates our database with the above data
await task_populateDB(testData);
@@ -63,23 +66,25 @@ add_task(async function test_onlyBookmar
compareArrayToResult(testData, root);
do_print("end first test");
// Test live-update
var liveUpdateTestData = [
// Add a bookmark that should show up
{ isBookmark: true,
uri: "http://bookmarked2.com/",
+ title: "",
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
isInQuery: true },
// Add a bookmark that should not show up
{ isBookmark: true,
uri: "http://bookmarked-elsewhere2.com/",
+ title: "",
parentGuid: PlacesUtils.bookmarks.menuGuid,
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
isInQuery: false }
];
await task_populateDB(liveUpdateTestData); // add to the db
// add to the test data
--- a/toolkit/components/places/tests/unit/test_bookmarks_setNullTitle.js
+++ b/toolkit/components/places/tests/unit/test_bookmarks_setNullTitle.js
@@ -1,16 +1,17 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
- * Both SetItemtitle and insertBookmark should allow for null titles.
+ * Both setItemTitle and insertBookmark should default to the empty string
+ * for null titles.
*/
const bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
const TEST_URL = "http://www.mozilla.org";
function run_test() {
@@ -18,27 +19,27 @@ function run_test() {
var itemId = bs.insertBookmark(bs.toolbarFolder,
uri(TEST_URL),
bs.DEFAULT_INDEX,
"");
// Check returned title is an empty string.
do_check_eq(bs.getItemTitle(itemId), "");
// Set title to null.
bs.setItemTitle(itemId, null);
- // Check returned title is null.
- do_check_eq(bs.getItemTitle(itemId), null);
+ // Check returned title defaults to an empty string.
+ do_check_eq(bs.getItemTitle(itemId), "");
// Cleanup.
bs.removeItem(itemId);
// Insert a bookmark with a null title.
itemId = bs.insertBookmark(bs.toolbarFolder,
uri(TEST_URL),
bs.DEFAULT_INDEX,
null);
- // Check returned title is null.
- do_check_eq(bs.getItemTitle(itemId), null);
+ // Check returned title defaults to an empty string.
+ do_check_eq(bs.getItemTitle(itemId), "");
// Set title to an empty string.
bs.setItemTitle(itemId, "");
// Check returned title is an empty string.
do_check_eq(bs.getItemTitle(itemId), "");
// Cleanup.
bs.removeItem(itemId);
}