--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -1393,28 +1393,17 @@ var BookmarkingUI = {
insertionPoint: ".panel-subview-footer"
});
},
// Set by sync after syncing bookmarks successfully once.
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) {
mobileMenuItem.hidden = !this._shouldShowMobileBookmarks();
},
_uninitView: function BUI__uninitView() {
// When an element with a placesView attached is removed and re-inserted,
--- a/browser/components/places/PlacesUIUtils.jsm
+++ b/browser/components/places/PlacesUIUtils.jsm
@@ -201,20 +201,16 @@ let InternalFaviconLoader = {
this._removeLoadDataFromWindowMap(win, loadData);
}, FAVICON_REQUEST_TIMEOUT);
let loadDataForWindow = gFaviconLoadDataMap.get(win);
loadDataForWindow.push(loadData);
},
};
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",
/**
* Makes a URI from a spec, and do fixup
* @param aSpec
* The string spec of the URI
* @return A URI object for the spec.
@@ -526,21 +522,26 @@ var PlacesUIUtils = {
canUserRemove(aNode, aView) {
let parentNode = aNode.parent;
if (!parentNode) {
// canUserRemove doesn't accept root nodes.
return false;
}
// 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;
}
}
// If it's not a bookmark, we can remove it unless it's a child of a
// livemark.
if (aNode.itemId == -1) {
// Rather than executing a db query, checking the existence of the feedURI
@@ -585,31 +586,17 @@ var PlacesUIUtils = {
if (!view || typeof view != "object") {
throw new Error("invalid value for aView");
}
let itemId = PlacesUtils.getConcreteItemId(placesNode);
if (itemId == PlacesUtils.placesRootId ||
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:
* {uri: string, isBookmark: boolean}
*/
_openTabset: function PUIU__openTabset(aItemsToOpen, aEvent, aWindow) {
if (!aItemsToOpen.length)
return;
@@ -809,271 +796,16 @@ var PlacesUIUtils = {
title = "";
}
} else
title = aNode.title;
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";
return weaveOK;
},
/**
* WARNING TO ADDON AUTHORS: DO NOT USE THIS METHOD. IT'S LIKELY TO BE REMOVED IN A
@@ -1349,24 +1081,17 @@ function canMoveUnwrappedNode(unwrappedN
let parentGuid = unwrappedNode.parentGuid;
// If there's no parent Guid, this was likely a virtual query that returns
// bookmarks, such as a tags query.
if (!parentGuid ||
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;
}
/**
* This gets the most appropriate item for using for batching. In the case of multiple
* views being related, the method returns the most expensive result to batch.
* For example, if it detects the left-hand library pane, then it will look for
* and return the reference to the right-hand pane.
--- a/browser/components/places/content/editBookmarkOverlay.js
+++ b/browser/components/places/content/editBookmarkOverlay.js
@@ -51,22 +51,16 @@ var gEditItemOverlay = {
let parentGuid = null;
if (node && isItem) {
if (!node.parent || (node.parent.itemId > 0 && !node.parent.bookmarkGuid)) {
throw new Error("Cannot use an incomplete node to initialize the edit bookmark panel");
}
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;
}
let focusedElement = aInitInfo.focusedElement;
let onPanelReady = aInitInfo.onPanelReady;
return this._paneInfo = { itemId, itemGuid, parentId, parentGuid, isItem,
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -31,55 +31,57 @@ var PlacesOrganizer = {
// observing additionalInfoBroadcaster.
_additionalInfoFields: [
"editBMPanel_descriptionRow",
"editBMPanel_loadInSidebarCheckbox",
"editBMPanel_keywordRow",
],
_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`;
},
/**
* Selects a left pane built-in item.
*
* @param {String} item The built-in item to select, may be one of (case sensitive):
* AllBookmarks, BookmarksMenu, BookmarksToolbar,
* History, Downloads, Tags, UnfiledBookmarks.
*/
selectLeftPaneBuiltIn(item) {
switch (item) {
case "AllBookmarks":
+ 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":
- 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.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;
default:
throw new Error(`Unrecognized item ${item} passed to selectLeftPaneRootItem`);
}
},
@@ -87,17 +89,16 @@ var PlacesOrganizer = {
* Opens a given hierarchy in the left pane, stopping at the last reachable
* container. Note: item ids should be considered deprecated.
*
* @param aHierarchy A single container or an array of containers, sorted from
* the outmost to the innermost in the hierarchy. Each
* 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)
throw new Error("Containers hierarchy not specified");
let hierarchy = [].concat(aHierarchy);
let selectWasSuppressed = this._places.view.selection.selectEventsSuppressed;
if (!selectWasSuppressed)
this._places.view.selection.selectEventsSuppressed = true;
@@ -307,22 +308,22 @@ var PlacesOrganizer = {
},
/**
* Sets the search scope based on aNode's properties.
* @param aNode
* 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.
PlacesQueryBuilder.setScope("bookmarks");
}
},
/**
--- a/browser/components/places/content/tree.xml
+++ b/browser/components/places/content/tree.xml
@@ -627,17 +627,17 @@
if (ids.length == 0 || !PlacesUtils.nodeIsContainer(node) ||
checkedGuidsSet.has(concreteGuid))
return foundOne;
// Only follow a query if it has been been explicitly opened by the
// 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)
return foundOne;
checkedGuidsSet.add(concreteGuid);
// Remember the beginning state so that we can re-close
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -133,16 +133,17 @@ PlacesTreeView.prototype = {
return false;
switch (aContainer.queryOptions.resultType) {
case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY:
case Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY:
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;
}
// If it's a folder, it's not a plain container.
let nodeType = aContainer.type;
return nodeType != Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER &&
nodeType != Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT;
},
@@ -1300,21 +1301,23 @@ PlacesTreeView.prototype = {
properties += ` queryFolder_${PlacesUtils.bookmarks.toolbarGuid}`;
break;
case PlacesUtils.bookmarks.virtualMenuGuid:
properties += ` queryFolder_${PlacesUtils.bookmarks.menuGuid}`;
break;
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";
else if (PlacesUtils.nodeIsURI(node)) {
properties += " " + PlacesUIUtils.guessUrlSchemeForUI(node.uri);
if (this._controller.hasCachedLivemarkInfo(node.parent)) {
properties += " livemarkItem";
@@ -1787,25 +1790,16 @@ PlacesTreeView.prototype = {
//
// Note that concrete itemIds aren't used intentionally. For example, we
// have no reason to disallow renaming a shortcut to the Bookmarks Toolbar,
// except for the one under All Bookmarks.
if (PlacesUtils.nodeIsSeparator(node) || PlacesUtils.isRootItem(itemGuid) ||
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;
},
setCellText: function PTV_setCellText(aRow, aColumn, aText) {
// We may only get here if the cell is editable.
let node = this._rows[aRow];
if (node.title != aText) {
PlacesTransactions.EditTitle({ guid: node.bookmarkGuid, title: aText })
--- a/browser/components/places/tests/browser/browser.ini
+++ b/browser/components/places/tests/browser/browser.ini
@@ -74,17 +74,16 @@ skip-if = (os == 'win' && ccov) # Bug 14
skip-if = (os == 'win' && ccov) # Bug 1423667
[browser_library_commands.js]
skip-if = (os == 'win' && ccov) # Bug 1423667
[browser_library_delete_bookmarks_in_tags.js]
[browser_library_delete_tags.js]
[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]
skip-if = (os == 'win' && ccov) # Bug 1423667
[browser_library_middleclick.js]
skip-if = (os == 'win' && ccov) # Bug 1423667
[browser_library_open_leak.js]
[browser_library_openFlatContainer.js]
--- a/browser/components/places/tests/browser/browser_copy_query_without_tree.js
+++ b/browser/components/places/tests/browser/browser_copy_query_without_tree.js
@@ -45,17 +45,17 @@ add_task(async function copy_mobile_shor
let library = await promiseLibrary();
registerCleanupFunction(async () => {
library.close();
await PlacesUtils.bookmarks.eraseEverything();
});
library.PlacesOrganizer.selectLeftPaneContainerByHierarchy([
- PlacesUIUtils.leftPaneQueries.AllBookmarks,
+ PlacesUtils.virtualAllBookmarksGuid,
PlacesUtils.bookmarks.virtualMobileGuid,
]);
await promiseClipboard(function() { library.PlacesOrganizer._places.controller.copy(); },
PlacesUtils.TYPE_X_MOZ_PLACE);
library.PlacesOrganizer.selectLeftPaneBuiltIn("UnfiledBookmarks");
deleted file mode 100644
--- a/browser/components/places/tests/browser/browser_library_left_pane_fixnames.js
+++ /dev/null
@@ -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);
-}
--- a/browser/components/places/tests/browser/browser_library_search.js
+++ b/browser/components/places/tests/browser/browser_library_search.js
@@ -25,124 +25,60 @@
* remains selected.
*/
const TEST_URL = "http://dummy.mozilla.org/";
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");
}
}
// Second, ensure that searching updates the content tree and search UI
// properly.
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");
}
}
add_task(async function test() {
// Add visits, a bookmark and a tag.
await PlacesTestUtils.addVisits(
[{ uri: Services.io.newURI(TEST_URL), visitDate: Date.now() * 1000,
transition: PlacesUtils.history.TRANSITION_TYPED },
@@ -155,17 +91,25 @@ add_task(async function test() {
title: "dummy",
url: TEST_URL,
});
PlacesUtils.tagging.tagURI(Services.io.newURI(TEST_URL), ["dummyTag"]);
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);
// Cleanup.
PlacesUtils.tagging.untagURI(Services.io.newURI(TEST_URL), ["dummyTag"]);
await PlacesUtils.bookmarks.eraseEverything();
await PlacesUtils.history.clear();
--- a/browser/components/places/tests/browser/head.js
+++ b/browser/components/places/tests/browser/head.js
@@ -1,36 +1,15 @@
ChromeUtils.defineModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
ChromeUtils.defineModuleGetter(this, "PlacesTestUtils",
"resource://testing-common/PlacesTestUtils.jsm");
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",
aLeftPaneRoot);
waitForFocus(function() {
callback(library);
}, library);
--- a/browser/components/places/tests/chrome/test_0_bug510634.xul
+++ b/browser/components/places/tests/chrome/test_0_bug510634.xul
@@ -32,89 +32,69 @@
<script type="application/javascript">
<![CDATA[
/**
* 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 = [
PlacesUtils.bookmarks.toolbarGuid,
PlacesUtils.bookmarks.menuGuid,
PlacesUtils.bookmarks.unfiledGuid,
];
for (let guid of folderGuids) {
let found = false;
for (let i = 0; i < tree.view.rowCount && !found; i++) {
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();
}
]]>
</script>
</window>
--- a/browser/components/places/tests/unit/head_bookmarks.js
+++ b/browser/components/places/tests/unit/head_bookmarks.js
@@ -15,19 +15,16 @@ if (commonFile) {
// Put any other stuff relative to this test folder below.
XPCOMUtils.defineLazyGetter(this, "PlacesUIUtils", function() {
ChromeUtils.import("resource:///modules/PlacesUIUtils.jsm");
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({
name: "PlacesTest",
ID: "{230de50e-4cd1-11dc-8314-0800200c9a66}",
version: "1",
platformVersion: "",
});
deleted file mode 100644
--- a/browser/components/places/tests/unit/test_leftpane_corruption_handling.js
+++ /dev/null
@@ -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;
-}
--- a/browser/components/places/tests/unit/xpcshell.ini
+++ b/browser/components/places/tests/unit/xpcshell.ini
@@ -15,10 +15,9 @@ support-files =
[test_browserGlue_corrupt_nobackup_default.js]
[test_browserGlue_distribution.js]
[test_browserGlue_migrate.js]
[test_browserGlue_prefs.js]
[test_browserGlue_restore.js]
[test_browserGlue_smartBookmarks.js]
[test_browserGlue_urlbar_defaultbehavior_migration.js]
[test_clearHistory_shutdown.js]
-[test_leftpane_corruption_handling.js]
[test_PUIU_batchUpdatesForNode.js]
--- a/browser/locales/en-US/chrome/browser/places/places.properties
+++ b/browser/locales/en-US/chrome/browser/places/places.properties
@@ -57,21 +57,16 @@ detailsPane.noItems=No items
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 number of items
# example: 111 items
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
# to users with screen readers
# See createResultLabel() in urlbarBindings.xml
tagResultLabel=Tag
bookmarkResultLabel=Bookmark
switchtabResultLabel=Tab
--- a/browser/themes/shared/places/tree-icons.inc.css
+++ b/browser/themes/shared/places/tree-icons.inc.css
@@ -53,39 +53,39 @@ treechildren::-moz-tree-image(container,
list-style-image: url("chrome://browser/skin/places/unfiledBookmarks.svg");
}
/* query-nodes should be styled even if they're not expandable */
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");
}
/* calendar icon for folders grouping items by date */
treechildren::-moz-tree-image(title, query, dayContainer) {
list-style-image: url("chrome://browser/skin/places/history.svg");
}
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");
}
/* We want some queries to look like ordinary folders. This must come
after the (title, query) selector, or it would get overridden. */
treechildren::-moz-tree-image(title, query, folder) {
list-style-image: url("chrome://browser/skin/places/folder.svg");
}
--- a/services/sync/modules/bookmark_validator.js
+++ b/services/sync/modules/bookmark_validator.js
@@ -17,26 +17,18 @@ ChromeUtils.defineModuleGetter(this, "Pl
ChromeUtils.defineModuleGetter(this, "PlacesSyncUtils",
"resource://gre/modules/PlacesSyncUtils.jsm");
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;
}
if (a.startsWith(QUERY_PROTOCOL) != b.startsWith(QUERY_PROTOCOL)) {
return false;
}
// Tag queries are special because we rewrite them to point to the
@@ -648,18 +640,16 @@ class BookmarkValidator {
// still use local IDs. We use this mapping to parse `place:` queries that
// refer to folders via their local IDs.
let recordsByQueryId = new Map();
let syncedRoots = SYNCED_ROOTS;
const traverse = async (treeNode, synced) => {
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);
let itemType = "item";
treeNode.ignored = !synced;
treeNode.id = guid;
switch (treeNode.type) {
case PlacesUtils.TYPE_X_MOZ_PLACE:
--- a/services/sync/tests/unit/test_bookmark_validator.js
+++ b/services/sync/tests/unit/test_bookmark_validator.js
@@ -280,36 +280,26 @@ add_task(async function test_cswc_server
"guid": "dddddddddddd",
"title": "",
"id": 2000,
"annos": [{
"name": "places/excludeFromBackup",
"flags": 0,
"expires": 4,
"value": 1
- }, {
- "name": "PlacesOrganizer/OrganizerFolder",
- "flags": 0,
- "expires": 4,
- "value": 7
}],
"type": "text/x-moz-place-container",
"children": [{
"guid": "eeeeeeeeeeee",
"title": "History",
"annos": [{
"name": "places/excludeFromBackup",
"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"
}]
});
server.push({
id: "dddddddddddd",
parentid: "places",
--- a/toolkit/components/places/Bookmarks.jsm
+++ b/toolkit/components/places/Bookmarks.jsm
@@ -147,18 +147,18 @@ var Bookmarks = Object.freeze({
/**
* The GUIDs of the user content root folders that we support, for easy access
* as a set.
*/
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",
virtualUnfiledGuid: "unfiled___v",
virtualMobileGuid: "mobile____v",
/**
* Checks if a guid is a virtual root.
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -113,17 +113,18 @@ function serializeNode(aNode, aIsLivemar
// is coming from.
data.instanceId = PlacesUtils.instanceId;
let guid = aNode.bookmarkGuid;
let grandParentId;
// 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;
if (aNode.parent) {
data.parent = aNode.parent.itemId;
data.parentGuid = aNode.parent.bookmarkGuid;
}
@@ -338,16 +339,39 @@ var PlacesUtils = {
TOPIC_FAVICONS_EXPIRED: "places-favicons-expired",
TOPIC_VACUUM_STARTING: "places-vacuum-starting",
TOPIC_BOOKMARKS_RESTORE_BEGIN: "bookmarks-restore-begin",
TOPIC_BOOKMARKS_RESTORE_SUCCESS: "bookmarks-restore-success",
TOPIC_BOOKMARKS_RESTORE_FAILED: "bookmarks-restore-failed",
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),
endl: NEWLINE,
/**
* Is a string a valid GUID?
*
--- a/toolkit/components/places/nsINavHistoryService.idl
+++ b/toolkit/components/places/nsINavHistoryService.idl
@@ -1075,16 +1075,21 @@ interface nsINavHistoryQueryOptions : ns
* This returns nsINavHistoryQueryResultNode nodes for each top-level bookmark
* root.
*
* @note Setting this resultType will force queryType to QUERY_TYPE_BOOKMARKS.
*/
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_*
*/
attribute unsigned short sortingMode;
/**
* The annotation to use in SORT_BY_ANNOTATION_* sorting modes.
*/
@@ -1134,17 +1139,17 @@ interface nsINavHistoryQueryOptions : ns
* Some pages in history are marked "hidden" and thus don't appear by default
* in queries. These include automatic framed visits and redirects. Setting
* this attribute will return all pages, even hidden ones. Does nothing for
* bookmark queries. Defaults to false.
*/
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.
*
* THIS DOES NOT WORK IN CONJUNCTION WITH SORTING BY TITLE. This is because
* sorting by title requires us to sort after using locale-sensetive sorting
* (as opposed to letting the database do it for us).
*
* Instead, we get the result ordered by date, pick the maxResult most recent
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -775,16 +775,20 @@ nsNavHistory::NormalizeTime(uint32_t aRe
// This query is evaluatable using EvaluateQueryForNode to do live
// updating.
// QUERYUPDATE_COMPLEX:
// This query is not evaluatable using EvaluateQueryForNode. When something
// happens that this query updates, you will need to re-run the query.
// 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
// change operations as simple instead of complex.
uint32_t
nsNavHistory::GetUpdateRequirements(const nsCOMArray<nsNavHistoryQuery>& aQueries,
nsNavHistoryQueryOptions* aOptions,
@@ -830,16 +834,20 @@ nsNavHistory::GetUpdateRequirements(cons
if (aOptions->ResultType() ==
nsINavHistoryQueryOptions::RESULTS_AS_TAG_QUERY)
return QUERYUPDATE_COMPLEX_WITH_BOOKMARKS;
if (aOptions->ResultType() ==
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
// the item to be in the top N items in the database.
if (aOptions->MaxResults() > 0)
return QUERYUPDATE_COMPLEX;
if (aQueries.Count() == 1 && domainBasedItems)
@@ -1411,16 +1419,17 @@ private:
nsresult Select();
nsresult SelectAsURI();
nsresult SelectAsVisit();
nsresult SelectAsDay();
nsresult SelectAsSite();
nsresult SelectAsTag();
nsresult SelectAsRoots();
+ nsresult SelectAsLeftPane();
nsresult Where();
nsresult GroupBy();
nsresult OrderBy();
nsresult Limit();
void OrderByColumnIndexAsc(int32_t aIndex);
void OrderByColumnIndexDesc(int32_t aIndex);
@@ -1517,16 +1526,21 @@ PlacesSQLQueryBuilder::Select()
NS_ENSURE_SUCCESS(rv, rv);
break;
case nsINavHistoryQueryOptions::RESULTS_AS_ROOTS_QUERY:
rv = SelectAsRoots();
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");
}
return NS_OK;
}
nsresult
PlacesSQLQueryBuilder::SelectAsURI()
@@ -1971,16 +1985,58 @@ PlacesSQLQueryBuilder::SelectAsRoots()
toolbarTitle.get(),
menuTitle.get(),
unfiledTitle.get(),
mobileString.get());
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()
{
// Set query options
nsAutoCString additionalVisitsConditions;
nsAutoCString additionalPlacesConditions;
if (!mIncludeHidden) {
@@ -3829,17 +3885,18 @@ nsNavHistory::RowToResult(mozIStorageVal
}
nsAutoCString guid;
if (itemId != -1) {
rv = aRow->GetUTF8String(nsNavBookmarks::kGetChildrenIndex_Guid, guid);
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);
}
RefPtr<nsNavHistoryResultNode> resultNode;
rv = QueryRowToResult(itemId, guid, url, title, accessCount, time,
getter_AddRefs(resultNode));
NS_ENSURE_SUCCESS(rv, rv);
--- a/toolkit/components/places/nsNavHistory.h
+++ b/toolkit/components/places/nsNavHistory.h
@@ -35,16 +35,17 @@
#endif
#define QUERYUPDATE_TIME 0
#define QUERYUPDATE_SIMPLE 1
#define QUERYUPDATE_COMPLEX 2
#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.
#define URI_LENGTH_MAX 65536
#define TITLE_LENGTH_MAX 4096
// Microsecond timeout for "recent" events such as typed and bookmark following.
// If you typed it more than this time ago, it's not recent.
--- a/toolkit/components/places/nsNavHistoryQuery.cpp
+++ b/toolkit/components/places/nsNavHistoryQuery.cpp
@@ -1354,22 +1354,22 @@ NS_IMETHODIMP
nsNavHistoryQueryOptions::GetResultType(uint16_t* aType)
{
*aType = mResultType;
return NS_OK;
}
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;
}
// excludeItems
NS_IMETHODIMP
nsNavHistoryQueryOptions::GetExcludeItems(bool* aExclude)
@@ -1462,17 +1462,19 @@ nsNavHistoryQueryOptions::GetQueryType(u
return NS_OK;
}
NS_IMETHODIMP
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;
}
// asyncEnabled
NS_IMETHODIMP
nsNavHistoryQueryOptions::GetAsyncEnabled(bool* _asyncEnabled)
--- a/toolkit/components/places/nsNavHistoryResult.cpp
+++ b/toolkit/components/places/nsNavHistoryResult.cpp
@@ -1808,17 +1808,18 @@ nsNavHistoryQueryResultNode::CanExpand()
bool
nsNavHistoryQueryResultNode::IsContainersQuery()
{
uint16_t resultType = Options()->ResultType();
return resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY ||
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;
}
/**
* Here we do not want to call ContainerResultNode::OnRemoving since our own
* ClearChildren will do the same thing and more (unregister the observers).
* The base ResultNode::OnRemoving will clear some regular node stats, so it
* is OK.
@@ -1881,17 +1882,18 @@ nsNavHistoryQueryResultNode::GetHasChild
return NS_OK;
}
uint16_t resultType = mOptions->ResultType();
// 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;
}
// For tag containers query we must check if we have any tag
if (resultType == nsINavHistoryQueryOptions::RESULTS_AS_TAG_QUERY) {
nsCOMPtr<nsITaggingService> tagging =
do_GetService(NS_TAGGINGSERVICE_CONTRACTID);
@@ -2107,16 +2109,23 @@ nsNavHistoryQueryResultNode::FillChildre
// if we are limiting our results remove items from the end of the
// mChildren array after sorting. This is done for root node only.
// note, if count < max results, we won't do anything.
if (!mParent && mOptions->MaxResults()) {
while ((uint32_t)mChildren.Count() > mOptions->MaxResults())
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);
if (mOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY ||
mOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_UNIFIED) {
// Date containers that contain site containers have no reason to observe
// history, if the inside site container is expanded it will update,
// otherwise we are going to refresh the parent query.
@@ -2191,22 +2200,31 @@ nsNavHistoryQueryResultNode::Refresh()
// left in a local copy of the observers array.
if (mIndentLevel > -1 && !mParent)
return NS_OK;
// Do not refresh if we are not expanded or if we are child of a query
// 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)
ClearChildren(true);
else
ClearChildren(false);
// Ignore errors from FillChildren, since we will still want to refresh
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/queries/test_results-as-left-pane.js
@@ -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;
+});
--- a/toolkit/components/places/tests/queries/xpcshell.ini
+++ b/toolkit/components/places/tests/queries/xpcshell.ini
@@ -12,16 +12,17 @@ skip-if = (os == 'win' && ccov) # Bug 14
[test_excludeQueries.js]
[test_history_queries_tags_liveUpdate.js]
[test_history_queries_titles_liveUpdate.js]
[test_onlyBookmarked.js]
[test_options_inherit.js]
[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]
[test_search_tags.js]
[test_searchterms-domain.js]
[test_searchterms-uri.js]
[test_searchterms-bookmarklets.js]
[test_sort-date-site-grouping.js]
--- a/toolkit/locales/en-US/chrome/places/places.properties
+++ b/toolkit/locales/en-US/chrome/places/places.properties
@@ -2,16 +2,19 @@
# 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/.
BookmarksMenuFolderTitle=Bookmarks Menu
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
finduri-AgeInDays-is-0=Today
finduri-AgeInDays-is-1=Yesterday
finduri-AgeInDays-is=%S days ago
finduri-AgeInDays-last-is=Last %S days
finduri-AgeInDays-isgreater=Older than %S days