mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
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:
parent
28f0bb0532
commit
9cd6e1d046
@ -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) {
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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 } }
|
||||
|
Loading…
Reference in New Issue
Block a user