Querying Places

  • Revision slug: Querying_Places
  • Revision title: Querying Places
  • Revision id: 131235
  • Created:
  • Creator: Dietrich
  • Is current revision? No
  • Comment

Revision Content

{{template.Firefox3()}}

Firefox history and bookmarks data is accessed using the "Places" query APIs. These APIs provide the ability to execute complex queries against history, bookmarks or both combined (targeting Firefox Alpha 6). The result of a query is an object which will contain a flat list, or tree structure of the matching data. The definitions of the query API and the resulting data structures is available in {{template.Source("toolkit/components/places/public/nsINavHistoryService.idl")}}. This page provides an introduction to some common operations, and example usage of the core APIs.

Executing a query

Places queries have several basic parts:

  • The query object: nsINavHistoryQuery, holds the search parameters
  • The query options: nsINavHistoryQueryOptions, allows configuration of the search result
  • The history service: nsINavHistoryService, executes the query

The first first step is to create the query and options, and fill them with the parameters you want. Use nsINavHistoryService.getNewQuery and nsINavHistoryService.getNewQueryOptions to retrieve empty objects. The defaults for these objects will result in a query that returns all of your browser history in a flat list:

var historyService = Components.classes["@mozilla.org/browser/nav-history-service;1"]
                               .getService(Components.interfaces.nsINavHistoryService);

// no query parameters will get all history
// XXX default sorting is... ?
var options = historyService.getNewQueryOptions();

// no query parameters will return everything
var query = historyService.getNewQuery();

// execute the query
var result = historyService.executeQuery(query, options);

Result types

The query options structure nsINavHistoryQueryOptions has an attribute resultType for the type of the results that you want. It is important to understand the differences between these:

  • RESULTS_AS_URI: This is the default, and means that you get one result node of type RESULT_TYPE_URI for each URI that matches the query. The visit date for each node will be the last visit date for that URL. You will definitely want this when showing bookmark folders.
  • RESULTS_AS_VISIT: This means that you want one entry for each time a page was visited matching the given query. Therefore, you may get duplicate entries for URLs, each with a different date. The nodes will be of type RESULT_TYPE_VISIT which will give you access to a session ID for each visit. This session ID is the same for all pages that were reached by clicking links. A new session starts when the user types a new URL or follows a bookmark. It is used to compute the dividing lines in the places history view.
  • RESULTS_AS_FULL_VISIT: This is the same as visits but the results will be of type RESULT_TYPE_FULL_VISIT and will have additional information about the visit, such as the referring visit, and how the transition happened (typed, redirect, link, etc). This information is typically not needed and causes the result nodes (of which there may be many) to be larger, which is why it is a separate option.

The named constants are properties of nsINavHistoryQueryOptions, for example Components.interfaces.nsINavHistoryQueryOptions.RESULTS_AS_VISIT.

Query parameters

Note: The query object interface is subject to change. In its current form it is not very expressive. Implementing a better interface is {{template.Bug(317830)}}.

You can pass one or more nsINavHistoryQuery objects to nsINavHistoryService.executeQuery/executeQueries. Within one query object, all parameters are ANDed together. The conditions for different query objects are then ORed together. This allows for a simpler implementation and interface than a full logical operation with nested clauses while still being expressive.

Example of querying for any pages I've visited that contain the word "firefox" in the title/URL or that I've visited today from mozilla.org.

// first query object searches for "firefox" in title/URL
var query1 = historyService.getNewQuery();
query1.searchTerms = "firefox";

// second query object searches for visited in past 24 hours AND from mozilla.org
var query2 = historyService.getNewQuery();
query2.beginTimeReference = query2.TIME_RELATIVE_NOW;
query2.beginTime = -24 * 60 * 60 * 1000000; // 24 hours ago in microseconds
query2.endTimeReference = query2.TIME_RELATIVE_NOW;
query2.endTime = 0; // now
query2.domain = "mozilla.org";

