mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 08:15:31 +00:00
Bug 1310295 - Make left pane queries virtual in the Places Library window. r=kitcambridge,mak
MozReview-Commit-ID: DcEMAlrXu8R --HG-- extra : rebase_source : c98e6a50702ef0c238d036fa2a26f4a6ac9f15af
This commit is contained in:
parent
4de3433bd6
commit
2155e56144
@ -1398,18 +1398,7 @@ var BookmarkingUI = {
|
||||
MOBILE_BOOKMARKS_PREF: "browser.bookmarks.showMobileBookmarks",
|
||||
|
||||
_shouldShowMobileBookmarks() {
|
||||
try {
|
||||
return Services.prefs.getBoolPref(this.MOBILE_BOOKMARKS_PREF);
|
||||
} catch (e) {}
|
||||
// No pref set (or invalid pref set), look for a mobile bookmarks left pane query.
|
||||
const organizerQueryAnno = "PlacesOrganizer/OrganizerQuery";
|
||||
const mobileBookmarksAnno = "MobileBookmarks";
|
||||
let shouldShow = PlacesUtils.annotations.getItemsWithAnnotation(organizerQueryAnno, {}).filter(
|
||||
id => PlacesUtils.annotations.getItemAnnotation(id, organizerQueryAnno) == mobileBookmarksAnno
|
||||
).length > 0;
|
||||
// Sync will change this pref if/when it adds a mobile bookmarks query.
|
||||
Services.prefs.setBoolPref(this.MOBILE_BOOKMARKS_PREF, shouldShow);
|
||||
return shouldShow;
|
||||
return Services.prefs.getBoolPref(this.MOBILE_BOOKMARKS_PREF, false);
|
||||
},
|
||||
|
||||
_initMobileBookmarks(mobileMenuItem) {
|
||||
|
@ -206,10 +206,6 @@ let InternalFaviconLoader = {
|
||||
};
|
||||
|
||||
var PlacesUIUtils = {
|
||||
ORGANIZER_LEFTPANE_VERSION: 8,
|
||||
ORGANIZER_FOLDER_ANNO: "PlacesOrganizer/OrganizerFolder",
|
||||
ORGANIZER_QUERY_ANNO: "PlacesOrganizer/OrganizerQuery",
|
||||
|
||||
LOAD_IN_SIDEBAR_ANNO: "bookmarkProperties/loadInSidebar",
|
||||
DESCRIPTION_ANNO: "bookmarkProperties/description",
|
||||
|
||||
@ -531,11 +527,16 @@ var PlacesUIUtils = {
|
||||
}
|
||||
|
||||
// Is it a query pointing to one of the special root folders?
|
||||
if (PlacesUtils.nodeIsQuery(parentNode) && PlacesUtils.nodeIsFolder(aNode)) {
|
||||
let guid = PlacesUtils.getConcreteItemGuid(aNode);
|
||||
// If the parent folder is not a folder, it must be a query, and so this node
|
||||
// cannot be removed.
|
||||
if (PlacesUtils.isRootItem(guid)) {
|
||||
if (PlacesUtils.nodeIsQuery(parentNode)) {
|
||||
if (PlacesUtils.nodeIsFolder(aNode)) {
|
||||
let guid = PlacesUtils.getConcreteItemGuid(aNode);
|
||||
// If the parent folder is not a folder, it must be a query, and so this node
|
||||
// cannot be removed.
|
||||
if (PlacesUtils.isRootItem(guid)) {
|
||||
return false;
|
||||
}
|
||||
} else if (PlacesUtils.isVirtualLeftPaneItem(aNode.bookmarkGuid)) {
|
||||
// If the item is a left-pane top-level item, it can't be removed.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -590,21 +591,7 @@ var PlacesUIUtils = {
|
||||
view.controller.hasCachedLivemarkInfo(placesNode))
|
||||
return true;
|
||||
|
||||
// leftPaneFolderId is a lazy getter
|
||||
// performing at least a synchronous DB query (and on its very first call
|
||||
// in a fresh profile, it also creates the entire structure).
|
||||
// Therefore we don't want to this function, which is called very often by
|
||||
// isCommandEnabled, to ever be the one that invokes it first, especially
|
||||
// because isCommandEnabled may be called way before the left pane folder is
|
||||
// even created (for example, if the user only uses the bookmarks menu or
|
||||
// toolbar for managing bookmarks). To do so, we avoid comparing to those
|
||||
// special folder if the lazy getter is still in place. This is safe merely
|
||||
// because the only way to access the left pane contents goes through
|
||||
// "resolving" the leftPaneFolderId getter.
|
||||
if (typeof Object.getOwnPropertyDescriptor(this, "leftPaneFolderId").get == "function") {
|
||||
return false;
|
||||
}
|
||||
return itemId == this.leftPaneFolderId;
|
||||
return false;
|
||||
},
|
||||
|
||||
/** aItemsToOpen needs to be an array of objects of the form:
|
||||
@ -814,261 +801,6 @@ var PlacesUIUtils = {
|
||||
return title || this.getString("noTitle");
|
||||
},
|
||||
|
||||
get leftPaneQueries() {
|
||||
// build the map
|
||||
this.leftPaneFolderId;
|
||||
return this.leftPaneQueries;
|
||||
},
|
||||
|
||||
get leftPaneFolderId() {
|
||||
delete this.leftPaneFolderId;
|
||||
return this.leftPaneFolderId = this.maybeRebuildLeftPane();
|
||||
},
|
||||
|
||||
// Get the folder id for the organizer left-pane folder.
|
||||
maybeRebuildLeftPane() {
|
||||
let leftPaneRoot = -1;
|
||||
|
||||
// Shortcuts to services.
|
||||
let bs = PlacesUtils.bookmarks;
|
||||
let as = PlacesUtils.annotations;
|
||||
|
||||
// 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") },
|
||||
};
|
||||
// All queries but PlacesRoot.
|
||||
const EXPECTED_QUERY_COUNT = 4;
|
||||
|
||||
// Removes an item and associated annotations, ignoring eventual errors.
|
||||
function safeRemoveItem(aItemId) {
|
||||
try {
|
||||
if (as.itemHasAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) &&
|
||||
!(as.getItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) in queries)) {
|
||||
// Some extension annotated their roots with our query annotation,
|
||||
// so we should not delete them.
|
||||
return;
|
||||
}
|
||||
// removeItemAnnotation does not check if item exists, nor the anno,
|
||||
// so this is safe to do.
|
||||
as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
|
||||
as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO);
|
||||
// This will throw if the annotation is an orphan.
|
||||
bs.removeItem(aItemId);
|
||||
} catch (e) { /* orphan anno */ }
|
||||
}
|
||||
|
||||
// Returns true if item really exists, false otherwise.
|
||||
function itemExists(aItemId) {
|
||||
try {
|
||||
bs.getFolderIdForItem(aItemId);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get all items marked as being the left pane folder.
|
||||
let items = as.getItemsWithAnnotation(this.ORGANIZER_FOLDER_ANNO);
|
||||
if (items.length > 1) {
|
||||
// Something went wrong, we cannot have more than one left pane folder,
|
||||
// remove all left pane folders and continue. We will create a new one.
|
||||
items.forEach(safeRemoveItem);
|
||||
} else if (items.length == 1 && items[0] != -1) {
|
||||
leftPaneRoot = items[0];
|
||||
// Check that organizer left pane root is valid.
|
||||
let version = as.getItemAnnotation(leftPaneRoot, this.ORGANIZER_FOLDER_ANNO);
|
||||
if (version != this.ORGANIZER_LEFTPANE_VERSION ||
|
||||
!itemExists(leftPaneRoot)) {
|
||||
// Invalid root, we must rebuild the left pane.
|
||||
safeRemoveItem(leftPaneRoot);
|
||||
leftPaneRoot = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (leftPaneRoot != -1) {
|
||||
// A valid left pane folder has been found.
|
||||
// Build the leftPaneQueries Map. This is used to quickly access them,
|
||||
// associating a mnemonic name to the real item ids.
|
||||
delete this.leftPaneQueries;
|
||||
this.leftPaneQueries = {};
|
||||
|
||||
let queryItems = as.getItemsWithAnnotation(this.ORGANIZER_QUERY_ANNO);
|
||||
// While looping through queries we will also check for their validity.
|
||||
let queriesCount = 0;
|
||||
let corrupt = false;
|
||||
for (let i = 0; i < queryItems.length; i++) {
|
||||
let queryName = as.getItemAnnotation(queryItems[i], this.ORGANIZER_QUERY_ANNO);
|
||||
|
||||
// Some extension did use our annotation to decorate their items
|
||||
// with icons, so we should check only our elements, to avoid dataloss.
|
||||
if (!(queryName in queries))
|
||||
continue;
|
||||
|
||||
let query = queries[queryName];
|
||||
query.itemId = queryItems[i];
|
||||
|
||||
if (!itemExists(query.itemId)) {
|
||||
// Orphan annotation, bail out and create a new left pane root.
|
||||
corrupt = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check that all queries have valid parents.
|
||||
let parentId = bs.getFolderIdForItem(query.itemId);
|
||||
if (!queryItems.includes(parentId) && parentId != leftPaneRoot) {
|
||||
// The parent is not part of the left pane, bail out and create a new
|
||||
// left pane root.
|
||||
corrupt = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Titles could have been corrupted or the user could have changed his
|
||||
// locale. Check title and eventually fix it.
|
||||
if (bs.getItemTitle(query.itemId) != query.title)
|
||||
bs.setItemTitle(query.itemId, query.title);
|
||||
if ("concreteId" in query) {
|
||||
if (bs.getItemTitle(query.concreteId) != query.concreteTitle)
|
||||
bs.setItemTitle(query.concreteId, query.concreteTitle);
|
||||
}
|
||||
|
||||
// Add the query to our cache.
|
||||
this.leftPaneQueries[queryName] = query.itemId;
|
||||
queriesCount++;
|
||||
}
|
||||
|
||||
// Note: it's not enough to just check for queriesCount, since we may
|
||||
// find an invalid query just after accounting for a sufficient number of
|
||||
// valid ones. As well as we can't just rely on corrupt since we may find
|
||||
// less valid queries than expected.
|
||||
if (corrupt || queriesCount != EXPECTED_QUERY_COUNT) {
|
||||
// Queries number is wrong, so the left pane must be corrupt.
|
||||
// Note: we can't just remove the leftPaneRoot, because some query could
|
||||
// have a bad parent, so we have to remove all items one by one.
|
||||
queryItems.forEach(safeRemoveItem);
|
||||
safeRemoveItem(leftPaneRoot);
|
||||
} else {
|
||||
// Everything is fine, return the current left pane folder.
|
||||
return leftPaneRoot;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new left pane folder.
|
||||
var callback = {
|
||||
// Helper to create an organizer special query.
|
||||
create_query: function CB_create_query(aQueryName, aParentId, aQueryUrl) {
|
||||
let itemId = bs.insertBookmark(aParentId,
|
||||
Services.io.newURI(aQueryUrl),
|
||||
bs.DEFAULT_INDEX,
|
||||
queries[aQueryName].title);
|
||||
// Mark as special organizer query.
|
||||
as.setItemAnnotation(itemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aQueryName,
|
||||
0, as.EXPIRE_NEVER);
|
||||
// We should never backup this, since it changes between profiles.
|
||||
as.setItemAnnotation(itemId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1,
|
||||
0, as.EXPIRE_NEVER);
|
||||
// Add to the queries map.
|
||||
PlacesUIUtils.leftPaneQueries[aQueryName] = itemId;
|
||||
return itemId;
|
||||
},
|
||||
|
||||
// Helper to create an organizer special folder.
|
||||
create_folder: function CB_create_folder(aFolderName, aParentId, aIsRoot) {
|
||||
// Left Pane Root Folder.
|
||||
let folderId = bs.createFolder(aParentId,
|
||||
queries[aFolderName].title,
|
||||
bs.DEFAULT_INDEX);
|
||||
// We should never backup this, since it changes between profiles.
|
||||
as.setItemAnnotation(folderId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1,
|
||||
0, as.EXPIRE_NEVER);
|
||||
|
||||
if (aIsRoot) {
|
||||
// Mark as special left pane root.
|
||||
as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO,
|
||||
PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION,
|
||||
0, as.EXPIRE_NEVER);
|
||||
} else {
|
||||
// Mark as special organizer folder.
|
||||
as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aFolderName,
|
||||
0, as.EXPIRE_NEVER);
|
||||
PlacesUIUtils.leftPaneQueries[aFolderName] = folderId;
|
||||
}
|
||||
return folderId;
|
||||
},
|
||||
|
||||
runBatched: function CB_runBatched(aUserData) {
|
||||
delete PlacesUIUtils.leftPaneQueries;
|
||||
PlacesUIUtils.leftPaneQueries = { };
|
||||
|
||||
// Left Pane Root Folder.
|
||||
leftPaneRoot = this.create_folder("PlacesRoot", bs.placesRoot, true);
|
||||
|
||||
// History Query.
|
||||
this.create_query("History", leftPaneRoot,
|
||||
"place:type=" +
|
||||
Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY +
|
||||
"&sort=" +
|
||||
Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING);
|
||||
|
||||
// Downloads.
|
||||
this.create_query("Downloads", leftPaneRoot,
|
||||
"place:transition=" +
|
||||
Ci.nsINavHistoryService.TRANSITION_DOWNLOAD +
|
||||
"&sort=" +
|
||||
Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING);
|
||||
|
||||
// Tags Query.
|
||||
this.create_query("Tags", leftPaneRoot,
|
||||
"place:type=" +
|
||||
Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY +
|
||||
"&sort=" +
|
||||
Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING);
|
||||
|
||||
// All Bookmarks Folder.
|
||||
this.create_query("AllBookmarks", leftPaneRoot,
|
||||
"place:type=" +
|
||||
Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY);
|
||||
}
|
||||
};
|
||||
bs.runInBatchMode(callback, null);
|
||||
|
||||
return leftPaneRoot;
|
||||
},
|
||||
|
||||
/**
|
||||
* If an item is a left-pane query, returns the name of the query
|
||||
* or an empty string if not.
|
||||
*
|
||||
* @param aItemId id of a container
|
||||
* @return the name of the query, or empty string if not a left-pane query
|
||||
*/
|
||||
getLeftPaneQueryNameFromId: function PUIU_getLeftPaneQueryNameFromId(aItemId) {
|
||||
var queryName = "";
|
||||
// If the let pane hasn't been built, use the annotation service
|
||||
// directly, to avoid building the left pane too early.
|
||||
if (Object.getOwnPropertyDescriptor(this, "leftPaneFolderId").value === undefined) {
|
||||
try {
|
||||
queryName = PlacesUtils.annotations.
|
||||
getItemAnnotation(aItemId, this.ORGANIZER_QUERY_ANNO);
|
||||
} catch (ex) {
|
||||
// doesn't have the annotation
|
||||
queryName = "";
|
||||
}
|
||||
} else {
|
||||
// If the left pane has already been built, use the name->id map
|
||||
// cached in PlacesUIUtils.
|
||||
for (let [name, id] of Object.entries(this.leftPaneQueries)) {
|
||||
if (aItemId == id)
|
||||
queryName = name;
|
||||
}
|
||||
}
|
||||
return queryName;
|
||||
},
|
||||
|
||||
shouldShowTabsFromOtherComputersMenuitem() {
|
||||
let weaveOK = Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED &&
|
||||
Weave.Svc.Prefs.get("firstSync", "") != "notReady";
|
||||
@ -1354,14 +1086,7 @@ function canMoveUnwrappedNode(unwrappedNode) {
|
||||
parentGuid == PlacesUtils.bookmarks.rootGuid) {
|
||||
return false;
|
||||
}
|
||||
// leftPaneFolderId and allBookmarksFolderId are lazy getters running
|
||||
// at least a synchronous DB query. Therefore we don't want to invoke
|
||||
// them first, especially because isCommandEnabled may be called way
|
||||
// before the left pane folder is even necessary.
|
||||
if (typeof Object.getOwnPropertyDescriptor(PlacesUIUtils, "leftPaneFolderId").get != "function" &&
|
||||
(unwrappedNode.parent == PlacesUIUtils.leftPaneFolderId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -56,12 +56,6 @@ var gEditItemOverlay = {
|
||||
}
|
||||
let parent = node.parent;
|
||||
isParentReadOnly = !PlacesUtils.nodeIsFolder(parent);
|
||||
if (!isParentReadOnly) {
|
||||
let folderId = PlacesUtils.getConcreteItemId(parent);
|
||||
isParentReadOnly = folderId == PlacesUtils.placesRootId ||
|
||||
(!("get" in Object.getOwnPropertyDescriptor(PlacesUIUtils, "leftPaneFolderId")) &&
|
||||
(folderId == PlacesUIUtils.leftPaneFolderId));
|
||||
}
|
||||
parentId = parent.itemId;
|
||||
parentGuid = parent.bookmarkGuid;
|
||||
}
|
||||
|
@ -36,8 +36,7 @@ var PlacesOrganizer = {
|
||||
],
|
||||
|
||||
_initFolderTree() {
|
||||
var leftPaneRoot = PlacesUIUtils.leftPaneFolderId;
|
||||
this._places.place = "place:excludeItems=1&expandQueries=0&folder=" + leftPaneRoot;
|
||||
this._places.place = `place:type=${Ci.nsINavHistoryQueryOptions.RESULTS_AS_LEFT_PANE_QUERY}&excludeItems=1&expandQueries=0`;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -50,31 +49,34 @@ var PlacesOrganizer = {
|
||||
selectLeftPaneBuiltIn(item) {
|
||||
switch (item) {
|
||||
case "AllBookmarks":
|
||||
case "History":
|
||||
case "Downloads":
|
||||
case "Tags": {
|
||||
var itemId = PlacesUIUtils.leftPaneQueries[item];
|
||||
this._places.selectItems([itemId]);
|
||||
// Forcefully expand all-bookmarks
|
||||
if (item == "AllBookmarks" || item == "History")
|
||||
PlacesUtils.asContainer(this._places.selectedNode).containerOpen = true;
|
||||
this._places.selectItems([PlacesUtils.virtualAllBookmarksGuid]);
|
||||
PlacesUtils.asContainer(this._places.selectedNode).containerOpen = true;
|
||||
break;
|
||||
case "History":
|
||||
this._places.selectItems([PlacesUtils.virtualHistoryGuid]);
|
||||
PlacesUtils.asContainer(this._places.selectedNode).containerOpen = true;
|
||||
break;
|
||||
case "Downloads":
|
||||
this._places.selectItems([PlacesUtils.virtualDownloadsGuid]);
|
||||
break;
|
||||
case "Tags":
|
||||
this._places.selectItems([PlacesUtils.virtualTagsGuid]);
|
||||
break;
|
||||
}
|
||||
case "BookmarksMenu":
|
||||
this.selectLeftPaneContainerByHierarchy([
|
||||
PlacesUIUtils.leftPaneQueries.AllBookmarks,
|
||||
PlacesUtils.virtualAllBookmarksGuid,
|
||||
PlacesUtils.bookmarks.virtualMenuGuid
|
||||
]);
|
||||
break;
|
||||
case "BookmarksToolbar":
|
||||
this.selectLeftPaneContainerByHierarchy([
|
||||
PlacesUIUtils.leftPaneQueries.AllBookmarks,
|
||||
PlacesUtils.virtualAllBookmarksGuid,
|
||||
PlacesUtils.bookmarks.virtualToolbarGuid
|
||||
]);
|
||||
break;
|
||||
case "UnfiledBookmarks":
|
||||
this.selectLeftPaneContainerByHierarchy([
|
||||
PlacesUIUtils.leftPaneQueries.AllBookmarks,
|
||||
PlacesUtils.virtualAllBookmarksGuid,
|
||||
PlacesUtils.bookmarks.virtualUnfiledGuid
|
||||
]);
|
||||
break;
|
||||
@ -92,7 +94,6 @@ var PlacesOrganizer = {
|
||||
* container may be either an item id, a Places URI string,
|
||||
* or a named query, like:
|
||||
* "BookmarksMenu", "BookmarksToolbar", "UnfiledBookmarks", "AllBookmarks".
|
||||
* @see PlacesUIUtils.leftPaneQueries for supported named queries.
|
||||
*/
|
||||
selectLeftPaneContainerByHierarchy(aHierarchy) {
|
||||
if (!aHierarchy)
|
||||
@ -312,12 +313,12 @@ var PlacesOrganizer = {
|
||||
* the node to set up scope from
|
||||
*/
|
||||
_setSearchScopeForNode: function PO__setScopeForNode(aNode) {
|
||||
let itemId = aNode.itemId;
|
||||
let itemGuid = aNode.bookmarkGuid;
|
||||
|
||||
if (PlacesUtils.nodeIsHistoryContainer(aNode) ||
|
||||
itemId == PlacesUIUtils.leftPaneQueries.History) {
|
||||
itemGuid == PlacesUtils.virtualHistoryGuid) {
|
||||
PlacesQueryBuilder.setScope("history");
|
||||
} else if (itemId == PlacesUIUtils.leftPaneQueries.Downloads) {
|
||||
} else if (itemGuid == PlacesUtils.virtualDownloadsGuid) {
|
||||
PlacesQueryBuilder.setScope("downloads");
|
||||
} else {
|
||||
// Default to All Bookmarks for all other nodes, per bug 469437.
|
||||
|
@ -632,7 +632,7 @@
|
||||
// caller. We support the "AllBookmarks" case to allow callers to
|
||||
// specify just the top-level bookmark folders.
|
||||
let shouldOpen = aOpenContainers && (PlacesUtils.nodeIsFolder(node) ||
|
||||
(PlacesUtils.nodeIsQuery(node) && node.itemId == PlacesUIUtils.leftPaneQueries.AllBookmarks));
|
||||
(PlacesUtils.nodeIsQuery(node) && node.bookmarkGuid == PlacesUIUtils.virtualAllBookmarksGuid));
|
||||
|
||||
PlacesUtils.asContainer(node);
|
||||
if (!node.containerOpen && !shouldOpen)
|
||||
|
@ -138,6 +138,7 @@ PlacesTreeView.prototype = {
|
||||
case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY:
|
||||
case Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY:
|
||||
case Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY:
|
||||
case Ci.nsINavHistoryQueryOptions.RESULTS_AS_LEFT_PANE_QUERY:
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1305,11 +1306,13 @@ PlacesTreeView.prototype = {
|
||||
case PlacesUtils.bookmarks.virtualUnfiledGuid:
|
||||
properties += ` queryFolder_${PlacesUtils.bookmarks.unfiledGuid}`;
|
||||
break;
|
||||
case PlacesUtils.virtualAllBookmarksGuid:
|
||||
case PlacesUtils.virtualHistoryGuid:
|
||||
case PlacesUtils.virtualDownloadsGuid:
|
||||
case PlacesUtils.virtualTagsGuid:
|
||||
properties += ` OrganizerQuery_${node.bookmarkGuid}`;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
let queryName = PlacesUIUtils.getLeftPaneQueryNameFromId(itemId);
|
||||
if (queryName)
|
||||
properties += " OrganizerQuery_" + queryName;
|
||||
}
|
||||
} else if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR)
|
||||
properties += " separator";
|
||||
@ -1792,15 +1795,6 @@ PlacesTreeView.prototype = {
|
||||
PlacesUtils.isQueryGeneratedFolder(itemGuid))
|
||||
return false;
|
||||
|
||||
let parentId = PlacesUtils.getConcreteItemId(node.parent);
|
||||
if (parentId == PlacesUIUtils.leftPaneFolderId) {
|
||||
// Note that the for the time being this is the check that actually
|
||||
// blocks renaming places "roots", and not the isRootItem check above.
|
||||
// That's because places root are only exposed through folder shortcuts
|
||||
// descendants of the left pane folder.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
|
@ -79,7 +79,6 @@ skip-if = (os == 'win' && ccov) # Bug 1423667
|
||||
[browser_library_downloads.js]
|
||||
skip-if = (os == 'win' && ccov) # Bug 1423667
|
||||
[browser_library_infoBox.js]
|
||||
[browser_library_left_pane_fixnames.js]
|
||||
[browser_library_left_pane_middleclick.js]
|
||||
skip-if = (os == 'win' && ccov) # Bug 1423667
|
||||
[browser_library_left_pane_select_hierarchy.js]
|
||||
|
@ -50,7 +50,7 @@ add_task(async function copy_mobile_shortcut() {
|
||||
});
|
||||
|
||||
library.PlacesOrganizer.selectLeftPaneContainerByHierarchy([
|
||||
PlacesUIUtils.leftPaneQueries.AllBookmarks,
|
||||
PlacesUtils.virtualAllBookmarksGuid,
|
||||
PlacesUtils.bookmarks.virtualMobileGuid,
|
||||
]);
|
||||
|
||||
|
@ -1,71 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Test we correctly fix broken Library left pane queries names.
|
||||
*/
|
||||
|
||||
// Array of left pane queries objects, each one has the following properties:
|
||||
// name: query's identifier got from annotations,
|
||||
// itemId: query's itemId,
|
||||
// correctTitle: original and correct query's title.
|
||||
var leftPaneQueries = [];
|
||||
|
||||
function onLibraryReady(organizer) {
|
||||
// Check titles have been fixed.
|
||||
for (var i = 0; i < leftPaneQueries.length; i++) {
|
||||
var query = leftPaneQueries[i];
|
||||
if ("concreteId" in query) {
|
||||
is(PlacesUtils.bookmarks.getItemTitle(query.concreteId),
|
||||
query.concreteTitle, "Concrete title is correct for query " + query.name);
|
||||
}
|
||||
}
|
||||
|
||||
// Close Library window.
|
||||
organizer.close();
|
||||
// No need to cleanup anything, we have a correct left pane now.
|
||||
finish();
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
// Ensure left pane is initialized.
|
||||
ok(PlacesUIUtils.leftPaneFolderId > 0, "left pane folder is initialized");
|
||||
|
||||
// Get the left pane folder.
|
||||
var leftPaneItems = PlacesUtils.annotations
|
||||
.getItemsWithAnnotation(PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
|
||||
|
||||
is(leftPaneItems.length, 1, "We correctly have only 1 left pane folder");
|
||||
// Check version.
|
||||
var version = PlacesUtils.annotations
|
||||
.getItemAnnotation(leftPaneItems[0],
|
||||
PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
|
||||
is(version, PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION, "Left pane version is actual");
|
||||
|
||||
// Get all left pane queries.
|
||||
var items = PlacesUtils.annotations
|
||||
.getItemsWithAnnotation(PlacesUIUtils.ORGANIZER_QUERY_ANNO);
|
||||
// Get current queries names.
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var itemId = items[i];
|
||||
var queryName = PlacesUtils.annotations
|
||||
.getItemAnnotation(items[i],
|
||||
PlacesUIUtils.ORGANIZER_QUERY_ANNO);
|
||||
var query = { name: queryName,
|
||||
itemId,
|
||||
correctTitle: PlacesUtils.bookmarks.getItemTitle(itemId) };
|
||||
|
||||
leftPaneQueries.push(query);
|
||||
// Rename to a bad title.
|
||||
PlacesUtils.bookmarks.setItemTitle(query.itemId, "badName");
|
||||
}
|
||||
|
||||
restoreLeftPaneGetters();
|
||||
|
||||
// Open Library, this will kick-off left pane code.
|
||||
openLibrary(onLibraryReady);
|
||||
}
|
@ -30,98 +30,33 @@ const TEST_DOWNLOAD_URL = "http://dummy.mozilla.org/dummy.pdf";
|
||||
|
||||
var gLibrary;
|
||||
|
||||
var testCases = [
|
||||
function allBookmarksScope() {
|
||||
let defScope = getDefaultScope(PlacesUIUtils.leftPaneQueries.AllBookmarks);
|
||||
search(PlacesUIUtils.allBookmarksFolderId, "dummy", defScope);
|
||||
},
|
||||
|
||||
function historyScope() {
|
||||
let defScope = getDefaultScope(PlacesUIUtils.leftPaneQueries.History);
|
||||
search(PlacesUIUtils.leftPaneQueries.History, "dummy", defScope);
|
||||
},
|
||||
|
||||
function downloadsScope() {
|
||||
let defScope = getDefaultScope(PlacesUIUtils.leftPaneQueries.Downloads);
|
||||
search(PlacesUIUtils.leftPaneQueries.Downloads, "dummy", defScope);
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns the default search scope for a given folder.
|
||||
*
|
||||
* @param aFolderId
|
||||
* the item ID of a node in the left pane's tree
|
||||
* @return the default scope when the folder is newly selected
|
||||
*/
|
||||
function getDefaultScope(aFolderId) {
|
||||
switch (aFolderId) {
|
||||
case PlacesUIUtils.leftPaneQueries.History:
|
||||
return "scopeBarHistory";
|
||||
case PlacesUIUtils.leftPaneQueries.Downloads:
|
||||
return "scopeBarDownloads";
|
||||
default:
|
||||
return "scopeBarAll";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the single nsINavHistoryQuery represented by a given place URI.
|
||||
*
|
||||
* @param aPlaceURI
|
||||
* a URI that represents a single query
|
||||
* @return an nsINavHistoryQuery object
|
||||
*/
|
||||
function queryStringToQuery(aPlaceURI) {
|
||||
let queries = {};
|
||||
PlacesUtils.history.queryStringToQueries(aPlaceURI, queries, {}, {});
|
||||
return queries.value[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the search by clearing the search box's text and ensures that the
|
||||
* search scope remains as expected.
|
||||
*
|
||||
* @param aExpectedScopeButtonId
|
||||
* this button should be selected after the reset
|
||||
*/
|
||||
function resetSearch(aExpectedScopeButtonId) {
|
||||
search(null, "", aExpectedScopeButtonId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a search for a given folder and search string and ensures that the
|
||||
* URI of the right pane's content tree is as expected for the folder and search
|
||||
* string. Also ensures that the search scope button is as expected after the
|
||||
* search.
|
||||
*
|
||||
* @param aFolderId
|
||||
* the item ID of a node in the left pane's tree
|
||||
* @param aFolderGuid
|
||||
* the item guid of a node in the left pane's tree
|
||||
* @param aSearchStr
|
||||
* the search text; may be empty to reset the search
|
||||
* @param aExpectedScopeButtonId
|
||||
* after searching the selected scope button should be this
|
||||
*/
|
||||
function search(aFolderId, aSearchStr, aExpectedScopeButtonId) {
|
||||
async function search(aFolderGuid, aSearchStr) {
|
||||
let doc = gLibrary.document;
|
||||
let folderTree = doc.getElementById("placesList");
|
||||
let contentTree = doc.getElementById("placeContent");
|
||||
|
||||
// First, ensure that selecting the folder in the left pane updates the
|
||||
// content tree properly.
|
||||
if (aFolderId) {
|
||||
folderTree.selectItems([aFolderId]);
|
||||
isnot(folderTree.selectedNode, null,
|
||||
if (aFolderGuid) {
|
||||
folderTree.selectItems([aFolderGuid]);
|
||||
Assert.notEqual(folderTree.selectedNode, null,
|
||||
"Sanity check: left pane tree should have selection after selecting!");
|
||||
|
||||
// getFolders() on a History query returns an empty array, so no use
|
||||
// comparing against aFolderId in that case.
|
||||
if (aFolderId !== PlacesUIUtils.leftPaneQueries.History &&
|
||||
aFolderId !== PlacesUIUtils.leftPaneQueries.Downloads) {
|
||||
// contentTree.place should be equal to contentTree.result.root.uri,
|
||||
// but it's not until bug 476952 is fixed.
|
||||
let query = queryStringToQuery(contentTree.result.root.uri);
|
||||
is(query.getFolders()[0], aFolderId,
|
||||
// The downloads folder never quite matches the url of the contentTree,
|
||||
// probably due to the way downloads are loaded.
|
||||
if (aFolderGuid !== PlacesUtils.virtualDownloadsGuid) {
|
||||
Assert.equal(folderTree.selectedNode.uri, contentTree.place,
|
||||
"Content tree's folder should be what was selected in the left pane");
|
||||
}
|
||||
}
|
||||
@ -131,13 +66,14 @@ function search(aFolderId, aSearchStr, aExpectedScopeButtonId) {
|
||||
let searchBox = doc.getElementById("searchFilter");
|
||||
searchBox.value = aSearchStr;
|
||||
gLibrary.PlacesSearchBox.search(searchBox.value);
|
||||
let query = queryStringToQuery(contentTree.result.root.uri);
|
||||
let queries = {};
|
||||
PlacesUtils.history.queryStringToQueries(contentTree.result.root.uri, queries, {}, {});
|
||||
if (aSearchStr) {
|
||||
is(query.searchTerms, aSearchStr,
|
||||
"Content tree's searchTerms should be text in search box");
|
||||
Assert.equal(queries.value[0].searchTerms, aSearchStr,
|
||||
"Content tree's searchTerms should be text in search box");
|
||||
} else {
|
||||
is(query.hasSearchTerms, false,
|
||||
"Content tree's searchTerms should not exist after search reset");
|
||||
Assert.equal(queries.value[0].hasSearchTerms, false,
|
||||
"Content tree's searchTerms should not exist after search reset");
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,7 +96,15 @@ add_task(async function test() {
|
||||
|
||||
gLibrary = await promiseLibrary();
|
||||
|
||||
testCases.forEach(aTest => aTest());
|
||||
const rootsToTest = [
|
||||
PlacesUtils.virtualAllBookmarksGuid,
|
||||
PlacesUtils.virtualHistoryGuid,
|
||||
PlacesUtils.virtualDownloadsGuid,
|
||||
];
|
||||
|
||||
for (let root of rootsToTest) {
|
||||
await search(root, "dummy");
|
||||
}
|
||||
|
||||
await promiseLibraryClosed(gLibrary);
|
||||
|
||||
|
@ -5,27 +5,6 @@ ChromeUtils.defineModuleGetter(this, "PlacesTestUtils",
|
||||
ChromeUtils.defineModuleGetter(this, "TestUtils",
|
||||
"resource://testing-common/TestUtils.jsm");
|
||||
|
||||
// We need to cache these before test runs...
|
||||
let leftPaneGetters = new Map([["leftPaneFolderId", null]]);
|
||||
for (let [key, val] of leftPaneGetters) {
|
||||
if (!val) {
|
||||
let getter = Object.getOwnPropertyDescriptor(PlacesUIUtils, key).get;
|
||||
if (typeof getter == "function") {
|
||||
leftPaneGetters.set(key, getter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ...And restore them when test ends.
|
||||
function restoreLeftPaneGetters() {
|
||||
for (let [key, getter] of leftPaneGetters) {
|
||||
Object.defineProperty(PlacesUIUtils, key, {
|
||||
enumerable: true, configurable: true, get: getter
|
||||
});
|
||||
}
|
||||
}
|
||||
registerCleanupFunction(restoreLeftPaneGetters);
|
||||
|
||||
function openLibrary(callback, aLeftPaneRoot) {
|
||||
let library = window.openDialog("chrome://browser/content/places/places.xul",
|
||||
"", "chrome,toolbar=yes,dialog=no,resizable",
|
||||
|
@ -37,58 +37,41 @@
|
||||
* Bug 510634 - Wrong icons on bookmarks sidebar
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=510634
|
||||
*
|
||||
* Ensures that properties for special queries are set on their tree nodes,
|
||||
* even if PlacesUIUtils.leftPaneFolderId was not initialized.
|
||||
* Ensures that properties for special queries are set on their tree nodes.
|
||||
*/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTest() {
|
||||
// We need to cache and restore the getters in order to simulate
|
||||
// Bug 510634.
|
||||
let leftPaneGetters = new Map([["leftPaneFolderId", null]]);
|
||||
for (let [key, val] of leftPaneGetters) {
|
||||
if (!val) {
|
||||
let getter = Object.getOwnPropertyDescriptor(PlacesUIUtils, key).get;
|
||||
if (typeof getter == "function") {
|
||||
leftPaneGetters.set(key, getter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function restoreLeftPaneGetters() {
|
||||
for (let [key, getter] of leftPaneGetters) {
|
||||
Object.defineProperty(PlacesUIUtils, key, {
|
||||
enumerable: true, configurable: true, get: getter
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let leftPaneFolderId = PlacesUIUtils.leftPaneFolderId;
|
||||
restoreLeftPaneGetters();
|
||||
|
||||
// Setup the places tree contents.
|
||||
let tree = document.getElementById("tree");
|
||||
tree.place = "place:queryType=1&folder=" + leftPaneFolderId;
|
||||
tree.place = `place:type=${Ci.nsINavHistoryQueryOptions.RESULTS_AS_LEFT_PANE_QUERY}&excludeItems=1&expandQueries=0`;
|
||||
|
||||
// The query-property is set on the title column for each row.
|
||||
let titleColumn = tree.treeBoxObject.columns.getColumnAt(0);
|
||||
|
||||
// Open All Bookmarks
|
||||
tree.selectItems([PlacesUIUtils.leftPaneQueries["AllBookmarks"]]);
|
||||
tree.selectItems([PlacesUtils.virtualAllBookmarksGuid]);
|
||||
PlacesUtils.asContainer(tree.selectedNode).containerOpen = true;
|
||||
is(tree.selectedNode.uri,
|
||||
"place:type=" + Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY +
|
||||
"&queryType=" + Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS,
|
||||
"Opened All Bookmarks");
|
||||
|
||||
for (let queryName of ["History", "Downloads", "Tags", "AllBookmarks"]) {
|
||||
const topLevelGuids = [
|
||||
PlacesUtils.virtualHistoryGuid,
|
||||
PlacesUtils.virtualDownloadsGuid,
|
||||
PlacesUtils.virtualTagsGuid,
|
||||
PlacesUtils.virtualAllBookmarksGuid
|
||||
];
|
||||
|
||||
for (let queryName of topLevelGuids) {
|
||||
let found = false;
|
||||
for (let i = 0; i < tree.view.rowCount && !found; i++) {
|
||||
rowProperties = tree.view.getCellProperties(i, titleColumn).split(" ");
|
||||
found = rowProperties.includes("OrganizerQuery_" + queryName);
|
||||
}
|
||||
ok(found, "OrganizerQuery_" + queryName + " is set");
|
||||
ok(found, `OrganizerQuery_${queryName} is set`);
|
||||
}
|
||||
|
||||
const folderGuids = [
|
||||
@ -103,15 +86,12 @@
|
||||
rowProperties = tree.view.getCellProperties(i, titleColumn).split(" ");
|
||||
found = rowProperties.includes("queryFolder_" + guid);
|
||||
}
|
||||
ok(found, "queryFolder_" + guid + " is set");
|
||||
ok(found, `queryFolder_${guid} is set`);
|
||||
}
|
||||
|
||||
// Close the root node
|
||||
tree.result.root.containerOpen = false;
|
||||
|
||||
// Restore the getters for the next test.
|
||||
restoreLeftPaneGetters();
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
@ -20,9 +20,6 @@ XPCOMUtils.defineLazyGetter(this, "PlacesUIUtils", function() {
|
||||
return PlacesUIUtils;
|
||||
});
|
||||
|
||||
const ORGANIZER_FOLDER_ANNO = "PlacesOrganizer/OrganizerFolder";
|
||||
const ORGANIZER_QUERY_ANNO = "PlacesOrganizer/OrganizerQuery";
|
||||
|
||||
// Needed by some test that relies on having an app registered.
|
||||
ChromeUtils.import("resource://testing-common/AppInfo.jsm", this);
|
||||
updateAppInfo({
|
||||
|
@ -1,149 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Tests that we build a working leftpane in various corruption situations.
|
||||
*/
|
||||
|
||||
// Used to store the original leftPaneFolderId getter.
|
||||
var gLeftPaneFolderIdGetter;
|
||||
// Used to store the original left Pane status as a JSON string.
|
||||
var gReferenceHierarchy;
|
||||
var gLeftPaneFolderId;
|
||||
|
||||
add_task(async function() {
|
||||
// We want empty roots.
|
||||
await PlacesUtils.bookmarks.eraseEverything();
|
||||
|
||||
// Sanity check.
|
||||
Assert.ok(!!PlacesUIUtils);
|
||||
|
||||
// Check getters.
|
||||
gLeftPaneFolderIdGetter = Object.getOwnPropertyDescriptor(PlacesUIUtils, "leftPaneFolderId");
|
||||
Assert.equal(typeof(gLeftPaneFolderIdGetter.get), "function");
|
||||
|
||||
registerCleanupFunction(() => PlacesUtils.bookmarks.eraseEverything());
|
||||
});
|
||||
|
||||
add_task(async function() {
|
||||
// Add a third party bogus annotated item. Should not be removed.
|
||||
let folder = await PlacesUtils.bookmarks.insert({
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
title: "test",
|
||||
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER
|
||||
});
|
||||
|
||||
let folderId = await PlacesUtils.promiseItemId(folder.guid);
|
||||
PlacesUtils.annotations.setItemAnnotation(folderId, ORGANIZER_QUERY_ANNO,
|
||||
"test", 0,
|
||||
PlacesUtils.annotations.EXPIRE_NEVER);
|
||||
|
||||
// Create the left pane, and store its current status, it will be used
|
||||
// as reference value.
|
||||
gLeftPaneFolderId = PlacesUIUtils.leftPaneFolderId;
|
||||
gReferenceHierarchy = folderIdToHierarchy(gLeftPaneFolderId);
|
||||
|
||||
while (gTests.length) {
|
||||
// Run current test.
|
||||
await gTests.shift()();
|
||||
|
||||
// Regenerate getters.
|
||||
Object.defineProperty(PlacesUIUtils, "leftPaneFolderId", gLeftPaneFolderIdGetter);
|
||||
gLeftPaneFolderId = PlacesUIUtils.leftPaneFolderId;
|
||||
|
||||
// Check the new left pane folder.
|
||||
let leftPaneHierarchy = folderIdToHierarchy(gLeftPaneFolderId);
|
||||
Assert.equal(gReferenceHierarchy, leftPaneHierarchy);
|
||||
|
||||
folder = await PlacesUtils.bookmarks.fetch({guid: folder.guid});
|
||||
Assert.equal(folder.title, "test");
|
||||
}
|
||||
});
|
||||
|
||||
// Corruption cases.
|
||||
var gTests = [
|
||||
|
||||
function test1() {
|
||||
print("1. Do nothing, checks test calibration.");
|
||||
},
|
||||
|
||||
async function test2() {
|
||||
print("2. Delete the left pane folder.");
|
||||
let guid = await PlacesUtils.promiseItemGuid(gLeftPaneFolderId);
|
||||
await PlacesUtils.bookmarks.remove(guid);
|
||||
},
|
||||
|
||||
async function test3() {
|
||||
print("3. Delete a child of the left pane folder.");
|
||||
let guid = await PlacesUtils.promiseItemGuid(gLeftPaneFolderId);
|
||||
let bm = await PlacesUtils.bookmarks.fetch({parentGuid: guid, index: 0});
|
||||
await PlacesUtils.bookmarks.remove(bm.guid);
|
||||
},
|
||||
|
||||
async function test4() {
|
||||
print("4. Create a duplicated left pane folder.");
|
||||
let folder = await PlacesUtils.bookmarks.insert({
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
title: "PlacesRoot",
|
||||
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER
|
||||
});
|
||||
|
||||
let folderId = await PlacesUtils.promiseItemId(folder.guid);
|
||||
PlacesUtils.annotations.setItemAnnotation(folderId, ORGANIZER_FOLDER_ANNO,
|
||||
"PlacesRoot", 0,
|
||||
PlacesUtils.annotations.EXPIRE_NEVER);
|
||||
},
|
||||
|
||||
async function test5() {
|
||||
print("5. Create a duplicated left pane query.");
|
||||
let folder = await PlacesUtils.bookmarks.insert({
|
||||
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
title: "AllBookmarks",
|
||||
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
|
||||
type: PlacesUtils.bookmarks.TYPE_FOLDER
|
||||
});
|
||||
|
||||
let folderId = await PlacesUtils.promiseItemId(folder.guid);
|
||||
PlacesUtils.annotations.setItemAnnotation(folderId, ORGANIZER_QUERY_ANNO,
|
||||
"AllBookmarks", 0,
|
||||
PlacesUtils.annotations.EXPIRE_NEVER);
|
||||
},
|
||||
|
||||
function test6() {
|
||||
print("6. Remove the left pane folder annotation.");
|
||||
PlacesUtils.annotations.removeItemAnnotation(gLeftPaneFolderId,
|
||||
ORGANIZER_FOLDER_ANNO);
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Convert a folder item id to a JSON representation of it and its contents.
|
||||
*/
|
||||
function folderIdToHierarchy(aFolderId) {
|
||||
let root = PlacesUtils.getFolderContents(aFolderId).root;
|
||||
let hier = JSON.stringify(hierarchyToObj(root));
|
||||
root.containerOpen = false;
|
||||
return hier;
|
||||
}
|
||||
|
||||
function hierarchyToObj(aNode) {
|
||||
let o = {};
|
||||
o.title = aNode.title;
|
||||
o.annos = PlacesUtils.getAnnotationsForItem(aNode.itemId);
|
||||
if (PlacesUtils.nodeIsURI(aNode)) {
|
||||
o.uri = aNode.uri;
|
||||
} else if (PlacesUtils.nodeIsFolder(aNode)) {
|
||||
o.children = [];
|
||||
PlacesUtils.asContainer(aNode).containerOpen = true;
|
||||
for (let i = 0; i < aNode.childCount; ++i) {
|
||||
o.children.push(hierarchyToObj(aNode.getChild(i)));
|
||||
}
|
||||
aNode.containerOpen = false;
|
||||
}
|
||||
return o;
|
||||
}
|
@ -20,5 +20,4 @@ support-files =
|
||||
[test_browserGlue_smartBookmarks.js]
|
||||
[test_browserGlue_urlbar_defaultbehavior_migration.js]
|
||||
[test_clearHistory_shutdown.js]
|
||||
[test_leftpane_corruption_handling.js]
|
||||
[test_PUIU_batchUpdatesForNode.js]
|
||||
|
@ -62,11 +62,6 @@ detailsPane.itemsCountLabel=One item;#1 items
|
||||
mostVisitedTitle=Most Visited
|
||||
recentTagsTitle=Recent Tags
|
||||
|
||||
OrganizerQueryHistory=History
|
||||
OrganizerQueryDownloads=Downloads
|
||||
OrganizerQueryAllBookmarks=All Bookmarks
|
||||
OrganizerQueryTags=Tags
|
||||
|
||||
# LOCALIZATION NOTE (tagResultLabel, bookmarkResultLabel, switchtabResultLabel,
|
||||
# keywordResultLabel, searchengineResultLabel)
|
||||
# Noun used to describe the location bar autocomplete result type
|
||||
|
@ -58,16 +58,16 @@ treechildren::-moz-tree-image(query) {
|
||||
list-style-image: url("chrome://browser/skin/places/folder-smart.svg");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(query, OrganizerQuery_AllBookmarks) {
|
||||
treechildren::-moz-tree-image(query, OrganizerQuery_allbms_____v) {
|
||||
list-style-image: url("chrome://browser/skin/places/allBookmarks.png");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(query, OrganizerQuery_Downloads) {
|
||||
treechildren::-moz-tree-image(query, OrganizerQuery_downloads__v) {
|
||||
list-style-image: url("chrome://browser/skin/places/downloads.png");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(title, query, tagContainer),
|
||||
treechildren::-moz-tree-image(query, OrganizerQuery_Tags) {
|
||||
treechildren::-moz-tree-image(query, OrganizerQuery_tags_______v) {
|
||||
list-style-image: url("chrome://browser/skin/places/tag.png");
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ treechildren::-moz-tree-image(title, query, hostContainer) {
|
||||
list-style-image: url("chrome://browser/skin/places/folder.svg");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(query, OrganizerQuery_History) {
|
||||
treechildren::-moz-tree-image(query, OrganizerQuery_history____v) {
|
||||
list-style-image: url("chrome://browser/skin/places/history.svg");
|
||||
}
|
||||
|
||||
|
@ -22,16 +22,8 @@ Cu.importGlobalProperties(["URLSearchParams"]);
|
||||
|
||||
var EXPORTED_SYMBOLS = ["BookmarkValidator", "BookmarkProblemData"];
|
||||
|
||||
const LEFT_PANE_ROOT_ANNO = "PlacesOrganizer/OrganizerFolder";
|
||||
const LEFT_PANE_QUERY_ANNO = "PlacesOrganizer/OrganizerQuery";
|
||||
const QUERY_PROTOCOL = "place:";
|
||||
|
||||
// Indicates if a local bookmark tree node should be excluded from syncing.
|
||||
function isNodeIgnored(treeNode) {
|
||||
return treeNode.annos && treeNode.annos.some(anno => anno.name == LEFT_PANE_ROOT_ANNO ||
|
||||
anno.name == LEFT_PANE_QUERY_ANNO);
|
||||
}
|
||||
|
||||
function areURLsEqual(a, b) {
|
||||
if (a === b) {
|
||||
return true;
|
||||
@ -653,8 +645,6 @@ class BookmarkValidator {
|
||||
await this.maybeYield();
|
||||
if (!synced) {
|
||||
synced = syncedRoots.includes(treeNode.guid);
|
||||
} else if (isNodeIgnored(treeNode)) {
|
||||
synced = false;
|
||||
}
|
||||
let localId = treeNode.id;
|
||||
let guid = PlacesSyncUtils.bookmarks.guidToRecordId(treeNode.guid);
|
||||
|
@ -285,11 +285,6 @@ add_task(async function test_cswc_serverUnexpected() {
|
||||
"flags": 0,
|
||||
"expires": 4,
|
||||
"value": 1
|
||||
}, {
|
||||
"name": "PlacesOrganizer/OrganizerFolder",
|
||||
"flags": 0,
|
||||
"expires": 4,
|
||||
"value": 7
|
||||
}],
|
||||
"type": "text/x-moz-place-container",
|
||||
"children": [{
|
||||
@ -300,11 +295,6 @@ add_task(async function test_cswc_serverUnexpected() {
|
||||
"flags": 0,
|
||||
"expires": 4,
|
||||
"value": 1
|
||||
}, {
|
||||
"name": "PlacesOrganizer/OrganizerQuery",
|
||||
"flags": 0,
|
||||
"expires": 4,
|
||||
"value": "History"
|
||||
}],
|
||||
"type": "text/x-moz-place",
|
||||
"uri": "place:type=3&sort=4"
|
||||
|
@ -152,8 +152,8 @@ var Bookmarks = Object.freeze({
|
||||
userContentRoots: ["toolbar_____", "menu________", "unfiled_____", "mobile______"],
|
||||
|
||||
/**
|
||||
* GUIDs associated with virtual queries that are used for display in the left
|
||||
* pane.
|
||||
* GUIDs associated with virtual queries that are used for displaying bookmark
|
||||
* folders in the left pane.
|
||||
*/
|
||||
virtualMenuGuid: "menu_______v",
|
||||
virtualToolbarGuid: "toolbar____v",
|
||||
|
@ -118,7 +118,8 @@ function serializeNode(aNode, aIsLivemark) {
|
||||
|
||||
// Some nodes, e.g. the unfiled/menu/toolbar ones can have a virtual guid, so
|
||||
// we ignore any that are a folder shortcut. These will be handled below.
|
||||
if (guid && !PlacesUtils.bookmarks.isVirtualRootItem(guid)) {
|
||||
if (guid && !PlacesUtils.bookmarks.isVirtualRootItem(guid) &&
|
||||
!PlacesUtils.isVirtualLeftPaneItem(guid)) {
|
||||
// TODO: Really guid should be set on everything, however currently this upsets
|
||||
// the drag 'n' drop / cut/copy/paste operations.
|
||||
data.itemGuid = guid;
|
||||
@ -343,6 +344,29 @@ var PlacesUtils = {
|
||||
|
||||
ACTION_SCHEME: "moz-action:",
|
||||
|
||||
/**
|
||||
* GUIDs associated with virtual queries that are used for displaying the
|
||||
* top-level folders in the left pane.
|
||||
*/
|
||||
virtualAllBookmarksGuid: "allbms_____v",
|
||||
virtualHistoryGuid: "history____v",
|
||||
virtualDownloadsGuid: "downloads__v",
|
||||
virtualTagsGuid: "tags_______v",
|
||||
|
||||
/**
|
||||
* Checks if a guid is a virtual left-pane root.
|
||||
*
|
||||
* @param {String} guid The guid of the item to look for.
|
||||
* @returns {Boolean} true if guid is a virtual root, false otherwise.
|
||||
*/
|
||||
isVirtualLeftPaneItem(guid) {
|
||||
return guid == PlacesUtils.virtualAllBookmarksGuid ||
|
||||
guid == PlacesUtils.virtualHistoryGuid ||
|
||||
guid == PlacesUtils.virtualDownloadsGuid ||
|
||||
guid == PlacesUtils.virtualTagsGuid;
|
||||
},
|
||||
|
||||
|
||||
asContainer: aNode => asContainer(aNode),
|
||||
asQuery: aNode => asQuery(aNode),
|
||||
|
||||
|
@ -1079,6 +1079,11 @@ interface nsINavHistoryQueryOptions : nsISupports
|
||||
*/
|
||||
const unsigned short RESULTS_AS_ROOTS_QUERY = 8;
|
||||
|
||||
/**
|
||||
* This returns nsINavHistoryQueryResultNode for each left-pane root.
|
||||
*/
|
||||
const unsigned short RESULTS_AS_LEFT_PANE_QUERY = 9;
|
||||
|
||||
/**
|
||||
* The sorting mode to be used for this query.
|
||||
* mode is one of SORT_BY_*
|
||||
@ -1139,7 +1144,7 @@ interface nsINavHistoryQueryOptions : nsISupports
|
||||
attribute boolean includeHidden;
|
||||
|
||||
/**
|
||||
* This is the maximum number of results that you want. The query is exeucted,
|
||||
* This is the maximum number of results that you want. The query is executed,
|
||||
* the results are sorted, and then the top 'maxResults' results are taken
|
||||
* and returned. Set to 0 (the default) to get all results.
|
||||
*
|
||||
|
@ -780,6 +780,10 @@ nsNavHistory::NormalizeTime(uint32_t aRelative, PRTime aOffset)
|
||||
// QUERYUPDATE_COMPLEX_WITH_BOOKMARKS:
|
||||
// A complex query that additionally has dependence on bookmarks. All
|
||||
// bookmark-dependent queries fall under this category.
|
||||
// QUERYUPDATE_MOBILEPREF:
|
||||
// A complex query but only updates when the mobile preference changes.
|
||||
// QUERYUPDATE_NONE:
|
||||
// A query that never updates, e.g. the left-pane root query.
|
||||
//
|
||||
// aHasSearchTerms will be set to true if the query has any dependence on
|
||||
// keywords. When there is no dependence on keywords, we can handle title
|
||||
@ -835,6 +839,10 @@ nsNavHistory::GetUpdateRequirements(const nsCOMArray<nsNavHistoryQuery>& aQuerie
|
||||
nsINavHistoryQueryOptions::RESULTS_AS_ROOTS_QUERY)
|
||||
return QUERYUPDATE_MOBILEPREF;
|
||||
|
||||
if (aOptions->ResultType() ==
|
||||
nsINavHistoryQueryOptions::RESULTS_AS_LEFT_PANE_QUERY)
|
||||
return QUERYUPDATE_NONE;
|
||||
|
||||
// Whenever there is a maximum number of results,
|
||||
// and we are not a bookmark query we must requery. This
|
||||
// is because we can't generally know if any given addition/change causes
|
||||
@ -1416,6 +1424,7 @@ private:
|
||||
nsresult SelectAsSite();
|
||||
nsresult SelectAsTag();
|
||||
nsresult SelectAsRoots();
|
||||
nsresult SelectAsLeftPane();
|
||||
|
||||
nsresult Where();
|
||||
nsresult GroupBy();
|
||||
@ -1522,6 +1531,11 @@ PlacesSQLQueryBuilder::Select()
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
break;
|
||||
|
||||
case nsINavHistoryQueryOptions::RESULTS_AS_LEFT_PANE_QUERY:
|
||||
rv = SelectAsLeftPane();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
break;
|
||||
|
||||
default:
|
||||
NS_NOTREACHED("Invalid result type");
|
||||
}
|
||||
@ -1975,6 +1989,48 @@ PlacesSQLQueryBuilder::SelectAsRoots()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PlacesSQLQueryBuilder::SelectAsLeftPane()
|
||||
{
|
||||
nsNavHistory *history = nsNavHistory::GetHistoryService();
|
||||
NS_ENSURE_STATE(history);
|
||||
|
||||
nsAutoCString historyTitle;
|
||||
nsAutoCString downloadsTitle;
|
||||
nsAutoCString tagsTitle;
|
||||
nsAutoCString allBookmarksTitle;
|
||||
|
||||
history->GetStringFromName("OrganizerQueryHistory", historyTitle);
|
||||
history->GetStringFromName("OrganizerQueryDownloads", downloadsTitle);
|
||||
history->GetStringFromName("TagsFolderTitle", tagsTitle);
|
||||
history->GetStringFromName("OrganizerQueryAllBookmarks", allBookmarksTitle);
|
||||
|
||||
mQueryString = nsPrintfCString(
|
||||
"SELECT * FROM ("
|
||||
"VALUES"
|
||||
"(null, 'place:type=%d&sort=%d', '%s', null, null, null, "
|
||||
"null, null, 0, 0, null, null, null, null, 'history____v', null), "
|
||||
"(null, 'place:transition=%d&sort=%d', '%s', null, null, null, "
|
||||
"null, null, 0, 0, null, null, null, null, 'downloads__v', null), "
|
||||
"(null, 'place:type=%d&sort=%d', '%s', null, null, null, "
|
||||
"null, null, 0, 0, null, null, null, null, 'tags_______v', null), "
|
||||
"(null, 'place:type=%d', '%s', null, null, null, "
|
||||
"null, null, 0, 0, null, null, null, null, 'allbms_____v', null) "
|
||||
")",
|
||||
nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY,
|
||||
nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING,
|
||||
historyTitle.get(),
|
||||
nsINavHistoryService::TRANSITION_DOWNLOAD,
|
||||
nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING,
|
||||
downloadsTitle.get(),
|
||||
nsINavHistoryQueryOptions::RESULTS_AS_TAG_QUERY,
|
||||
nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING,
|
||||
tagsTitle.get(),
|
||||
nsINavHistoryQueryOptions::RESULTS_AS_ROOTS_QUERY,
|
||||
allBookmarksTitle.get());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PlacesSQLQueryBuilder::Where()
|
||||
{
|
||||
@ -3834,7 +3890,8 @@ nsNavHistory::RowToResult(mozIStorageValueArray* aRow,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_ROOTS_QUERY) {
|
||||
if (aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_ROOTS_QUERY ||
|
||||
aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_LEFT_PANE_QUERY) {
|
||||
rv = aRow->GetUTF8String(kGetInfoIndex_Guid, guid);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
@ -40,6 +40,7 @@
|
||||
#define QUERYUPDATE_COMPLEX_WITH_BOOKMARKS 3
|
||||
#define QUERYUPDATE_HOST 4
|
||||
#define QUERYUPDATE_MOBILEPREF 5
|
||||
#define QUERYUPDATE_NONE 6
|
||||
|
||||
// Clamp title and URL to generously large, but not too large, length.
|
||||
// See bug 319004 for details.
|
||||
|
@ -1359,12 +1359,12 @@ nsNavHistoryQueryOptions::GetResultType(uint16_t* aType)
|
||||
NS_IMETHODIMP
|
||||
nsNavHistoryQueryOptions::SetResultType(uint16_t aType)
|
||||
{
|
||||
if (aType > RESULTS_AS_ROOTS_QUERY)
|
||||
if (aType > RESULTS_AS_LEFT_PANE_QUERY)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
// Tag queries, containers and the roots query are bookmarks related, so we
|
||||
// set the QueryType accordingly.
|
||||
if (aType == RESULTS_AS_TAG_QUERY || aType == RESULTS_AS_TAG_CONTENTS ||
|
||||
aType == RESULTS_AS_ROOTS_QUERY)
|
||||
aType == RESULTS_AS_ROOTS_QUERY || aType == RESULTS_AS_LEFT_PANE_QUERY)
|
||||
mQueryType = QUERY_TYPE_BOOKMARKS;
|
||||
mResultType = aType;
|
||||
return NS_OK;
|
||||
@ -1467,7 +1467,9 @@ nsNavHistoryQueryOptions::SetQueryType(uint16_t aQueryType)
|
||||
// Tag query and containers are forced to QUERY_TYPE_BOOKMARKS when the
|
||||
// resultType is set.
|
||||
if (mResultType == RESULTS_AS_TAG_CONTENTS ||
|
||||
mResultType == RESULTS_AS_TAG_QUERY)
|
||||
mResultType == RESULTS_AS_TAG_QUERY ||
|
||||
mResultType == RESULTS_AS_LEFT_PANE_QUERY ||
|
||||
mResultType == RESULTS_AS_ROOTS_QUERY)
|
||||
return NS_OK;
|
||||
mQueryType = aQueryType;
|
||||
return NS_OK;
|
||||
|
@ -1813,7 +1813,8 @@ nsNavHistoryQueryResultNode::IsContainersQuery()
|
||||
resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_SITE_QUERY ||
|
||||
resultType == nsINavHistoryQueryOptions::RESULTS_AS_TAG_QUERY ||
|
||||
resultType == nsINavHistoryQueryOptions::RESULTS_AS_SITE_QUERY ||
|
||||
resultType == nsINavHistoryQueryOptions::RESULTS_AS_ROOTS_QUERY;
|
||||
resultType == nsINavHistoryQueryOptions::RESULTS_AS_ROOTS_QUERY ||
|
||||
resultType == nsINavHistoryQueryOptions::RESULTS_AS_LEFT_PANE_QUERY;
|
||||
}
|
||||
|
||||
|
||||
@ -1886,7 +1887,8 @@ nsNavHistoryQueryResultNode::GetHasChildren(bool* aHasChildren)
|
||||
// Tags are always populated, otherwise they are removed.
|
||||
if (resultType == nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS ||
|
||||
// AllBookmarks also always has children.
|
||||
resultType == nsINavHistoryQueryOptions::RESULTS_AS_ROOTS_QUERY) {
|
||||
resultType == nsINavHistoryQueryOptions::RESULTS_AS_ROOTS_QUERY ||
|
||||
resultType == nsINavHistoryQueryOptions::RESULTS_AS_LEFT_PANE_QUERY) {
|
||||
*aHasChildren = true;
|
||||
return NS_OK;
|
||||
}
|
||||
@ -2112,6 +2114,13 @@ nsNavHistoryQueryResultNode::FillChildren()
|
||||
mChildren.RemoveObjectAt(mChildren.Count() - 1);
|
||||
}
|
||||
|
||||
// If we're not updating the query, we don't need to add listeners, so bail
|
||||
// out early.
|
||||
if (mLiveUpdate == QUERYUPDATE_NONE) {
|
||||
mContentsValid = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsNavHistoryResult* result = GetResult();
|
||||
NS_ENSURE_STATE(result);
|
||||
|
||||
@ -2196,12 +2205,21 @@ nsNavHistoryQueryResultNode::Refresh()
|
||||
// containing other queries. In this case calling Refresh for each child
|
||||
// query could cause a major slowdown. We should not refresh nested
|
||||
// queries, since we will already refresh the parent one.
|
||||
if (!mExpanded ||
|
||||
(mParent && mParent->IsQuery() &&
|
||||
mParent->GetAsQuery()->IsContainersQuery())) {
|
||||
// Don't update, just invalidate and unhook
|
||||
// The only exception to this, is if the parent query is of QUERYUPDATE_NONE,
|
||||
// this can be the case for the RESULTS_AS_TAG_QUERY
|
||||
// under RESULTS_AS_LEFT_PANE_QUERY.
|
||||
if (!mExpanded) {
|
||||
ClearChildren(true);
|
||||
return NS_OK; // no updates in tree state
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mParent && mParent->IsQuery()) {
|
||||
nsNavHistoryQueryResultNode* parent = mParent->GetAsQuery();
|
||||
if (parent->IsContainersQuery() && parent->mLiveUpdate != QUERYUPDATE_NONE) {
|
||||
// Don't update, just invalidate and unhook
|
||||
ClearChildren(true);
|
||||
return NS_OK; // no updates in tree state
|
||||
}
|
||||
}
|
||||
|
||||
if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS)
|
||||
|
@ -0,0 +1,64 @@
|
||||
"use strict";
|
||||
|
||||
const MOBILE_BOOKMARKS_PREF = "browser.bookmarks.showMobileBookmarks";
|
||||
|
||||
const expectedRoots = [{
|
||||
title: "OrganizerQueryHistory",
|
||||
uri: `place:sort=${Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING}&type=${Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY}`,
|
||||
guid: "history____v",
|
||||
}, {
|
||||
title: "OrganizerQueryDownloads",
|
||||
uri: `place:transition=${Ci.nsINavHistoryService.TRANSITION_DOWNLOAD}&sort=${Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING}`,
|
||||
guid: "downloads__v",
|
||||
}, {
|
||||
title: "TagsFolderTitle",
|
||||
uri: `place:sort=${Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING}&type=${Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY}&queryType=1`,
|
||||
guid: "tags_______v",
|
||||
}, {
|
||||
title: "OrganizerQueryAllBookmarks",
|
||||
uri: `place:type=${Ci.nsINavHistoryQueryOptions.RESULTS_AS_ROOTS_QUERY}&queryType=1`,
|
||||
guid: "allbms_____v",
|
||||
}];
|
||||
|
||||
const placesStrings = Services.strings.createBundle("chrome://places/locale/places.properties");
|
||||
|
||||
function getLeftPaneQuery() {
|
||||
var query = PlacesUtils.history.getNewQuery();
|
||||
|
||||
// Options
|
||||
var options = PlacesUtils.history.getNewQueryOptions();
|
||||
options.resultType = options.RESULTS_AS_LEFT_PANE_QUERY;
|
||||
|
||||
// Results
|
||||
var result = PlacesUtils.history.executeQuery(query, options);
|
||||
return result.root;
|
||||
}
|
||||
|
||||
function assertExpectedChildren(root, expectedChildren) {
|
||||
Assert.equal(root.childCount, expectedChildren.length, "Should have the expected number of children.");
|
||||
|
||||
for (let i = 0; i < root.childCount; i++) {
|
||||
Assert.equal(root.getChild(i).uri, expectedChildren[i].uri,
|
||||
"Should have the correct uri for root ${i}");
|
||||
Assert.equal(root.getChild(i).title, placesStrings.GetStringFromName(expectedChildren[i].title),
|
||||
"Should have the correct title for root ${i}");
|
||||
Assert.equal(root.getChild(i).bookmarkGuid, expectedChildren[i].guid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This test will test the basic RESULTS_AS_ROOTS_QUERY, that simply returns,
|
||||
* the existing bookmark roots.
|
||||
*/
|
||||
add_task(async function test_results_as_root() {
|
||||
let root = getLeftPaneQuery();
|
||||
root.containerOpen = true;
|
||||
|
||||
Assert.equal(PlacesUtils.asQuery(root).queryOptions.queryType,
|
||||
Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS,
|
||||
"Should have a query type of QUERY_TYPE_BOOKMARKS");
|
||||
|
||||
assertExpectedChildren(root, expectedRoots);
|
||||
|
||||
root.containerOpen = false;
|
||||
});
|
@ -17,6 +17,7 @@ skip-if = (os == 'win' && ccov) # Bug 1423667
|
||||
[test_queryMultipleFolder.js]
|
||||
[test_querySerialization.js]
|
||||
[test_redirects.js]
|
||||
[test_results-as-left-pane.js]
|
||||
[test_results-as-roots.js]
|
||||
[test_results-as-tag-contents-query.js]
|
||||
[test_results-as-visit.js]
|
||||
|
@ -7,6 +7,9 @@ BookmarksToolbarFolderTitle=Bookmarks Toolbar
|
||||
OtherBookmarksFolderTitle=Other Bookmarks
|
||||
TagsFolderTitle=Tags
|
||||
MobileBookmarksFolderTitle=Mobile Bookmarks
|
||||
OrganizerQueryHistory=History
|
||||
OrganizerQueryDownloads=Downloads
|
||||
OrganizerQueryAllBookmarks=All Bookmarks
|
||||
|
||||
# LOCALIZATION NOTE (dateName):
|
||||
# These are used to generate history containers when history is grouped by date
|
||||
|
Loading…
Reference in New Issue
Block a user