mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-28 11:28:38 +00:00
Bug 543444 - Replace single-view API with multiple observers. r=mak sr=rstrong
This commit is contained in:
parent
84ebaa3d89
commit
f62b88bc41
@ -422,7 +422,7 @@ var gSanitizePromptDialog = {
|
||||
|
||||
var view = gContiguousSelectionTreeHelper.setTree(this.placesTree,
|
||||
new PlacesTreeView());
|
||||
result.viewer = view;
|
||||
result.addObserver(view, false);
|
||||
this.initDurationDropdown();
|
||||
},
|
||||
|
||||
@ -529,8 +529,8 @@ var gSanitizePromptDialog = {
|
||||
*/
|
||||
unload: function ()
|
||||
{
|
||||
var view = this.placesTree.view;
|
||||
view.QueryInterface(Ci.nsINavHistoryResultViewer).result.viewer = null;
|
||||
let result = this.placesTree.getResult();
|
||||
result.removeObserver(this.placesTree.view);
|
||||
this.placesTree.view = null;
|
||||
},
|
||||
|
||||
|
@ -1109,20 +1109,21 @@ PlacesController.prototype = {
|
||||
* The dragstart event.
|
||||
*/
|
||||
setDataTransfer: function PC_setDataTransfer(aEvent) {
|
||||
var dt = aEvent.dataTransfer;
|
||||
var doCopy = ["copyLink", "copy", "link"].indexOf(dt.effectAllowed) != -1;
|
||||
let dt = aEvent.dataTransfer;
|
||||
let doCopy = ["copyLink", "copy", "link"].indexOf(dt.effectAllowed) != -1;
|
||||
|
||||
let result = this._view.getResult();
|
||||
let didSuppressNotifications = result.suppressNotifications;
|
||||
if (!didSuppressNotifications)
|
||||
result.suppressNotifications = true;
|
||||
|
||||
var result = this._view.getResult();
|
||||
var oldViewer = result.viewer;
|
||||
try {
|
||||
result.viewer = null;
|
||||
var nodes = this._view.getDraggableSelection();
|
||||
|
||||
for (var i = 0; i < nodes.length; ++i) {
|
||||
let nodes = this._view.getDraggableSelection();
|
||||
for (let i = 0; i < nodes.length; ++i) {
|
||||
var node = nodes[i];
|
||||
|
||||
function addData(type, index, overrideURI) {
|
||||
var wrapNode = PlacesUtils.wrapNode(node, type, overrideURI, doCopy);
|
||||
let wrapNode = PlacesUtils.wrapNode(node, type, overrideURI, doCopy);
|
||||
dt.mozSetDataAt(type, wrapNode, index);
|
||||
}
|
||||
|
||||
@ -1144,8 +1145,8 @@ PlacesController.prototype = {
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (oldViewer)
|
||||
result.viewer = oldViewer;
|
||||
if (!didSuppressNotifications)
|
||||
result.suppressNotifications = false;
|
||||
}
|
||||
},
|
||||
|
||||
@ -1153,29 +1154,32 @@ PlacesController.prototype = {
|
||||
* Copy Bookmarks and Folders to the clipboard
|
||||
*/
|
||||
copy: function PC_copy() {
|
||||
var result = this._view.getResult();
|
||||
var oldViewer = result.viewer;
|
||||
try {
|
||||
result.viewer = null;
|
||||
var nodes = this._view.getSelectionNodes();
|
||||
let result = this._view.getResult();
|
||||
|
||||
var xferable = Cc["@mozilla.org/widget/transferable;1"].
|
||||
let didSuppressNotifications = result.suppressNotifications;
|
||||
if (!didSuppressNotifications)
|
||||
result.suppressNotifications = true;
|
||||
|
||||
try {
|
||||
let nodes = this._view.getSelectionNodes();
|
||||
|
||||
let xferable = Cc["@mozilla.org/widget/transferable;1"].
|
||||
createInstance(Ci.nsITransferable);
|
||||
var foundFolder = false, foundLink = false;
|
||||
var copiedFolders = [];
|
||||
var placeString, mozURLString, htmlString, unicodeString;
|
||||
let foundFolder = false, foundLink = false;
|
||||
let copiedFolders = [];
|
||||
let placeString, mozURLString, htmlString, unicodeString;
|
||||
placeString = mozURLString = htmlString = unicodeString = "";
|
||||
|
||||
for (var i = 0; i < nodes.length; ++i) {
|
||||
var node = nodes[i];
|
||||
for (let i = 0; i < nodes.length; ++i) {
|
||||
let node = nodes[i];
|
||||
if (this._shouldSkipNode(node, copiedFolders))
|
||||
continue;
|
||||
if (PlacesUtils.nodeIsFolder(node))
|
||||
copiedFolders.push(node);
|
||||
|
||||
function generateChunk(type, overrideURI) {
|
||||
var suffix = i < (nodes.length - 1) ? NEWLINE : "";
|
||||
var uri = overrideURI;
|
||||
let suffix = i < (nodes.length - 1) ? NEWLINE : "";
|
||||
let uri = overrideURI;
|
||||
|
||||
if (PlacesUtils.nodeIsLivemarkContainer(node))
|
||||
uri = PlacesUtils.livemarks.getFeedURI(node.itemId).spec
|
||||
@ -1216,8 +1220,8 @@ PlacesController.prototype = {
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (oldViewer)
|
||||
result.viewer = oldViewer;
|
||||
if (!didSuppressNotifications)
|
||||
result.suppressNotifications = false;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -523,9 +523,9 @@
|
||||
<implementation>
|
||||
<destructor><![CDATA[
|
||||
if (this._result) {
|
||||
this._result.removeObserver(this._resultObserver);
|
||||
this._resultNode.containerOpen = false;
|
||||
this._resultNode = null;
|
||||
this._result.viewer = null;
|
||||
this._result = null;
|
||||
}
|
||||
]]></destructor>
|
||||
@ -692,8 +692,8 @@
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!-- nsINavHistoryResultViewer -->
|
||||
<field name="_viewer"><![CDATA[({
|
||||
<!-- nsINavHistoryResultObserver -->
|
||||
<field name="_resultObserver"><![CDATA[({
|
||||
_self: this,
|
||||
|
||||
get result() {
|
||||
@ -701,24 +701,21 @@
|
||||
},
|
||||
|
||||
set result(val) {
|
||||
// some methods (e.g. getURLsFromContainer) temporarily null out the
|
||||
// viewer when they do temporary changes to the view, this does _not_
|
||||
// call setResult(null), but then, we're called again with the result
|
||||
// object which is already set for this viewer. At that point,
|
||||
// we should do nothing.
|
||||
if (this._self._result != val) {
|
||||
if (this._self._result)
|
||||
this._self._resultNode.containerOpen = false;
|
||||
|
||||
this._self.parentNode._built = false;
|
||||
this._self._result = val;
|
||||
if (val) {
|
||||
this._self._resultNode = val.root;
|
||||
this._self._resultNode._DOMElement = this._self.parentNode;
|
||||
}
|
||||
else
|
||||
this._self._resultNode = null;
|
||||
if (this._self._result) {
|
||||
this._self._result.removeObserver(this);
|
||||
this._self._resultNode.containerOpen = false;
|
||||
}
|
||||
|
||||
this._self.parentNode._built = false;
|
||||
this._self._result = val;
|
||||
if (val) {
|
||||
this._self._resultNode = val.root;
|
||||
this._self._resultNode._DOMElement = this._self.parentNode;
|
||||
}
|
||||
else {
|
||||
this._self._resultNode = null;
|
||||
}
|
||||
|
||||
return val;
|
||||
},
|
||||
|
||||
@ -895,6 +892,14 @@
|
||||
},
|
||||
|
||||
sortingChanged: function PMV_sortingChanged(aSortingMode) {
|
||||
},
|
||||
|
||||
QueryInterface: function PTV_QueryInterface(aIID) {
|
||||
if (aIID.equals(Ci.nsINavHistoryResultObserver) ||
|
||||
aIID.equals(Ci.nsISupportsWeakReference) ||
|
||||
aIID.equals(Ci.nsISupports))
|
||||
return this;
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
})]]></field>
|
||||
|
||||
@ -914,7 +919,7 @@
|
||||
queries.value.length,
|
||||
options.value);
|
||||
|
||||
result.viewer = this._viewer;
|
||||
result.addObserver(this._resultObserver, false);
|
||||
return val;
|
||||
]]></setter>
|
||||
</property>
|
||||
|
@ -100,9 +100,9 @@
|
||||
window.removeEventListener("resize", this, false);
|
||||
|
||||
if (this._result) {
|
||||
this._result.removeObserver(this._resultObserver);
|
||||
this._resultNode.containerOpen = false;
|
||||
this._resultNode = null;
|
||||
this._result.viewer = null;
|
||||
this._result = null;
|
||||
}
|
||||
]]></destructor>
|
||||
@ -402,7 +402,7 @@
|
||||
var result =
|
||||
history.executeQueries(queries.value, queries.value.length,
|
||||
options.value);
|
||||
result.viewer = this._viewer;
|
||||
result.addObserver(this._resultObserver, false);
|
||||
}
|
||||
catch(ex) {
|
||||
// Invalid query, or had no results.
|
||||
@ -509,8 +509,8 @@
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!-- nsINavHistoryResultViewer -->
|
||||
<field name="_viewer"><![CDATA[({
|
||||
<!-- nsINavHistoryResultObserver -->
|
||||
<field name="_resultObserver"><![CDATA[({
|
||||
_self: this,
|
||||
|
||||
get result() {
|
||||
@ -518,25 +518,22 @@
|
||||
},
|
||||
|
||||
set result(val) {
|
||||
// some methods (e.g. getURLsFromContainer) temporarily null out the
|
||||
// viewer when they do temporary changes to the view, this does _not_
|
||||
// call setResult(null), but then, we're called again with the result
|
||||
// object which is already set for this viewer. At that point,
|
||||
// we should do nothing.
|
||||
if (this._self._result != val) {
|
||||
if (this._self._result)
|
||||
this._self._resultNode.containerOpen = false;
|
||||
|
||||
this._self._result = val;
|
||||
if (val) {
|
||||
this._self._resultNode = val.root;
|
||||
this._self._resultNode._DOMElement = this._self;
|
||||
// This calls _rebuild through invalidateContainer.
|
||||
this._self._resultNode.containerOpen = true;
|
||||
}
|
||||
else
|
||||
this._self._resultNode = null;
|
||||
if (this._self._result) {
|
||||
this._self._result.removeObserver(this);
|
||||
this._self._resultNode.containerOpen = false;
|
||||
}
|
||||
|
||||
this._self._result = val;
|
||||
if (val) {
|
||||
this._self._resultNode = val.root;
|
||||
this._self._resultNode._DOMElement = this._self;
|
||||
// This calls _rebuild through invalidateContainer.
|
||||
this._self._resultNode.containerOpen = true;
|
||||
}
|
||||
else {
|
||||
this._self._resultNode = null;
|
||||
}
|
||||
|
||||
return val;
|
||||
},
|
||||
|
||||
@ -757,6 +754,14 @@
|
||||
},
|
||||
|
||||
sortingChanged: function TV_V_sortingChanged(aSortingMode) {
|
||||
},
|
||||
|
||||
QueryInterface: function PTV_QueryInterface(aIID) {
|
||||
if (aIID.equals(Ci.nsINavHistoryResultObserver) ||
|
||||
aIID.equals(Ci.nsISupportsWeakReference) ||
|
||||
aIID.equals(Ci.nsISupports))
|
||||
return this;
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
})]]></field>
|
||||
|
||||
|
@ -58,7 +58,6 @@
|
||||
// the viewer's reference to our treeBoxObject.
|
||||
var result = this.getResult();
|
||||
if (result) {
|
||||
result.viewer = null;
|
||||
result.root.containerOpen = false;
|
||||
}
|
||||
this.view = null;
|
||||
@ -71,7 +70,12 @@
|
||||
<!-- overriding -->
|
||||
<property name="view">
|
||||
<getter><![CDATA[
|
||||
return this.treeBoxObject.view.QueryInterface(Ci.nsINavHistoryResultTreeViewer);
|
||||
try {
|
||||
return this.treeBoxObject.view.QueryInterface(Ci.nsINavHistoryResultTreeViewer);
|
||||
}
|
||||
catch(e) {
|
||||
return null;
|
||||
}
|
||||
]]></getter>
|
||||
<setter><![CDATA[
|
||||
return this.treeBoxObject.view = val;
|
||||
@ -122,22 +126,24 @@
|
||||
<parameter name="options"/>
|
||||
<body><![CDATA[
|
||||
// Cleanup old result if exists.
|
||||
var oldResult = this.getResult();
|
||||
if (oldResult)
|
||||
let oldResult = this.getResult();
|
||||
if (oldResult) {
|
||||
oldResult.removeObserver(this.view);
|
||||
oldResult.root.containerOpen = false;
|
||||
}
|
||||
|
||||
var result = PlacesUtils.history
|
||||
let result = PlacesUtils.history
|
||||
.executeQueries(queries, queries.length,
|
||||
options);
|
||||
var callback;
|
||||
let callback;
|
||||
if (this.flatList) {
|
||||
var onOpenFlatContainer = this.onOpenFlatContainer;
|
||||
let onOpenFlatContainer = this.onOpenFlatContainer;
|
||||
if (onOpenFlatContainer)
|
||||
callback = new Function("aContainer", onOpenFlatContainer);
|
||||
}
|
||||
|
||||
var treeView = new PlacesTreeView(this.flatList, callback);
|
||||
result.viewer = treeView;
|
||||
let treeView = new PlacesTreeView(this.flatList, callback);
|
||||
result.addObserver(treeView, false);
|
||||
this.view = treeView;
|
||||
if (!this._controller) {
|
||||
this._controller = new PlacesController(this);
|
||||
@ -261,13 +267,17 @@
|
||||
// opening each folder as we go.
|
||||
for (var i = parents.length - 1; i >= 0; --i) {
|
||||
var index = view.treeIndexForNode(parents[i]);
|
||||
if (view.isContainer(index) && !view.isContainerOpen(index))
|
||||
if (index != Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE &&
|
||||
view.isContainer(index) && !view.isContainerOpen(index))
|
||||
view.toggleOpenState(index);
|
||||
}
|
||||
// Select the specified node...
|
||||
}
|
||||
|
||||
var index = view.treeIndexForNode(node);
|
||||
if (index == Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE)
|
||||
return;
|
||||
|
||||
view.selection.select(index);
|
||||
// ... and ensure it's visible, not scrolled off somewhere.
|
||||
this.treeBoxObject.ensureRowIsVisible(index);
|
||||
@ -278,7 +288,7 @@
|
||||
<method name="getResult">
|
||||
<body><![CDATA[
|
||||
try {
|
||||
return this.view.QueryInterface(Ci.nsINavHistoryResultViewer).result;
|
||||
return this.view.QueryInterface(Ci.nsINavHistoryResultObserver).result;
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
@ -319,16 +329,19 @@
|
||||
<!-- nsIPlacesView -->
|
||||
<property name="hasSelection">
|
||||
<getter><![CDATA[
|
||||
return this.view.selection.count >= 1;
|
||||
return this.view && this.view.selection.count >= 1;
|
||||
]]></getter>
|
||||
</property>
|
||||
|
||||
<!-- nsIPlacesView -->
|
||||
<method name="getSelectionNodes">
|
||||
<body><![CDATA[
|
||||
let nodes = [];
|
||||
if (!this.hasSelection)
|
||||
return nodes;
|
||||
|
||||
let selection = this.view.selection;
|
||||
let rc = selection.getRangeCount();
|
||||
let nodes = [];
|
||||
let resultview = this.view;
|
||||
for (let i = 0; i < rc; ++i) {
|
||||
let min = { }, max = { };
|
||||
@ -367,9 +380,12 @@
|
||||
// filter out all such redundancies since some partial amount of
|
||||
// the folder's children may be selected.
|
||||
//
|
||||
let nodes = [];
|
||||
if (!this.hasSelection)
|
||||
return nodes;
|
||||
|
||||
var selection = this.view.selection;
|
||||
var rc = selection.getRangeCount();
|
||||
var nodes = [];
|
||||
var resultview = this.view;
|
||||
// This list is kept independently of the range selected (i.e. OUTSIDE
|
||||
// the for loop) since the row index of a container is unique for the
|
||||
@ -404,7 +420,7 @@
|
||||
<property name="selectedNode">
|
||||
<getter><![CDATA[
|
||||
var view = this.view;
|
||||
if (view.selection.count != 1)
|
||||
if (!view || view.selection.count != 1)
|
||||
return null;
|
||||
|
||||
var selection = view.selection;
|
||||
@ -640,12 +656,18 @@
|
||||
return foundOne;
|
||||
}
|
||||
|
||||
// Null the viewer while looking for nodes
|
||||
var result = this.getResult();
|
||||
var oldViewer = result.viewer;
|
||||
result.viewer = null;
|
||||
findNodes(this.getResultNode());
|
||||
result.viewer = oldViewer;
|
||||
// Disable notifications while looking for nodes.
|
||||
let result = this.getResult();
|
||||
let didSuppressNotifications = result.suppressNotifications;
|
||||
if (!didSuppressNotifications)
|
||||
result.suppressNotifications = true
|
||||
try {
|
||||
findNodes(this.getResultNode());
|
||||
}
|
||||
finally {
|
||||
if (!didSuppressNotifications)
|
||||
result.suppressNotifications = false;
|
||||
}
|
||||
|
||||
// For all the nodes we've found, highlight the corresponding
|
||||
// index in the tree.
|
||||
@ -659,6 +681,8 @@
|
||||
}
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var index = resultview.treeIndexForNode(nodes[i]);
|
||||
if (index == Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE)
|
||||
continue;
|
||||
selection.rangedSelect(index, index, true);
|
||||
}
|
||||
selection.selectEventsSuppressed = false;
|
||||
|
@ -63,11 +63,11 @@ PlacesTreeView.prototype = {
|
||||
|
||||
QueryInterface: function PTV_QueryInterface(aIID) {
|
||||
if (aIID.equals(Ci.nsITreeView) ||
|
||||
aIID.equals(Ci.nsINavHistoryResultViewer) ||
|
||||
aIID.equals(Ci.nsINavHistoryResultObserver) ||
|
||||
aIID.equals(Ci.nsINavHistoryResultTreeViewer) ||
|
||||
aIID.equals(Ci.nsISupportsWeakReference) ||
|
||||
aIID.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
|
||||
@ -591,7 +591,7 @@ PlacesTreeView.prototype = {
|
||||
return [this.COLUMN_TYPE_UNKNOWN, false];
|
||||
},
|
||||
|
||||
// nsINavHistoryResultViewer
|
||||
// nsINavHistoryResultObserver
|
||||
nodeInserted: function PTV_nodeInserted(aParentNode, aNode, aNewIndex) {
|
||||
NS_ASSERT(this._result, "Got a notification but have no result!");
|
||||
if (!this._tree || !this._result)
|
||||
@ -995,22 +995,18 @@ PlacesTreeView.prototype = {
|
||||
|
||||
get result() this._result,
|
||||
set result(val) {
|
||||
// Some methods (e.g. getURLsFromContainer) temporarily null out the
|
||||
// viewer when they do temporary changes to the view, this does _not_
|
||||
// call setResult(null), but then, we're called again with the result
|
||||
// object which is already set for this viewer. At that point,
|
||||
// we should do nothing.
|
||||
if (this._result != val) {
|
||||
if (this._result)
|
||||
this._rootNode.containerOpen = false;
|
||||
|
||||
this._result = val;
|
||||
this._rootNode = val ? val.root : null;
|
||||
|
||||
// If the tree is not set yet, setTree will call finishInit.
|
||||
if (this._tree && val)
|
||||
this._finishInit();
|
||||
if (this._result) {
|
||||
this._result.removeObserver(this);
|
||||
this._rootNode.containerOpen = false;
|
||||
}
|
||||
|
||||
this._result = val;
|
||||
this._rootNode = val ? val.root : null;
|
||||
|
||||
// If the tree is not set yet, setTree will call finishInit.
|
||||
if (this._tree && val)
|
||||
this._finishInit();
|
||||
|
||||
return val;
|
||||
},
|
||||
|
||||
@ -1363,8 +1359,10 @@ PlacesTreeView.prototype = {
|
||||
if (hasOldTree) {
|
||||
// detach from result when we are detaching from the tree.
|
||||
// This breaks the reference cycle between us and the result.
|
||||
if (!aTree)
|
||||
this._result.viewer = null;
|
||||
if (!aTree) {
|
||||
this._result.removeObserver(this);
|
||||
this._rootNode.containerOpen = false;
|
||||
}
|
||||
}
|
||||
if (aTree)
|
||||
this._finishInit();
|
||||
|
@ -447,12 +447,10 @@ interface nsINavHistoryQueryResultNode : nsINavHistoryContainerResultNode
|
||||
/**
|
||||
* Allows clients to observe what is happening to a result as it updates itself
|
||||
* according to history and bookmark system events. Register this observer on a
|
||||
* result using registerView
|
||||
*
|
||||
* @see nsINavHistoryResult for where this fits in
|
||||
* result using nsINavHistoryResult::addObserver.
|
||||
*/
|
||||
[scriptable, uuid(af4ac418-a687-4775-8ffa-97c160196432)]
|
||||
interface nsINavHistoryResultViewer : nsISupports
|
||||
[scriptable, uuid(9a229620-1faf-11df-8a39-0800200c9a66)]
|
||||
interface nsINavHistoryResultObserver : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called when 'aItem' is inserted into 'aParent' at index 'aNewIndex'.
|
||||
@ -650,9 +648,7 @@ interface nsINavHistoryResultViewer : nsISupports
|
||||
void sortingChanged(in unsigned short sortingMode);
|
||||
|
||||
/**
|
||||
* Called by the result when this object is set using
|
||||
* nsINavHistoryResult.viewer. This will be set to NULL when the result
|
||||
* is being deallocated. This should not be set by other code.
|
||||
* Called by the result when this observer is added.
|
||||
*/
|
||||
attribute nsINavHistoryResult result;
|
||||
};
|
||||
@ -669,8 +665,8 @@ interface nsINavHistoryResultViewer : nsISupports
|
||||
* object, attach it to a result, never attach it to a tree, and forget about
|
||||
* it, it will leak!
|
||||
*/
|
||||
[scriptable, uuid(fa77e4e9-9fc8-45d2-9507-0fe4f0602505)]
|
||||
interface nsINavHistoryResultTreeViewer : nsINavHistoryResultViewer
|
||||
[scriptable, uuid(f8b518c0-1faf-11df-8a39-0800200c9a66)]
|
||||
interface nsINavHistoryResultTreeViewer : nsINavHistoryResultObserver
|
||||
{
|
||||
/**
|
||||
* This allows you to get at the real node for a given row index. This is
|
||||
@ -697,23 +693,8 @@ interface nsINavHistoryResultTreeViewer : nsINavHistoryResultViewer
|
||||
|
||||
/**
|
||||
* The result of a history/bookmark query.
|
||||
*
|
||||
* Use the "root" element to access the children of this query.
|
||||
*
|
||||
* The basic design of the system is a model-view-controller. This result object
|
||||
* represents the model where the data is stored. External components
|
||||
* provide the view and controller which define how the data looks and how
|
||||
* interaction happens.
|
||||
* [RESULT]----->[viewer]----->[controller]
|
||||
* |
|
||||
* +-- nsINavHistoryResultViewer
|
||||
*
|
||||
* The result indicates to the view when something changes through the
|
||||
* nsINavHistoryResultViewer interface. The viewer is set through
|
||||
* the nsINavHistoryResult.viewer property.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(d1562f6f-8d5a-4042-8524-72f747a51b18)]
|
||||
[scriptable, uuid(c2229ce3-2159-4001-859c-7013c52f7619)]
|
||||
interface nsINavHistoryResult : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -731,13 +712,37 @@ interface nsINavHistoryResult : nsISupports
|
||||
attribute AUTF8String sortingAnnotation;
|
||||
|
||||
/**
|
||||
* The viewer for this result (see comment for the class for how these
|
||||
* objects are related). This may be null, in which case you can still
|
||||
* manually walk the tree using the root node. When this is non-null, you
|
||||
* can access the flattened list of items (flatItemCount, nodeForFlatIndex,
|
||||
* flatIndexForNode).
|
||||
* Whether or not notifications on result changes are suppressed.
|
||||
* Initially set to false.
|
||||
*
|
||||
* Use this to avoid flickering and to improve performance when you
|
||||
* do temporary changes to the result structure (e.g. when searching for a
|
||||
* node recursively).
|
||||
*/
|
||||
attribute nsINavHistoryResultViewer viewer;
|
||||
attribute boolean suppressNotifications;
|
||||
|
||||
/**
|
||||
* Adds an observer for changes done in the result.
|
||||
*
|
||||
* @param aObserver
|
||||
* a result observer.
|
||||
* @param aOwnsWeak
|
||||
* If false, the result will keep an owning reference to the observer,
|
||||
* which must be removed using removeObserver.
|
||||
* If true, the result will keep a weak reference to the observer, which
|
||||
* must implement nsISupportsWeakReference.
|
||||
*
|
||||
* @see nsINavHistoryResultObserver
|
||||
*/
|
||||
void addObserver(in nsINavHistoryResultObserver aObserver, in boolean aOwnsWeak);
|
||||
|
||||
/**
|
||||
* Removes an observer that was added by addObserver.
|
||||
*
|
||||
* @param aObserver
|
||||
* a result observer that was added by addObserver.
|
||||
*/
|
||||
void removeObserver(in nsINavHistoryResultObserver aObserver);
|
||||
|
||||
/**
|
||||
* This is the root of the results. Remember that you need to open all
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -110,10 +110,6 @@ private:
|
||||
// nsNavHistory creates this object and fills in mChildren (by getting
|
||||
// it through GetTopLevel()). Then FilledAllResults() is called to finish
|
||||
// object initialization.
|
||||
//
|
||||
// This object implements nsITreeView so you can just set it to a tree
|
||||
// view and it will work. This object also observes the necessary history
|
||||
// and bookmark events to keep itself up-to-date.
|
||||
|
||||
#define NS_NAVHISTORYRESULT_IID \
|
||||
{ 0x455d1d40, 0x1b9b, 0x40e6, { 0xa6, 0x41, 0x8b, 0xb7, 0xe8, 0x82, 0x23, 0x87 } }
|
||||
@ -130,9 +126,6 @@ public:
|
||||
nsNavHistoryContainerResultNode* aRoot,
|
||||
nsNavHistoryResult** result);
|
||||
|
||||
// the tree viewer can go faster if it can bypass XPCOM
|
||||
friend class nsNavHistoryResultTreeViewer;
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_NAVHISTORYRESULT_IID)
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
@ -148,10 +141,6 @@ public:
|
||||
void RemoveAllBookmarksObserver(nsNavHistoryQueryResultNode* aNode);
|
||||
void StopObserving();
|
||||
|
||||
// returns the view. NOT-ADDREFED. May be NULL if there is no view
|
||||
nsINavHistoryResultViewer* GetView() const
|
||||
{ return mView; }
|
||||
|
||||
public:
|
||||
// two-stage init, use NewHistoryResult to construct
|
||||
nsNavHistoryResult(nsNavHistoryContainerResultNode* mRoot);
|
||||
@ -176,8 +165,6 @@ public:
|
||||
// The sorting annotation to be used for in SORT_BY_ANNOTATION_* modes
|
||||
nsCString mSortingAnnotation;
|
||||
|
||||
nsCOMPtr<nsINavHistoryResultViewer> mView;
|
||||
|
||||
// node observers
|
||||
PRBool mIsHistoryObserver;
|
||||
PRBool mIsBookmarkFolderObserver;
|
||||
@ -197,6 +184,9 @@ public:
|
||||
void InvalidateTree();
|
||||
|
||||
PRBool mBatchInProgress;
|
||||
|
||||
nsMaybeWeakPtrArray<nsINavHistoryResultObserver> mObservers;
|
||||
PRBool mSuppressNotifications;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHistoryResult, NS_NAVHISTORYRESULT_IID)
|
||||
@ -538,25 +528,25 @@ public:
|
||||
|
||||
PRBool AreChildrenVisible();
|
||||
|
||||
// overridded by descendents to populate
|
||||
// Overridded by descendents to populate.
|
||||
virtual nsresult OpenContainer();
|
||||
nsresult CloseContainer(PRBool aUpdateView = PR_TRUE);
|
||||
nsresult CloseContainer(PRBool aSuppressNotifications = PR_FALSE);
|
||||
|
||||
// this points to the result that owns this container. All containers have
|
||||
// This points to the result that owns this container. All containers have
|
||||
// their result pointer set so we can quickly get to the result without having
|
||||
// to walk the tree. Yet, this also saves us from storing a million pointers
|
||||
// for every leaf node to the result.
|
||||
nsRefPtr<nsNavHistoryResult> mResult;
|
||||
|
||||
// for example, RESULT_TYPE_QUERY. Query and Folder results override GetType
|
||||
// For example, RESULT_TYPE_QUERY. Query and Folder results override GetType
|
||||
// so this is not used, but is still kept in sync.
|
||||
PRUint32 mContainerType;
|
||||
|
||||
// when there are children, this stores the open state in the tree
|
||||
// this is set to the default in the constructor
|
||||
// When there are children, this stores the open state in the tree
|
||||
// this is set to the default in the constructor.
|
||||
PRBool mExpanded;
|
||||
|
||||
// Filled in by the result type generator in nsNavHistory
|
||||
// Filled in by the result type generator in nsNavHistory.
|
||||
nsCOMArray<nsNavHistoryResultNode> mChildren;
|
||||
|
||||
PRBool mChildrenReadOnly;
|
||||
@ -567,9 +557,9 @@ public:
|
||||
nsCString mDynamicContainerType;
|
||||
|
||||
void FillStats();
|
||||
void ReverseUpdateStats(PRInt32 aAccessCountChange);
|
||||
nsresult ReverseUpdateStats(PRInt32 aAccessCountChange);
|
||||
|
||||
// sorting
|
||||
// Sorting methods.
|
||||
typedef nsCOMArray<nsNavHistoryResultNode>::nsCOMArrayComparatorFunc SortComparator;
|
||||
virtual PRUint16 GetSortType();
|
||||
virtual void GetSortingAnnotation(nsACString& aSortingAnnotation);
|
||||
@ -653,10 +643,10 @@ public:
|
||||
nsNavHistoryContainerResultNode* aContainer,
|
||||
const nsCString& aSpec,
|
||||
nsCOMArray<nsNavHistoryResultNode>* aMatches);
|
||||
void UpdateURIs(PRBool aRecursive, PRBool aOnlyOne, PRBool aUpdateSort,
|
||||
const nsCString& aSpec,
|
||||
void (*aCallback)(nsNavHistoryResultNode*,void*, nsNavHistoryResult*),
|
||||
void* aClosure);
|
||||
nsresult UpdateURIs(PRBool aRecursive, PRBool aOnlyOne, PRBool aUpdateSort,
|
||||
const nsCString& aSpec,
|
||||
nsresult (*aCallback)(nsNavHistoryResultNode*,void*, nsNavHistoryResult*),
|
||||
void* aClosure);
|
||||
nsresult ChangeTitles(nsIURI* aURI, const nsACString& aNewTitle,
|
||||
PRBool aRecursive, PRBool aOnlyOne);
|
||||
};
|
||||
|
@ -24,6 +24,7 @@
|
||||
* Asaf Romano <mano@mozilla.com>
|
||||
* Sungjoon Steve Won <stevewon@gmail.com>
|
||||
* Dietrich Ayala <dietrich@mozilla.com>
|
||||
* Marco Bonardo <mak77@bonardo.net>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -1097,24 +1098,29 @@ var PlacesUtils = {
|
||||
if (!this.nodeIsContainer(aNode))
|
||||
return false;
|
||||
|
||||
var root = this.getContainerNodeWithOptions(aNode, false, true);
|
||||
var oldViewer = root.parentResult.viewer;
|
||||
var wasOpen = root.containerOpen;
|
||||
let root = this.getContainerNodeWithOptions(aNode, false, true);
|
||||
let result = root.parentResult;
|
||||
let didSuppressNotifications = false;
|
||||
let wasOpen = root.containerOpen;
|
||||
if (!wasOpen) {
|
||||
root.parentResult.viewer = null;
|
||||
didSuppressNotifications = result.suppressNotifications;
|
||||
if (!didSuppressNotifications)
|
||||
result.suppressNotifications = true;
|
||||
|
||||
root.containerOpen = true;
|
||||
}
|
||||
|
||||
var found = false;
|
||||
for (var i = 0; i < root.childCount && !found; i++) {
|
||||
var child = root.getChild(i);
|
||||
let found = false;
|
||||
for (let i = 0; i < root.childCount && !found; i++) {
|
||||
let child = root.getChild(i);
|
||||
if (this.nodeIsURI(child))
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!wasOpen) {
|
||||
root.containerOpen = false;
|
||||
root.parentResult.viewer = oldViewer;
|
||||
if (!didSuppressNotifications)
|
||||
result.suppressNotifications = false;
|
||||
}
|
||||
return found;
|
||||
},
|
||||
@ -1128,27 +1134,32 @@ var PlacesUtils = {
|
||||
* @returns array of uris in the first level of the container.
|
||||
*/
|
||||
getURLsForContainerNode: function PU_getURLsForContainerNode(aNode) {
|
||||
var urls = [];
|
||||
let urls = [];
|
||||
if (!this.nodeIsContainer(aNode))
|
||||
return urls;
|
||||
|
||||
var root = this.getContainerNodeWithOptions(aNode, false, true);
|
||||
var oldViewer = root.parentResult.viewer;
|
||||
var wasOpen = root.containerOpen;
|
||||
let root = this.getContainerNodeWithOptions(aNode, false, true);
|
||||
let result = root.parentResult;
|
||||
let wasOpen = root.containerOpen;
|
||||
let didSuppressNotifications = false;
|
||||
if (!wasOpen) {
|
||||
root.parentResult.viewer = null;
|
||||
didSuppressNotifications = result.suppressNotifications;
|
||||
if (!didSuppressNotifications)
|
||||
result.suppressNotifications = true;
|
||||
|
||||
root.containerOpen = true;
|
||||
}
|
||||
|
||||
for (var i = 0; i < root.childCount; ++i) {
|
||||
var child = root.getChild(i);
|
||||
for (let i = 0; i < root.childCount; ++i) {
|
||||
let child = root.getChild(i);
|
||||
if (this.nodeIsURI(child))
|
||||
urls.push({uri: child.uri, isBookmark: this.nodeIsBookmark(child)});
|
||||
}
|
||||
|
||||
if (!wasOpen) {
|
||||
root.containerOpen = false;
|
||||
root.parentResult.viewer = oldViewer;
|
||||
if (!didSuppressNotifications)
|
||||
result.suppressNotifications = false;
|
||||
}
|
||||
return urls;
|
||||
},
|
||||
|
@ -74,6 +74,14 @@ function readFileData(aFile) {
|
||||
}
|
||||
|
||||
var result;
|
||||
var resultObserver = {
|
||||
itemChanged: function(item) {
|
||||
// The favicon should not be set on the containing query.
|
||||
if (item.uri.substr(0,5) == "place")
|
||||
print("Testing itemChanged on: " + item.uri);
|
||||
do_check_eq(item.icon.spec, null);
|
||||
}
|
||||
};
|
||||
|
||||
// main
|
||||
function run_test() {
|
||||
@ -105,15 +113,8 @@ function run_test() {
|
||||
options.excludeQueries = 1;
|
||||
options.sortingMode = options.SORT_BY_DATE_DESCENDING;
|
||||
result = histsvc.executeQuery(query, options);
|
||||
// Associate a viewer to our result
|
||||
result.viewer = {
|
||||
itemChanged: function(item) {
|
||||
// The favicon should not be set on the containing query.
|
||||
if (item.uri.substr(0,5) == "place")
|
||||
dump("\nTesting itemChanged on: \n " + item.uri + "\n\n");
|
||||
do_check_eq(item.icon.spec, null);
|
||||
}
|
||||
};
|
||||
result.addObserver(resultObserver, false);
|
||||
|
||||
var root = result.root;
|
||||
root.containerOpen = true;
|
||||
|
||||
@ -130,7 +131,7 @@ function run_test() {
|
||||
function end_test() {
|
||||
var root = result.root;
|
||||
root.containerOpen = false;
|
||||
result.viewer = null;
|
||||
result.removeObserver(resultObserver);
|
||||
|
||||
do_test_finished();
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ function add_visit(aURI, aDate) {
|
||||
return placeID;
|
||||
}
|
||||
|
||||
var viewer = {
|
||||
var resultObserver = {
|
||||
insertedNode: null,
|
||||
nodeInserted: function(parent, node, newIndex) {
|
||||
this.insertedNode = node;
|
||||
@ -117,9 +117,6 @@ var viewer = {
|
||||
this.sortingMode = sortingMode;
|
||||
},
|
||||
result: null,
|
||||
ignoreInvalidateContainer: false,
|
||||
addViewObserver: function(observer, ownsWeak) {},
|
||||
removeViewObserver: function(observer) {},
|
||||
reset: function() {
|
||||
this.insertedNode = null;
|
||||
this.removedNode = null;
|
||||
@ -143,55 +140,55 @@ function run_test() {
|
||||
options.resultType = options.RESULTS_AS_VISIT;
|
||||
var query = histsvc.getNewQuery();
|
||||
var result = histsvc.executeQuery(query, options);
|
||||
result.viewer = viewer;
|
||||
result.addObserver(resultObserver, false);
|
||||
var root = result.root;
|
||||
root.containerOpen = true;
|
||||
|
||||
// nsINavHistoryResultViewer.containerOpened
|
||||
do_check_neq(viewer.openedContainer, null);
|
||||
// nsINavHistoryResultObserver.containerOpened
|
||||
do_check_neq(resultObserver.openedContainer, null);
|
||||
|
||||
// nsINavHistoryResultViewer.nodeInserted
|
||||
// nsINavHistoryResultObserver.nodeInserted
|
||||
// add a visit
|
||||
var testURI = uri("http://mozilla.com");
|
||||
add_visit(testURI);
|
||||
do_check_eq(testURI.spec, viewer.insertedNode.uri);
|
||||
do_check_eq(testURI.spec, resultObserver.insertedNode.uri);
|
||||
|
||||
// nsINavHistoryResultViewer.nodeHistoryDetailsChanged
|
||||
// nsINavHistoryResultObserver.nodeHistoryDetailsChanged
|
||||
// adding a visit causes nodeHistoryDetailsChanged for the folder
|
||||
do_check_eq(root.uri, viewer.nodeChangedByHistoryDetails.uri);
|
||||
do_check_eq(root.uri, resultObserver.nodeChangedByHistoryDetails.uri);
|
||||
|
||||
// nsINavHistoryResultViewer.itemTitleChanged for a leaf node
|
||||
// nsINavHistoryResultObserver.itemTitleChanged for a leaf node
|
||||
bhist.addPageWithDetails(testURI, "baz", Date.now() * 1000);
|
||||
do_check_eq(viewer.nodeChangedByTitle.title, "baz");
|
||||
do_check_eq(resultObserver.nodeChangedByTitle.title, "baz");
|
||||
|
||||
// nsINavHistoryResultViewer.nodeRemoved
|
||||
// nsINavHistoryResultObserver.nodeRemoved
|
||||
var removedURI = uri("http://google.com");
|
||||
add_visit(removedURI);
|
||||
bhist.removePage(removedURI);
|
||||
do_check_eq(removedURI.spec, viewer.removedNode.uri);
|
||||
do_check_eq(removedURI.spec, resultObserver.removedNode.uri);
|
||||
|
||||
// XXX nsINavHistoryResultViewer.nodeReplaced
|
||||
// XXX nsINavHistoryResultObserver.nodeReplaced
|
||||
// NHQRN.onVisit()->NHCRN.MergeResults()->NHCRN.ReplaceChildURIAt()->NHRV.NodeReplaced()
|
||||
|
||||
// nsINavHistoryResultViewer.invalidateContainer
|
||||
// nsINavHistoryResultObserver.invalidateContainer
|
||||
bhist.removePagesFromHost("mozilla.com", false);
|
||||
do_check_eq(root.uri, viewer.invalidatedContainer.uri);
|
||||
do_check_eq(root.uri, resultObserver.invalidatedContainer.uri);
|
||||
|
||||
// nsINavHistoryResultViewer.sortingChanged
|
||||
viewer.invalidatedContainer = null;
|
||||
// nsINavHistoryResultObserver.sortingChanged
|
||||
resultObserver.invalidatedContainer = null;
|
||||
result.sortingMode = options.SORT_BY_TITLE_ASCENDING;
|
||||
do_check_eq(viewer.sortingMode, options.SORT_BY_TITLE_ASCENDING);
|
||||
do_check_eq(viewer.invalidatedContainer, result.root);
|
||||
do_check_eq(resultObserver.sortingMode, options.SORT_BY_TITLE_ASCENDING);
|
||||
do_check_eq(resultObserver.invalidatedContainer, result.root);
|
||||
|
||||
// nsINavHistoryResultViewer.containerClosed
|
||||
// nsINavHistoryResultObserver.containerClosed
|
||||
root.containerOpen = false;
|
||||
do_check_eq(viewer.closedContainer, viewer.openedContainer);
|
||||
result.viewer = null;
|
||||
do_check_eq(resultObserver.closedContainer, resultObserver.openedContainer);
|
||||
result.removeObserver(resultObserver);
|
||||
|
||||
// bookmarks query
|
||||
|
||||
// reset the viewer
|
||||
viewer.reset();
|
||||
// Reset the result observer.
|
||||
resultObserver.reset();
|
||||
|
||||
try {
|
||||
var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
|
||||
@ -203,49 +200,49 @@ function run_test() {
|
||||
var query = histsvc.getNewQuery();
|
||||
query.setFolders([bmsvc.bookmarksMenuFolder], 1);
|
||||
var result = histsvc.executeQuery(query, options);
|
||||
result.viewer = viewer;
|
||||
result.addObserver(resultObserver, false);
|
||||
var root = result.root;
|
||||
root.containerOpen = true;
|
||||
|
||||
// nsINavHistoryResultViewer.containerOpened
|
||||
do_check_neq(viewer.openedContainer, null);
|
||||
// nsINavHistoryResultObserver.containerOpened
|
||||
do_check_neq(resultObserver.openedContainer, null);
|
||||
|
||||
// nsINavHistoryResultViewer.nodeInserted
|
||||
// nsINavHistoryResultObserver.nodeInserted
|
||||
// add a bookmark
|
||||
var testBookmark = bmsvc.insertBookmark(bmsvc.bookmarksMenuFolder, testURI, bmsvc.DEFAULT_INDEX, "foo");
|
||||
do_check_eq("foo", viewer.insertedNode.title);
|
||||
do_check_eq(testURI.spec, viewer.insertedNode.uri);
|
||||
do_check_eq("foo", resultObserver.insertedNode.title);
|
||||
do_check_eq(testURI.spec, resultObserver.insertedNode.uri);
|
||||
|
||||
// nsINavHistoryResultViewer.nodeHistoryDetailsChanged
|
||||
// nsINavHistoryResultObserver.nodeHistoryDetailsChanged
|
||||
// adding a visit causes nodeHistoryDetailsChanged for the folder
|
||||
do_check_eq(root.uri, viewer.nodeChangedByHistoryDetails.uri);
|
||||
do_check_eq(root.uri, resultObserver.nodeChangedByHistoryDetails.uri);
|
||||
|
||||
// nsINavHistoryResultViewer.nodeTitleChanged for a leaf node
|
||||
// nsINavHistoryResultObserver.nodeTitleChanged for a leaf node
|
||||
bmsvc.setItemTitle(testBookmark, "baz");
|
||||
do_check_eq(viewer.nodeChangedByTitle.title, "baz");
|
||||
do_check_eq(viewer.newTitle, "baz");
|
||||
do_check_eq(resultObserver.nodeChangedByTitle.title, "baz");
|
||||
do_check_eq(resultObserver.newTitle, "baz");
|
||||
|
||||
var testBookmark2 = bmsvc.insertBookmark(bmsvc.bookmarksMenuFolder, uri("http://google.com"), bmsvc.DEFAULT_INDEX, "foo");
|
||||
bmsvc.moveItem(testBookmark2, bmsvc.bookmarksMenuFolder, 0);
|
||||
do_check_eq(viewer.movedNode.itemId, testBookmark2);
|
||||
do_check_eq(resultObserver.movedNode.itemId, testBookmark2);
|
||||
|
||||
// nsINavHistoryResultViewer.nodeRemoved
|
||||
// nsINavHistoryResultObserver.nodeRemoved
|
||||
bmsvc.removeItem(testBookmark2);
|
||||
do_check_eq(testBookmark2, viewer.removedNode.itemId);
|
||||
do_check_eq(testBookmark2, resultObserver.removedNode.itemId);
|
||||
|
||||
// XXX nsINavHistoryResultViewer.nodeReplaced
|
||||
// XXX nsINavHistoryResultObserver.nodeReplaced
|
||||
// NHQRN.onVisit()->NHCRN.MergeResults()->NHCRN.ReplaceChildURIAt()->NHRV.NodeReplaced()
|
||||
|
||||
// XXX nsINavHistoryResultViewer.invalidateContainer
|
||||
// XXX nsINavHistoryResultObserver.invalidateContainer
|
||||
|
||||
// nsINavHistoryResultViewer.sortingChanged
|
||||
viewer.invalidatedContainer = null;
|
||||
// nsINavHistoryResultObserver.sortingChanged
|
||||
resultObserver.invalidatedContainer = null;
|
||||
result.sortingMode = options.SORT_BY_TITLE_ASCENDING;
|
||||
do_check_eq(viewer.sortingMode, options.SORT_BY_TITLE_ASCENDING);
|
||||
do_check_eq(viewer.invalidatedContainer, result.root);
|
||||
do_check_eq(resultObserver.sortingMode, options.SORT_BY_TITLE_ASCENDING);
|
||||
do_check_eq(resultObserver.invalidatedContainer, result.root);
|
||||
|
||||
// nsINavHistoryResultViewer.containerClosed
|
||||
// nsINavHistoryResultObserver.containerClosed
|
||||
root.containerOpen = false;
|
||||
do_check_eq(viewer.closedContainer, viewer.openedContainer);
|
||||
result.viewer = null;
|
||||
do_check_eq(resultObserver.closedContainer, resultObserver.openedContainer);
|
||||
result.removeObserver(resultObserver);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user