var result = historyService.executeQueries([query1, query2], 2, options);
Note: Keyword searching doesn't work correctly across OR queries. The current behavior does the normal query and then selects keywords from the first query and filters all the results. (In other words, the keywords from the first query are ANDed with all queries.) Keywords from subsequent query objects are ignored. This is {{template.Bug(320332)}}.

Bookmark queries

There is a quick-start for doing simple bookmark queries in Accessing Bookmarks.

The contents of bookmark folders can be retrieved by setting the "folders" member in the query object. This item is an array of folder IDs from the bookmark service. Typically, you will only have one folder ID in this list, which will given you the contents of that folder. You can set multiple folders and the result will be the intersection of all the folders.

Warning: There is an option GROUP_BY_FOLDER that purports to have an effect on bookmark queries. This isn't implemented, see {{template.Bug(331487)}}. You should always use this option if you want a bookmarks hierarchy. Lack of this option will be changed to return a flattened list of all bookmark items in all folders returned by the query.

For sorting, you will generally want to use SORT_BY_NONE (the default) since this will return items in their "natural" order as specified by the user in the bookmarks manager. Other sortings will work, however.

For bookmark queries you will generally want no query parameters to retrieve all items from the requested folder(s). When you specify exactly one folder, GROUP_BY_FOLDER, and no query parameters, the system will be more efficient querying and keeping the results up-to-date since this maps to exactly one bookmark folder.

var bookmarkService = Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"]
                                .getService(Components.interfaces.nsINavBookmarksService);
// |query| and |options| are objects created in the previous section
query.setFolders([bookmarkService.toolbarRoot], 1);
options.setGroupingMode([options.GROUP_BY_FOLDER], 1);
var result = historyService.executeQuery(query, options);

Serializing queries

Query and options objects can be serialized into a string starting with "place:" using queriesToQueryString. The resulting string can be stored or bookmarked. When a "place:" URI is bookmarked, it will expand to the results of the query when it is opened by the user. The original objects can be deserialized from the string using queryStringToQueries.

You should never manually construct "place:" URI strings. The format is subject to change. Moreover, if you save the query, the URI should be normalized so it will match other queries of the same parameters and can be updated properly if it is bookmarked. If you need one, create the proper objects and call the history service to serialize it for you. If the format is changed, queries stored as bookmarks or associated with annotations may be automatically migrated, so you shouldn't assume that just because these strings are stored, that they're guaranteed to be static. The format is guaranteed to change between alpha 1 and Firefox 2.0

Be careful, queryStringToQueries may not return any query objects if the string was empty. Your code should handle this case. There will always be an options structure returned. If no options were specified, it will have the default values. If there were no query parameters specified but the input string was not empty (there were options) you may get one query object returned, containing the default query values.

Example of serializing and deserializing two queries and an options object:

var queryString = historyService.queriesToQueryStrings([query1, query2], 2, options);

var queriesRef = { };
var queryCountRef = { };
var optionsRef = { };
historyService.queryStringToQueries(queryString, queriesRef, queryCountRef, optionsRef);
// now use queriesRef.value, optionsRef.value

Using the results

The most common way to use results is to implement a view. There is a built-in view that will put results in tree controls, and you can also implement your own. See Places:Views for more on this. This section discusses how to access the result directly, for example, if you are creating your own view or are processing the results instead of displaying them.

Note: Be careful when accessing nodes and do not keep references to them around. Notifications sent to the result from the history and bookmarks system, as well as commands executed by the programmer such as sorting may cause the structure to change and nodes may be inserted, removed, or replaced.

