Bug 543444 - Replace single-view API with multiple observers. r=mak sr=rstrong

This commit is contained in:
Asaf Romano 2010-03-12 12:14:47 +01:00
parent 84ebaa3d89
commit f62b88bc41
12 changed files with 1081 additions and 1180 deletions

View File

@ -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;
},

View File

@ -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;
}
},

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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);
};

View File

@ -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;
},

View File

@ -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();
}

View File

@ -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);
}