Bug 1107364 - remove findNodeByDetails and maintain a map to fetch a node by details speedily, r=mak

This patch removes the interface/header/implementation.

This adds a Map to PlacesTreeView to maintain the relation between node
details and the nodes, based on changes in `this._rows` or on
node details changed events. The Map exists from node details
to nodes (not rows).

MozReview-Commit-ID: EUNiXNIB5rN

--HG--
extra : rebase_source : 5391d9240e1f871c53d8aa26ae951b440b7280ad
This commit is contained in:
MilindL 2017-06-08 20:51:51 +05:30
parent 28f0bb0532
commit 9cd6e1d046
4 changed files with 76 additions and 78 deletions

View File

@ -10,6 +10,31 @@ const PTV_interfaces = [Ci.nsITreeView,
Ci.nsINavHistoryResultTreeViewer,
Ci.nsISupportsWeakReference];
/**
* This returns the key for any node/details object.
*
* @param nodeOrDetails
* A node, or an object containing the following properties:
* - uri
* - time
* - itemId
* In case any of these is missing, an empty string will be returned. This is
* to facilitate easy delete statements which occur due to assignment to items in `this._rows`,
* since the item we are deleting may be undefined in the array.
*
* @return key or empty string.
*/
function makeNodeDetailsKey(nodeOrDetails) {
if (nodeOrDetails &&
typeof nodeOrDetails === "object" &&
"uri" in nodeOrDetails &&
"time" in nodeOrDetails &&
"itemId" in nodeOrDetails) {
return `${nodeOrDetails.uri}*${nodeOrDetails.time}*${nodeOrDetails.itemId}`;
}
return "";
}
function PlacesTreeView(aFlatList, aOnOpenFlatContainer, aController) {
this._tree = null;
this._result = null;
@ -17,6 +42,7 @@ function PlacesTreeView(aFlatList, aOnOpenFlatContainer, aController) {
this._rootNode = null;
this._rows = [];
this._flatList = aFlatList;
this._nodeDetails = new Map();
this._openContainerCallback = aOnOpenFlatContainer;
this._controller = aController;
}
@ -187,8 +213,11 @@ PlacesTreeView.prototype = {
}
}
if (row != -1)
if (row != -1) {
this._nodeDetails.delete(makeNodeDetailsKey(this._rows[row]));
this._nodeDetails.set(makeNodeDetailsKey(aNode), aNode);
this._rows[row] = aNode;
}
return row;
},
@ -233,16 +262,27 @@ PlacesTreeView.prototype = {
// If there's no container prior to the given row, it's a child of
// the root node (remember: all containers are listed in the rows array).
if (!rowNode)
return this._rows[aRow] = this._rootNode.getChild(aRow);
if (!rowNode) {
let newNode = this._rootNode.getChild(aRow);
this._nodeDetails.delete(makeNodeDetailsKey(this._rows[aRow]));
this._nodeDetails.set(makeNodeDetailsKey(newNode), newNode);
return this._rows[aRow] = newNode;
}
// Unset elements may exist only in plain containers. Thus, if the nearest
// node is a container, it's the row's parent, otherwise, it's a sibling.
if (rowNode instanceof Ci.nsINavHistoryContainerResultNode)
return this._rows[aRow] = rowNode.getChild(aRow - row - 1);
if (rowNode instanceof Ci.nsINavHistoryContainerResultNode) {
let newNode = rowNode.getChild(aRow - row - 1);
this._nodeDetails.delete(makeNodeDetailsKey(this._rows[aRow]));
this._nodeDetails.set(makeNodeDetailsKey(newNode), newNode);
return this._rows[aRow] = newNode;
}
let [parent, parentRow] = this._getParentByChildRow(row);
return this._rows[aRow] = parent.getChild(aRow - parentRow - 1);
let newNode = parent.getChild(aRow - parentRow - 1);
this._nodeDetails.delete(makeNodeDetailsKey(this._rows[aRow]));
this._nodeDetails.set(makeNodeDetailsKey(newNode), newNode);
return this._rows[aRow] = newNode;
},
/**
@ -271,6 +311,10 @@ PlacesTreeView.prototype = {
// iteration.
let cc = aContainer.childCount;
let newElements = new Array(cc);
// We need to clean up the node details from aFirstChildRow + 1 to the end of rows.
for (let i = aFirstChildRow + 1; i < this._rows.length; i++) {
this._nodeDetails.delete(makeNodeDetailsKey(this._rows[i]));
}
this._rows = this._rows.splice(0, aFirstChildRow)
.concat(newElements, this._rows);
@ -292,11 +336,14 @@ PlacesTreeView.prototype = {
// Remove the element for the filtered separator.
// Notice that the rows array was initially resized to include all
// children.
this._nodeDetails.delete(makeNodeDetailsKey(this._rows[row]));
this._rows.splice(row, 1);
continue;
}
}
this._nodeDetails.delete(makeNodeDetailsKey(this._rows[row]));
this._nodeDetails.set(makeNodeDetailsKey(curChild), curChild);
this._rows[row] = curChild;
rowsInserted++;
@ -407,8 +454,9 @@ PlacesTreeView.prototype = {
// invisible.
let ancestors = PlacesUtils.nodeAncestors(aOldNode);
for (let ancestor of ancestors) {
if (!ancestor.containerOpen)
if (!ancestor.containerOpen) {
return -1;
}
}
return this._getRowForNode(aOldNode, true);
@ -419,10 +467,8 @@ PlacesTreeView.prototype = {
// the old node, we'll select the first one after refresh. There's
// nothing we could do about that, because aOldNode.parent is
// gone by the time invalidateContainer is called.
let newNode = aUpdatedContainer.findNodeByDetails(aOldNode.uri,
aOldNode.time,
aOldNode.itemId,
true);
let newNode = this._nodeDetails.get(makeNodeDetailsKey(aOldNode));
if (!newNode)
return -1;
@ -649,6 +695,7 @@ PlacesTreeView.prototype = {
}
}
this._nodeDetails.set(makeNodeDetailsKey(aNode), aNode);
this._rows.splice(row, 0, aNode);
this._tree.rowCountChanged(row, 1);
@ -700,7 +747,9 @@ PlacesTreeView.prototype = {
// Remove the node and its children, if any.
let count = this._countVisibleRowsForNodeAtRow(oldRow);
this._rows.splice(oldRow, count);
for (let splicedNode of this._rows.splice(oldRow, count)) {
this._nodeDetails.delete(makeNodeDetailsKey(splicedNode));
}
this._tree.rowCountChanged(oldRow, -count);
// Redraw the parent if its twisty state has changed.
@ -753,7 +802,9 @@ PlacesTreeView.prototype = {
}
// Remove node and its children, if any, from the old position.
this._rows.splice(oldRow, count);
for (let splicedNode of this._rows.splice(oldRow, count)) {
this._nodeDetails.delete(makeNodeDetailsKey(splicedNode));
}
this._tree.rowCountChanged(oldRow, -count);
// Insert the node into the new position.
@ -817,6 +868,10 @@ PlacesTreeView.prototype = {
},
nodeURIChanged: function PTV_nodeURIChanged(aNode, aOldURI) {
this._nodeDetails.delete(makeNodeDetailsKey({uri: aOldURI,
itemId: aNode.itemId,
time: aNode.time}));
this._nodeDetails.set(makeNodeDetailsKey(aNode), aNode);
this._invalidateCellValue(aNode, this.COLUMN_TYPE_URI);
},
@ -827,6 +882,10 @@ PlacesTreeView.prototype = {
nodeHistoryDetailsChanged:
function PTV_nodeHistoryDetailsChanged(aNode, aOldVisitDate,
aOldVisitCount) {
this._nodeDetails.delete(makeNodeDetailsKey({uri: aNode.uri,
itemId: aNode.itemId,
time: aOldVisitDate}));
this._nodeDetails.set(makeNodeDetailsKey(aNode), aNode);
if (aNode.parent && this._controller.hasCachedLivemarkInfo(aNode.parent)) {
// Find the node in the parent.
let parentRow = this._flatList ? 0 : this._getRowForNode(aNode.parent);
@ -918,6 +977,7 @@ PlacesTreeView.prototype = {
// If the root node is now closed, the tree is empty.
if (!this._rootNode.containerOpen) {
this._nodeDetails.clear();
this._rows = [];
if (replaceCount)
this._tree.rowCountChanged(startReplacement, -replaceCount);
@ -944,7 +1004,9 @@ PlacesTreeView.prototype = {
this.selection.selectEventsSuppressed = true;
// First remove the old elements
this._rows.splice(startReplacement, replaceCount);
for (let splicedNode of this._rows.splice(startReplacement, replaceCount)) {
this._nodeDetails.delete(makeNodeDetailsKey(splicedNode));
}
// If the container is now closed, we're done.
if (!aContainer.containerOpen) {

View File

@ -253,28 +253,6 @@ interface nsINavHistoryContainerResultNode : nsINavHistoryResultNode
* container.
*/
unsigned long getChildIndex(in nsINavHistoryResultNode aNode);
/**
* Look for a node in the container by some of its details. Does not search
* closed containers.
*
* @param aURI
* the node's uri attribute value
* @param aTime
* the node's time attribute value.
* @param aItemId
* the node's itemId attribute value.
* @param aRecursive
* whether or not to search recursively.
*
* @throws NS_ERROR_NOT_AVAILABLE if this container is closed.
* @return a result node that matches the given details if any, null
* otherwise.
*/
nsINavHistoryResultNode findNodeByDetails(in AUTF8String aURIString,
in PRTime aTime,
in long long aItemId,
in boolean aRecursive);
};