The nsINavHistoryResult object returned by executeQuery/executeQueries contains the list of matches to the given history or bookmarks query. These results are contained in a tree structure made up of nodes. A node's type can be retrieved using its type attribute. This type tells you what interface you can QueryInterface the node to in order to get at more detailed information:

  • nsINavHistoryResultNode: Base class for all nodes. Contains URI, title, and other general info.
  • nsINavHistoryVisitResultNode: Derived from nsINavHistoryResultNode, contains session information.
  • nsINavHistoryFullVisitResultNode: Derived from nsINavHistoryVisitResultNode, contains information about how the user navigated to this page. Note: currently unimplemented, see {{template.Bug(320831)}}.
  • nsINavHistoryContainerResultNode: General container node giving access to its children. Derived from nsINavHistoryResultNode.
  • nsINavHistoryQueryResultNode: A type of container representing a query of the history system. It allows you to get the query options and parameters.
  • nsINavHistoryFolderResultNode: Derived from nsINavHistoryQueryResultNode, this represents a special type of query mapping to the exact contents of one bookmarks folder. It gives easy access to its folder ID, and also updates itself more efficiently than a general query.

Example of detecting the type of a node

var Ci = Components.interfaces;
switch(node.type)
  case node.RESULT_TYPE_URI:
    dump("URI result " + node.uri + "\n");
    break;
  case node.RESULT_TYPE_VISIT:
    var visit = node.QueryInterface(Ci.nsINavHistoryVisitResultNode);
    dump("Visit result " + node.uri + " session = " + visit.sessionId + "\n");
    break;
  case node.RESULT_TYPE_FULL_VISIT:
    var fullVisit = node.QueryInterface(Ci.nsINavHistoryFullVisitResultNode);
    dump("Full visit result " + node.uri + " session = " + fullVisit.sessionId + " transitionType = " +
         fullVisit.transitionType + "\n");
    break;
  case node.RESULT_TYPE_HOST:
    var container = node.QueryInterface(Ci.nsINavHistoryContainerResultNode);
    dump("Host " + container.title + "\n");
    break;
  case node.RESULT_TYPE_REMOTE_CONTAINER:
    var container = node.QueryInterface(Ci.nsINavHistoryContainerResultNode);
    dump("Remote container " + container.title + " type = " + container.remoteContainerType + "\n");
    break;
  case node.RESULT_TYPE_QUERY:
    var query = node.QueryInterface(Ci.nsINavHistoryQueryResultNode);
    dump("Query, place URI = " + query.uri + "\n");
    break;
  case node.RESULT_TYPE_FOLDER:
    // note that the folder is also a query and so has a query and an options structure
    var folder = node.QueryInterface(Ci.nsINavHistoryFolderResultNode);
    dump("Folder " + folder.title + " id = " + folder.folderId + "\n");
    break;
  case node.RESULT_TYPE_SEPARATOR:
    dump("-----------\n");
    break;
}

Containers

Containers hold lists of other containers and result nodes. Each result has a container representing the root of the query. It can be retrieved using the root attribute of the result. For general queries, this root container is a nsINavHistoryQueryResultNode with the query parameters and options that you supplied in the original query. For queries mapping to one bookmark folder, this will be a nsINavHistoryFolderResultNode.

Containers can be open or closed. This corresponds to the open and closed state in a tree view, and can also be mapped to showing and hiding menus. To get at a container's contents, you must first open the container. Most container types populate themselves lazily, so opening a container actually corresponds to executing the given query. While a container is open, it will listen to the history and bookmarks systems' notifications and modify their contents to keep themselves up-to-date. For this reason, it is best to close a container as soon as you are done with it, since it will give better performance. If you close a container and re-open it before any history or bookmark change notifications come, the results will generally still be there and this operation will be fast.

Example of traversing a container:

var cont = result.root;
cont.containerOpen = true;
for (var i = 0; i < cont.childCount; i ++) {
  var node = cont.getChild(i);
  dump(node.title + "\n");
}
cont.containerOpen = false;

The result view interface

If you are mapping a result into UI, you can implement the nsINavHistoryResultViewer interface and attach it to the result with the nsINavHistoryResult.viewer attribute. This viewer will be called when the result tree changes, either as a result of user action or as a result of notifications from the bookmarks and history systems. Your implementation would then reflect these changes in the UI.

A prepackaged view interface for a nsITreeBoxObject is provided that manages the complex view requirements of a tree. This object's interface is nsINavHistoryResultTreeViewer (a descendent of nsINavHistoryResultViewer) and can be created using the contract @mozilla.org/browser/nav-history/result-tree-viewer;1.

var treeviewer =
  Components.classes["@mozilla.org/browser/nav-history/result-tree-viewer;1"]
            .createInstance(Components.interfaces.nsINavHistoryResultTreeViewer);
result.viewer = treeviewer;
mytree.view = treeviewer.QueryInterface(Components.interfaces.nsITreeView);

Both the result and the tree will register themselves with the viewer object using result attribute and the setTree method respectively. Do not set these explicitly.

Revision Source

<p>
{{template.Firefox3()}}
</p><p>Firefox history and bookmarks data is accessed using the "<a href="en/Places">Places</a>" query APIs. These APIs provide the ability to execute complex queries against history, bookmarks or both combined (targeting Firefox Alpha 6). The result of a query is an object which will contain a flat list, or tree structure of the matching data. The definitions of the query API and the resulting data structures is available in {{template.Source("toolkit/components/places/public/nsINavHistoryService.idl")}}. This page provides an introduction to some common operations, and example usage of the core APIs.
</p>
<h3 name="Executing_a_query"> Executing a query </h3>
<p>Places queries have several basic parts:
</p>
<ul><li> The query object: <code>nsINavHistoryQuery</code>, holds the search parameters
</li><li> The query options: <code>nsINavHistoryQueryOptions</code>, allows configuration of the search result
</li><li> The history service: <code>nsINavHistoryService</code>, executes the query
</li></ul>
<p>The first first step is to create the query and options, and fill them with the parameters you want. Use <code>nsINavHistoryService.getNewQuery</code> and <code>nsINavHistoryService.getNewQueryOptions</code> to retrieve empty objects. The defaults for these objects will result in a query that returns all of your browser history in a flat list:
</p>
<pre>var historyService = Components.classes["@mozilla.org/browser/nav-history-service;1"]
                               .getService(Components.interfaces.nsINavHistoryService);

// no query parameters will get all history
// XXX default sorting is... ?
var options = historyService.getNewQueryOptions();

// no query parameters will return everything
var query = historyService.getNewQuery();

// execute the query
var result = historyService.executeQuery(query, options);

</pre>
<h4 name="Result_types"> Result types </h4>
<p>The query options structure <code>nsINavHistoryQueryOptions</code> has an attribute <code>resultType</code> for the type of the results that you want. It is important to understand the differences between these:
</p>
<ul><li> <b>RESULTS_AS_URI</b>: This is the default, and means that you get one result node of type <code>RESULT_TYPE_URI</code> for each URI that matches the query. The visit date for each node will be the last visit date for that URL. You will definitely want this when showing bookmark folders.
</li><li> <b>RESULTS_AS_VISIT</b>: This means that you want one entry for each time a page was visited matching the given query. Therefore, you may get duplicate entries for URLs, each with a different date. The nodes will be of type <code>RESULT_TYPE_VISIT</code> which will give you access to a session ID for each visit. This session ID is the same for all pages that were reached by clicking links. A new session starts when the user types a new URL or follows a bookmark. It is used to compute the dividing lines in the places history view.
</li><li> <b>RESULTS_AS_FULL_VISIT</b>: This is the same as visits but the results will be of type <code>RESULT_TYPE_FULL_VISIT</code> and will have additional information about the visit, such as the referring visit, and how the transition happened (typed, redirect, link, etc). This information is typically not needed and causes the result nodes (of which there may be many) to be larger, which is why it is a separate option.
</li></ul>
<p>The named constants are properties of <code>nsINavHistoryQueryOptions</code>, for example <code>Components.interfaces.nsINavHistoryQueryOptions.RESULTS_AS_VISIT</code>.
</p>
<h4 name="Query_parameters"> Query parameters </h4>
<dl><dd><div class="note"><b>Note</b>: The query object interface is subject to change. In its current form it is not very expressive. Implementing a better interface is {{template.Bug(317830)}}.</div>
</dd></dl>
<p>You can pass one or more <code>nsINavHistoryQuery</code> objects to <code>nsINavHistoryService.executeQuery</code>/<code>executeQueries</code>. Within one query object, all parameters are <i>AND</i>ed together. The conditions for different query objects are then <i>OR</i>ed together. This allows for a simpler implementation and interface than a full logical operation with nested clauses while still being expressive.
</p><p>Example of querying for any pages I've visited that contain the word "firefox" in the title/URL or that I've visited today from mozilla.org.
</p>
<pre>// first query object searches for "firefox" in title/URL
var query1 = historyService.getNewQuery();
query1.searchTerms = "firefox";