View File

@ -1708,43 +1708,6 @@ nsNavHistoryContainerResultNode::GetChildIndex(nsINavHistoryResultNode* aNode,
return NS_OK;
}
NS_IMETHODIMP
nsNavHistoryContainerResultNode::FindNodeByDetails(const nsACString& aURIString,
PRTime aTime,
int64_t aItemId,
bool aRecursive,
nsINavHistoryResultNode** _retval) {
if (!mExpanded)
return NS_ERROR_NOT_AVAILABLE;
*_retval = nullptr;
for (int32_t i = 0; i < mChildren.Count(); ++i) {
if (mChildren[i]->mURI.Equals(aURIString) &&
mChildren[i]->mTime == aTime &&
mChildren[i]->mItemId == aItemId) {
*_retval = mChildren[i];
break;
}
if (aRecursive && mChildren[i]->IsContainer()) {
nsNavHistoryContainerResultNode* asContainer =
mChildren[i]->GetAsContainer();
if (asContainer->mExpanded) {
nsresult rv = asContainer->FindNodeByDetails(aURIString, aTime,
aItemId,
aRecursive,
_retval);
if (NS_SUCCEEDED(rv) && _retval)
break;
}
}
}
NS_IF_ADDREF(*_retval);
return NS_OK;
}
/**
* HOW QUERY UPDATING WORKS
*

View File

@ -419,11 +419,6 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHistoryResultNode, NS_NAVHISTORYRESULTNODE_II
{ return nsNavHistoryContainerResultNode::GetChild(index, _retval); } \
NS_IMETHOD GetChildIndex(nsINavHistoryResultNode* aNode, uint32_t* _retval) override \
{ return nsNavHistoryContainerResultNode::GetChildIndex(aNode, _retval); } \
NS_IMETHOD FindNodeByDetails(const nsACString& aURIString, PRTime aTime, \
int64_t aItemId, bool aRecursive, \
nsINavHistoryResultNode** _retval) override \
{ return nsNavHistoryContainerResultNode::FindNodeByDetails(aURIString, aTime, aItemId, \
aRecursive, _retval); }
#define NS_NAVHISTORYCONTAINERRESULTNODE_IID \
{ 0x6e3bf8d3, 0x22aa, 0x4065, { 0x86, 0xbc, 0x37, 0x46, 0xb5, 0xb3, 0x2c, 0xe8 } }