// second query object searches for visited in past 24 hours AND from mozilla.org
var query2 = historyService.getNewQuery();
query2.beginTimeReference = query2.TIME_RELATIVE_NOW;
query2.beginTime = -24 * 60 * 60 * 1000000; // 24 hours ago in microseconds
query2.endTimeReference = query2.TIME_RELATIVE_NOW;
query2.endTime = 0; // now
query2.domain = "mozilla.org";

var result = historyService.executeQueries([query1, query2], 2, options);
</pre>
<div class="note"><b>Note</b>: Keyword searching doesn't work correctly across <i>OR</i> queries. The current behavior does the normal query and then selects keywords from the first query and filters all the results. (In other words, the keywords from the first query are <i>AND</i>ed with all queries.) Keywords from subsequent query objects are ignored. This is {{template.Bug(320332)}}.</div>
<h4 name="Bookmark_queries"> Bookmark queries </h4>
<p>There is a quick-start for doing simple bookmark queries in <a href="en/Places/Accessing_Bookmarks">Accessing Bookmarks</a>.
</p><p>The contents of bookmark folders can be retrieved by setting the "folders" member in the query object. This item is an array of folder IDs from the bookmark service. Typically, you will only have one folder ID in this list, which will given you the contents of that folder. You can set multiple folders and the result will be the intersection of all the folders.
</p>
<div class="warning"><b>Warning</b>: There is an option GROUP_BY_FOLDER that purports to have an effect on bookmark queries. This isn't implemented, see {{template.Bug(331487)}}. You should always use this option if you want a bookmarks hierarchy. Lack of this option will be changed to return a flattened list of all bookmark items in all folders returned by the query.</div>
<p>For sorting, you will generally want to use <code>SORT_BY_NONE</code> (the default) since this will return items in their "natural" order as specified by the user in the bookmarks manager. Other sortings will work, however.
</p><p>For bookmark queries you will generally want no query parameters to retrieve all items from the requested folder(s). When you specify exactly one folder, <code>GROUP_BY_FOLDER</code>, and no query parameters, the system will be more efficient querying and keeping the results up-to-date since this maps to exactly one bookmark folder.
</p>
<pre>var bookmarkService = Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"]
                                .getService(Components.interfaces.nsINavBookmarksService);
// |query| and |options| are objects created in the previous section
query.setFolders([bookmarkService.toolbarRoot], 1);
options.setGroupingMode([options.GROUP_BY_FOLDER], 1);
var result = historyService.executeQuery(query, options);
</pre>
<h3 name="Serializing_queries"> Serializing queries </h3>
<p>Query and options objects can be serialized into a string starting with "place:" using <code>queriesToQueryString</code>. The resulting string can be stored or bookmarked. When a "place:" URI is bookmarked, it will expand to the results of the query when it is opened by the user. The original objects can be deserialized from the string using <code>queryStringToQueries</code>.
</p>
<div class="note">You should never manually construct "place:" URI strings. The format is subject to change. Moreover, if you save the query, the URI should be normalized so it will match other queries of the same parameters and can be updated properly if it is bookmarked. If you need one, create the proper objects and call the history service to serialize it for you. If the format is changed, queries stored as bookmarks or associated with annotations may be automatically migrated, so you shouldn't assume that just because these strings are stored, that they're guaranteed to be static. The format is guaranteed to change between alpha 1 and Firefox 2.0</div>
<p>Be careful, <code>queryStringToQueries</code> may not return any query objects if the string was empty. Your code should handle this case. There will always be an options structure returned. If no options were specified, it will have the default values. If there were no query parameters specified but the input string was not empty (there were options) you may get one query object returned, containing the default query values.
</p><p>Example of serializing and deserializing two queries and an options object:
</p>
<pre>var queryString = historyService.queriesToQueryStrings([query1, query2], 2, options);

var queriesRef = { };
var queryCountRef = { };
var optionsRef = { };
historyService.queryStringToQueries(queryString, queriesRef, queryCountRef, optionsRef);
// now use queriesRef.value, optionsRef.value
</pre>
<h3 name="Using_the_results"> Using the results </h3>
<p>The most common way to use results is to implement a view. There is a built-in view that will put results in tree controls, and you can also implement your own. See <a href="en/Places/Views">Places:Views</a> for more on this. This section discusses how to access the result directly, for example, if you are creating your own view or are processing the results instead of displaying them.
</p><p><i>Note: Be careful when accessing nodes and do not keep references to them around. Notifications sent to the result from the history and bookmarks system, as well as commands executed by the programmer such as sorting may cause the structure to change and nodes may be inserted, removed, or replaced.</i>
</p><p>The <code>nsINavHistoryResult</code> object returned by <code>executeQuery</code>/<code>executeQueries</code> contains the list of matches to the given history or bookmarks query. These results are contained in a tree structure made up of nodes. A node's type can be retrieved using its <code>type</code> attribute. This type tells you what interface you can <code>QueryInterface</code> the node to in order to get at more detailed information:
</p>
<ul><li> <b>nsINavHistoryResultNode</b>: Base class for all nodes. Contains URI, title, and other general info.
</li><li> <b>nsINavHistoryVisitResultNode</b>: Derived from <code>nsINavHistoryResultNode</code>, contains session information.
</li><li> <b>nsINavHistoryFullVisitResultNode</b>: Derived from <code>nsINavHistoryVisitResultNode</code>, contains information about how the user navigated to this page. <i>Note: currently unimplemented, see {{template.Bug(320831)}}.</i>
</li><li> <b>nsINavHistoryContainerResultNode</b>: General container node giving access to its children. Derived from <code>nsINavHistoryResultNode</code>.
</li><li> <b>nsINavHistoryQueryResultNode</b>: A type of container representing a query of the history system. It allows you to get the query options and parameters.
</li><li> <b>nsINavHistoryFolderResultNode</b>: Derived from <code>nsINavHistoryQueryResultNode</code>, this represents a special type of query mapping to the exact contents of one bookmarks folder. It gives easy access to its folder ID, and also updates itself more efficiently than a general query.
</li></ul>
<p>Example of detecting the type of a node
</p>
<pre>var Ci = Components.interfaces;
switch(node.type)
  case node.RESULT_TYPE_URI:
    dump("URI result " + node.uri + "\n");
    break;
  case node.RESULT_TYPE_VISIT:
    var visit = node.QueryInterface(Ci.nsINavHistoryVisitResultNode);
    dump("Visit result " + node.uri + " session = " + visit.sessionId + "\n");
    break;
  case node.RESULT_TYPE_FULL_VISIT:
    var fullVisit = node.QueryInterface(Ci.nsINavHistoryFullVisitResultNode);
    dump("Full visit result " + node.uri + " session = " + fullVisit.sessionId + " transitionType = " +
         fullVisit.transitionType + "\n");
    break;
  case node.RESULT_TYPE_HOST:
    var container = node.QueryInterface(Ci.nsINavHistoryContainerResultNode);
    dump("Host " + container.title + "\n");
    break;
  case node.RESULT_TYPE_REMOTE_CONTAINER:
    var container = node.QueryInterface(Ci.nsINavHistoryContainerResultNode);
    dump("Remote container " + container.title + " type = " + container.remoteContainerType + "\n");
    break;
  case node.RESULT_TYPE_QUERY:
    var query = node.QueryInterface(Ci.nsINavHistoryQueryResultNode);
    dump("Query, place URI = " + query.uri + "\n");
    break;
  case node.RESULT_TYPE_FOLDER:
    // note that the folder is also a query and so has a query and an options structure
    var folder = node.QueryInterface(Ci.nsINavHistoryFolderResultNode);
    dump("Folder " + folder.title + " id = " + folder.folderId + "\n");
    break;
  case node.RESULT_TYPE_SEPARATOR:
    dump("-----------\n");
    break;
}
</pre>
<h4 name="Containers"> Containers </h4>
<p>Containers hold lists of other containers and result nodes. Each result has a container representing the root of the query. It can be retrieved using the <code>root</code> attribute of the result. For general queries, this root container is a <code>nsINavHistoryQueryResultNode</code> with the query parameters and options that you supplied in the original query. For queries mapping to one bookmark folder, this will be a <code>nsINavHistoryFolderResultNode</code>.
</p><p>Containers can be open or closed. This corresponds to the open and closed state in a tree view, and can also be mapped to showing and hiding menus. To get at a container's contents, you must first open the container. Most container types populate themselves lazily, so opening a container actually corresponds to executing the given query. While a container is open, it will listen to the history and bookmarks systems' notifications and modify their contents to keep themselves up-to-date. For this reason, it is best to close a container as soon as you are done with it, since it will give better performance. If you close a container and re-open it before any history or bookmark change notifications come, the results will generally still be there and this operation will be fast.
</p><p>Example of traversing a container:
</p>
<pre>var cont = result.root;
cont.containerOpen = true;
for (var i = 0; i &lt; cont.childCount; i ++) {
  var node = cont.getChild(i);
  dump(node.title + "\n");
}
cont.containerOpen = false;
</pre>
<h4 name="The_result_view_interface"> The result view interface </h4>
<p>If you are mapping a result into UI, you can implement the <code>nsINavHistoryResultViewer</code> interface and attach it to the result with the <code>nsINavHistoryResult.viewer</code> attribute. This viewer will be called when the result tree changes, either as a result of user action or as a result of notifications from the bookmarks and history systems. Your implementation would then reflect these changes in the UI.
</p><p>A prepackaged view interface for a <a href="en/NsITreeBoxObject">nsITreeBoxObject</a> is provided that manages the complex view requirements of a tree. This object's interface is <code>nsINavHistoryResultTreeViewer</code> (a descendent of <code>nsINavHistoryResultViewer</code>) and can be created using the contract <code>@mozilla.org/browser/nav-history/result-tree-viewer;1</code>.
</p>
<pre>var treeviewer =
  Components.classes["@mozilla.org/browser/nav-history/result-tree-viewer;1"]
            .createInstance(Components.interfaces.nsINavHistoryResultTreeViewer);
result.viewer = treeviewer;
mytree.view = treeviewer.QueryInterface(Components.interfaces.nsITreeView);
</pre>
<p>Both the result and the tree will register themselves with the viewer object using <code>result</code> attribute and the <code>setTree</code> method respectively. Do not set these explicitly.
</p>
Revert to this revision