History-based bookmarks implementation for Places. Bug 314553, r=brettw.

This commit is contained in:
bryner%brianryner.com 2005-11-16 22:53:47 +00:00
parent 9d6eceb84f
commit d0295dcf29
12 changed files with 1814 additions and 230 deletions

View File

@ -87,3 +87,9 @@
#define NS_NAVHISTORY_CONTRACTID \
"@mozilla.org/browser/nav-history;1"
#define NS_NAVBOOKMARKSSERVICE_CID \
{ 0x9de95a0c, 0x39a4, 0x4d64, {0x9a, 0x53, 0x17, 0x94, 0x0d, 0xd7, 0xca, 0xbb}}
#define NS_NAVBOOKMARKSSERVICE_CONTRACTID \
"@mozilla.org/browser/nav-bookmarks-service;1"

View File

@ -43,6 +43,7 @@
#include "nsForwardProxyDataSource.h"
#ifdef MOZ_PLACES
#include "nsNavHistory.h"
#include "nsNavBookmarks.h"
#endif
#ifdef XP_WIN
#include "nsWindowsShellService.h"
@ -77,6 +78,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsBookmarksService, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsForwardProxyDataSource, Init)
#ifdef MOZ_PLACES
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsNavHistory, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsNavBookmarks, Init)
#endif
#ifdef XP_WIN
NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindowsShellService)
@ -137,6 +139,11 @@ static const nsModuleComponentInfo components[] =
NS_NAVHISTORY_CID,
"@mozilla.org/autocomplete/search;1?name=history",
nsNavHistoryConstructor },
{ "Browser Bookmarks Service",
NS_NAVBOOKMARKSSERVICE_CID,
NS_NAVBOOKMARKSSERVICE_CONTRACTID,
nsNavBookmarksConstructor },
#endif
{ "Bookmarks",

View File

@ -49,6 +49,7 @@ XPIDL_MODULE = places
XPIDLSRCS = \
nsIAnnotationService.idl \
nsINavHistory.idl \
nsINavBookmarksService.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,264 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Places.
*
* The Initial Developer of the Original Code is
* Google Inc.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brian Ryner <bryner@brianryner.com> (original author)
*
* 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
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
interface nsIURI;
interface nsINavHistoryResult;
/**
* Observer for bookmark changes.
*/
[scriptable, uuid(c4159523-bedb-4e3f-a163-087e6d89e8a6)]
interface nsINavBookmarkObserver : nsISupports
{
/**
* Notify this observer that a batch transaction has started.
* Other notifications will be sent during the batch change,
* but the observer is guaranteed that onEndUpdateBatch() will be called
* at the completion of changes.
*/
void onBeginUpdateBatch();
/**
* Notify this observer that a batch transaction has ended.
*/
void onEndUpdateBatch();
/**
* True requests that you want to get called for all updates, false if you
* don't necessarily care about which exact things changed during a batch
* update. If false, this will sometimes not call you for things in between
* onBeginUpdateBatch and onEndUpdateBatch. You'll still get the begin and
* end, so you'll know something changed.
*
* Lots of observers don't care about what changes, only that something
* changed so they can update their UI. This allows delete operations to
* avoid iterating over every item, and just doing a single bulk SQL DELETE
* command, which is much more efficient.
*
* Note that you still might get called if you say false in some situations.
* This only skips certain time-consuming notifications if NO observers
* want the information.
*/
readonly attribute boolean wantAllDetails;
/**
* Notify this observer that the bookmark was added.
* Called after the actual add took place.
*
* @param bookmark The bookmark item that was added.
* @param folder The folder that the item was added to.
* @param index The item's index in the folder.
*/
void onItemAdded(in nsIURI bookmark, in PRInt64 folder, in long index);
/**
* Notify this observer that the bookmark was removed.
* Called after the actual remove took place.
*
* @param bookmark The bookmark item will be removed.
* @param folder The folder that the item was removed from.
* @param index The bookmark's index in the folder.
*/
void onItemRemoved(in nsIURI bookmark, in PRInt64 container, in long index);
/**
* Notify this observer that a bookmark's information has changed. This
* will be called whenever any attributes like "title" are changed.
*
* @param bookmark The bookmark which changed.
* @param property The property which changed.
*/
void onItemChanged(in nsIURI bookmark, in ACString property);
/**
* Notify this observer that a bookmark folder has been added.
* @param folder The id of the folder that was added.
* @param parent The id of the folder's parent.
* @param index The folder's index inside its parent.
*/
void onFolderAdded(in PRInt64 folder, in PRInt64 parent, in long index);
/**
* Notify this observer that a bookmark folder has been removed.
* @param folder The id of the folder that was removed.
* @param parent The id of the folder's old parent.
* @param index The folder's old index in its parent.
*/
void onFolderRemoved(in PRInt64 folder, in PRInt64 parent, in long index);
/**
* Notify this observer that a bookmark folder has been moved.
* @param folder The id of the folder that was moved.
* @param newParent The id of the folder's new paremt.
* @param index The folder's index inside newParent.
*/
void onFolderMoved(in PRInt64 folder, in PRInt64 newParent, in long index);
/**
* Notify this observer that a bookmark folder's information has changed.
* This will be called whenever any attributes like "title" are changed.
* @param folder The id of the folder that was changed.
* @param property The property that was changed.
*/
void onFolderChanged(in PRInt64 folder, in ACString property);
};
/**
* The BookmarksService interface provides methods for managing bookmarked
* history items. Bookmarks consist of a set of user-customizable
* folders. A URI in history can be contained in one or more such folders.
*/
[scriptable, uuid(5feca204-b735-40e8-921f-8b057eedffe2)]
interface nsINavBookmarksService : nsISupports
{
/**
* The folder ID of the bookmarks root.
*/
readonly attribute PRInt64 bookmarksRoot;
/**
* A query result containing the bookmarks folder tree.
*/
readonly attribute nsINavHistoryResult bookmarks;
/**
* Inserts a child item into the given folder.
* @param folder The id of the parent folder
* @param item The URI to insert
* @param index The index to insert at, or -1 to append
*/
void insertItem(in PRInt64 folder, in nsIURI item, in PRInt32 index);
/**
* Removes a child item from the given folder.
* @param folder The folder to remove the child from
* @param item The child item to remove
*/
void removeItem(in PRInt64 folder, in nsIURI child);
/**
* Creates a new child folder and inserts it under the given parent.
* @param parent The id of the parent folder
* @param name The name of the new folder
* @param index The index to insert at, or -1 to append
* @returns the ID of the newly-inserted folder
*/
PRInt64 createFolder(in PRInt64 parent, in AString name, in PRInt32 index);
/**
* Removes a folder from the bookmarks tree.
* @param folder The id of the folder to remove.
*/
void removeFolder(in PRInt64 folder);
/**
* Moves a folder to a different container, preserving its contents.
* @param folder The folder to move
* @param newParent The id of the folder's new parent
* @param index The folder's index under newParent, or -1 to append
*/
void moveFolder(in PRInt64 folder, in PRInt64 newParent, in PRInt32 index);
/**
* Set the history/bookmark title for a URI. The new title will be used
* anywhere the URI is shown in bookmarks or history.
* @param uri The URI whose name should be set
* @param title The new title for the URI
*/
void setItemTitle(in nsIURI uri, in AString title);
/**
* Get the history/bookmark title for the URI.
* @param uri The URI whose title should be retrieved
* @returns The title for the URI.
*/
AString getItemTitle(in nsIURI uri);
/**
* Set the title for a bookmark folder.
* @param folder The folder whose title should be set
* @param title The new title for the folder
*/
void setFolderTitle(in PRInt64 folder, in AString title);
/**
* Get the title for a bookmark folder.
* @param folder The folder whose title should be retrieved
* @returns The title for the folder
*/
AString getFolderTitle(in PRInt64 folder);
/**
* Returns true if the given URI is in any bookmark folder.
*/
boolean isBookmarked(in nsIURI uri);
/**
* Returns the list of folder ids that contain the given URI.
*/
void getBookmarkCategories(in nsIURI uri, out PRUint32 count,
[array, retval, size_is(count)] out PRInt64 categories);
/**
* Adds a bookmark observer. The bookmark service will keep an owning
* reference to the observer.
*/
void addObserver(in nsINavBookmarkObserver aObserver);
/**
* Removes a bookmark observer.
*/
void removeObserver(in nsINavBookmarkObserver aObserver);
/**
* Causes observers to be notified of a beginUpdateBatch when a lot of things
* are about to change. Calls can be nested, observers will only be
* notified when all batches begin/end.
*/
void beginUpdateBatch();
/**
* Causes observers to be notified of an endUpdateBatch when a batch is
* done changing. Should match beginUpdateBatch or bad things will happen.
*/
void endUpdateBatch();
};

View File

@ -55,7 +55,8 @@ interface nsINavHistoryResultNode : nsISupports
const PRUint32 RESULT_TYPE_VISIT = 1;
const PRUint32 RESULT_TYPE_HOST = 2;
const PRUint32 RESULT_TYPE_DAY = 3;
readonly attribute PRUInt32 type;
const PRUint32 RESULT_TYPE_FOLDER = 4;
readonly attribute PRUint32 type;
/**
* URL of the web page in question. Empty for all other types, including
@ -305,6 +306,101 @@ interface nsINavHistoryQuery : nsISupports
readonly attribute boolean hasDomain;
};
/**
* This object represents the global options for executing a query.
*/
[scriptable, uuid(25fd4de4-33b0-475e-a63d-2bcb1d123e0d)]
interface nsINavHistoryQueryOptions : nsISupports
{
/**
* Grouping by day. The results will be an array of nsINavHistoryResults with
* type = RESULT_TYPE_DAY, one for each day where there are results. These
* will have children of corresponding to the search results of that day.
*/
const PRInt32 GROUP_BY_DAY = 0;
/**
* Groping by exact host. The results will be an array of nsINavHistoryResults
* with type = RESULT_TYPE_HOST, one for each unique host (for example,
* "bugzilla.mozilla.org" and "www.mozilla.org" will be separate). The
* children of these will correspond to the results for each host.
*/
const PRInt32 GROUP_BY_HOST = 1;
/**
* Grouping by toplevel domain. Similar to GROUP_BY_HOST, but there will be
* one result for each toplevel domain (mozilla.org will be one entry, and
* will contain results including, for example, "bugzilla.mozilla.org" and
* "www.mozilla.org").
*/
const PRInt32 GROUP_BY_DOMAIN = 2;
/**
* Group by bookmark folder.
* This should only be used for queries which have onlyBookmarked set.
*/
const PRInt32 GROUP_BY_FOLDER = 3;
/**
* You can ask for the results to be pre-sorted. Since the DB has indices
* of many items, it can produce sorted results almost for free. These should
* be self-explanatory.
*
* Note: re-sorting is slower, as is sorting by title or when you have a
* host name.
*/
const PRInt32 SORT_BY_NONE = 0;
const PRInt32 SORT_BY_TITLE_ASCENDING = 1;
const PRInt32 SORT_BY_TITLE_DESCENDING = 2;
const PRInt32 SORT_BY_DATE_ASCENDING = 3;
const PRInt32 SORT_BY_DATE_DESCENDING = 4;
const PRInt32 SORT_BY_URL_ASCENDING = 5;
const PRInt32 SORT_BY_URL_DESCENDING = 6;
const PRInt32 SORT_BY_VISITCOUNT_ASCENDING = 7;
const PRInt32 SORT_BY_VISITCOUNT_DESCENDING = 8;
/**
* "URL" results, one for each URL visited in the range.
*/
const PRInt32 RESULT_TYPE_URL = 0;
/**
* "Visit" results, with one for each time a page was visited
* (this will often give you multiple results for one URL).
*/
const PRInt32 RESULT_TYPE_VISIT = 1;
/**
* Set the grouping mode to be used for this query.
* Grouping mode is an array of GROUP_BY_* values that specifies the structure
* of the tree you want. For example, an array consisting of
* [GROUP_BY_DAY, GROUP_BY_DOMAIN] will give you a tree whose first level is
* a list of days, and whose second level is a list of domains, and whose
* third level is a list of pages in those domains. If you don't want a tree,
* you can specify an empty array.
*/
void setGroupingMode([const,array,size_is(groupCount)] in PRInt32 groupingMode,
in PRUint32 groupCount);
/**
* Set the sorting mode to be used for this query.
* mode is one of SORT_BY_*
*/
void setSortingMode(in PRInt32 mode);
/**
* Sets the result type. One of RESULT_TYPE_*.
*/
void setResultType(in PRInt32 type);
/**
* When set, allows items with "place:" URIs to appear as containers,
* with the container's contents filled in from the stored query.
* If not set, these will appear as normal items.
*/
void setExpandPlaces(in boolean expand);
};
[scriptable, uuid(C51F54CB-5E89-4B20-A37C-1343888935B7)]
interface nsINavHistory : nsISupports
{
@ -325,74 +421,23 @@ interface nsINavHistory : nsISupports
*/
boolean canAddURI(in nsIURI aURI);
/**
* Grouping by day. The results will be an array of nsINavHistoryResults with
* type = RESULT_TYPE_DAY, one for each day where there are results. These
* will have children of corresponding to the search results of that day.
*/
const PRUint32 GROUP_BY_DAY = 0;
/**
* Groping by exact host. The results will be an array of nsINavHistoryResults
* with type = RESULT_TYPE_HOST, one for each unique host (for example,
* "bugzilla.mozilla.org" and "www.mozilla.org" will be separate). The
* children of these will correspond to the results for each host.
*/
const PRUint32 GROUP_BY_HOST = 1;
/**
* Grouping by toplevel domain. Similar to GROUP_BY_HOST, but there will be
* one result for each toplevel domain (mozilla.org will be one entry, and
* will contain results including, for example, "bugzilla.mozilla.org" and
* "www.mozilla.org").
*/
const PRUint32 GROUP_BY_DOMAIN = 2;
/**
* You can ask for the results to be pre-sorted. Since the DB has indices
* of many items, it can produce sorted results almost for free. These should
* be self-explanatory.
*
* Note: re-sorting is slower, as is sorting by title or when you have a
* host name.
*/
const PRUint32 SORT_BY_NONE = 0;
const PRUint32 SORT_BY_TITLE_ASCENDING = 1;
const PRUint32 SORT_BY_TITLE_DESCENDING = 2;
const PRUint32 SORT_BY_DATE_ASCENDING = 3;
const PRUint32 SORT_BY_DATE_DESCENDING = 4;
const PRUint32 SORT_BY_URL_ASCENDING = 5;
const PRUint32 SORT_BY_URL_DESCENDING = 6;
const PRUint32 SORT_BY_VISITCOUNT_ASCENDING = 7;
const PRUint32 SORT_BY_VISITCOUNT_DESCENDING = 8;
/**
* This returns a new query object that you can pass to executeQuer[y/ies].
* It will be initialized to all empty (so using it will give you all history).
*/
nsINavHistoryQuery getNewQuery();
/**
* This returns a new options object that you can pass to executeQuer[y/ies]
* after setting the desired options.
*/
nsINavHistoryQueryOptions getNewQueryOptions();
/**
* Executes a single query.
*
* sortingMode is one of SORT_BY_*
*
* Grouping mode is an array of GROUP_BY_* values that specifies the structure
* of the tree you want. For example, an array consisting of
* [GROUP_BY_DAY, GROUP_BY_DOMAIN] will give you a tree whose first level is
* a list of days, and whose second level is a list of domains, and whose
* third level is a list of pages in those domains. If you don't want a tree,
* you can specify an empty array.
*
* asVisits is what to return for the pages. If false, this will return "URL"
* results, one for each URL visited in the range. If true, this will return
* "visit" results, with one for each time a page was visited (this will
* often give you multiple results for one URL).
*/
nsINavHistoryResult executeQuery(in nsINavHistoryQuery aQuery,
[const,array,size_is(aGroupCount)] in PRInt32 aGroupingMode, in PRUint32 aGroupCount,
in PRInt32 aSortingMode, in PRBool aAsVisits);
in nsINavHistoryQueryOptions options);
/**
* Executes an array of queries. All of the query objects are ORed
@ -401,8 +446,7 @@ interface nsINavHistory : nsISupports
*/
nsINavHistoryResult executeQueries(
[const,array,size_is(aQueryCount)] in nsINavHistoryQuery aQueries, in PRUint32 aQueryCount,
[const,array,size_is(aGroupCount)] in PRInt32 aGroupingMode, in PRUint32 aGroupCount,
in PRInt32 aSortingMode, in PRBool aAsVisits);
in nsINavHistoryQueryOptions options);
/**
* Adds a history observer. The history service will keep an owning

View File

@ -55,7 +55,8 @@ interface nsINavHistoryResultNode : nsISupports
const PRUint32 RESULT_TYPE_VISIT = 1;
const PRUint32 RESULT_TYPE_HOST = 2;
const PRUint32 RESULT_TYPE_DAY = 3;
readonly attribute PRUInt32 type;
const PRUint32 RESULT_TYPE_FOLDER = 4;
readonly attribute PRUint32 type;
/**
* URL of the web page in question. Empty for all other types, including
@ -305,6 +306,101 @@ interface nsINavHistoryQuery : nsISupports
readonly attribute boolean hasDomain;
};
/**
* This object represents the global options for executing a query.
*/
[scriptable, uuid(25fd4de4-33b0-475e-a63d-2bcb1d123e0d)]
interface nsINavHistoryQueryOptions : nsISupports
{
/**
* Grouping by day. The results will be an array of nsINavHistoryResults with
* type = RESULT_TYPE_DAY, one for each day where there are results. These
* will have children of corresponding to the search results of that day.
*/
const PRInt32 GROUP_BY_DAY = 0;
/**
* Groping by exact host. The results will be an array of nsINavHistoryResults
* with type = RESULT_TYPE_HOST, one for each unique host (for example,
* "bugzilla.mozilla.org" and "www.mozilla.org" will be separate). The
* children of these will correspond to the results for each host.
*/
const PRInt32 GROUP_BY_HOST = 1;
/**
* Grouping by toplevel domain. Similar to GROUP_BY_HOST, but there will be
* one result for each toplevel domain (mozilla.org will be one entry, and
* will contain results including, for example, "bugzilla.mozilla.org" and
* "www.mozilla.org").
*/
const PRInt32 GROUP_BY_DOMAIN = 2;
/**
* Group by bookmark folder.
* This should only be used for queries which have onlyBookmarked set.
*/
const PRInt32 GROUP_BY_FOLDER = 3;
/**
* You can ask for the results to be pre-sorted. Since the DB has indices
* of many items, it can produce sorted results almost for free. These should
* be self-explanatory.
*
* Note: re-sorting is slower, as is sorting by title or when you have a
* host name.
*/
const PRInt32 SORT_BY_NONE = 0;
const PRInt32 SORT_BY_TITLE_ASCENDING = 1;
const PRInt32 SORT_BY_TITLE_DESCENDING = 2;
const PRInt32 SORT_BY_DATE_ASCENDING = 3;
const PRInt32 SORT_BY_DATE_DESCENDING = 4;
const PRInt32 SORT_BY_URL_ASCENDING = 5;
const PRInt32 SORT_BY_URL_DESCENDING = 6;
const PRInt32 SORT_BY_VISITCOUNT_ASCENDING = 7;
const PRInt32 SORT_BY_VISITCOUNT_DESCENDING = 8;
/**
* "URL" results, one for each URL visited in the range.
*/
const PRInt32 RESULT_TYPE_URL = 0;
/**
* "Visit" results, with one for each time a page was visited
* (this will often give you multiple results for one URL).
*/
const PRInt32 RESULT_TYPE_VISIT = 1;
/**
* Set the grouping mode to be used for this query.
* Grouping mode is an array of GROUP_BY_* values that specifies the structure
* of the tree you want. For example, an array consisting of
* [GROUP_BY_DAY, GROUP_BY_DOMAIN] will give you a tree whose first level is
* a list of days, and whose second level is a list of domains, and whose
* third level is a list of pages in those domains. If you don't want a tree,
* you can specify an empty array.
*/
void setGroupingMode([const,array,size_is(groupCount)] in PRInt32 groupingMode,
in PRUint32 groupCount);
/**
* Set the sorting mode to be used for this query.
* mode is one of SORT_BY_*
*/
void setSortingMode(in PRInt32 mode);
/**
* Sets the result type. One of RESULT_TYPE_*.
*/
void setResultType(in PRInt32 type);
/**
* When set, allows items with "place:" URIs to appear as containers,
* with the container's contents filled in from the stored query.
* If not set, these will appear as normal items.
*/
void setExpandPlaces(in boolean expand);
};
[scriptable, uuid(C51F54CB-5E89-4B20-A37C-1343888935B7)]
interface nsINavHistory : nsISupports
{
@ -325,74 +421,23 @@ interface nsINavHistory : nsISupports
*/
boolean canAddURI(in nsIURI aURI);
/**
* Grouping by day. The results will be an array of nsINavHistoryResults with
* type = RESULT_TYPE_DAY, one for each day where there are results. These
* will have children of corresponding to the search results of that day.
*/
const PRUint32 GROUP_BY_DAY = 0;
/**
* Groping by exact host. The results will be an array of nsINavHistoryResults
* with type = RESULT_TYPE_HOST, one for each unique host (for example,
* "bugzilla.mozilla.org" and "www.mozilla.org" will be separate). The
* children of these will correspond to the results for each host.
*/
const PRUint32 GROUP_BY_HOST = 1;
/**
* Grouping by toplevel domain. Similar to GROUP_BY_HOST, but there will be
* one result for each toplevel domain (mozilla.org will be one entry, and
* will contain results including, for example, "bugzilla.mozilla.org" and
* "www.mozilla.org").
*/
const PRUint32 GROUP_BY_DOMAIN = 2;
/**
* You can ask for the results to be pre-sorted. Since the DB has indices
* of many items, it can produce sorted results almost for free. These should
* be self-explanatory.
*
* Note: re-sorting is slower, as is sorting by title or when you have a
* host name.
*/
const PRUint32 SORT_BY_NONE = 0;
const PRUint32 SORT_BY_TITLE_ASCENDING = 1;
const PRUint32 SORT_BY_TITLE_DESCENDING = 2;
const PRUint32 SORT_BY_DATE_ASCENDING = 3;
const PRUint32 SORT_BY_DATE_DESCENDING = 4;
const PRUint32 SORT_BY_URL_ASCENDING = 5;
const PRUint32 SORT_BY_URL_DESCENDING = 6;
const PRUint32 SORT_BY_VISITCOUNT_ASCENDING = 7;
const PRUint32 SORT_BY_VISITCOUNT_DESCENDING = 8;
/**
* This returns a new query object that you can pass to executeQuer[y/ies].
* It will be initialized to all empty (so using it will give you all history).
*/
nsINavHistoryQuery getNewQuery();
/**
* This returns a new options object that you can pass to executeQuer[y/ies]
* after setting the desired options.
*/
nsINavHistoryQueryOptions getNewQueryOptions();
/**
* Executes a single query.
*
* sortingMode is one of SORT_BY_*
*
* Grouping mode is an array of GROUP_BY_* values that specifies the structure
* of the tree you want. For example, an array consisting of
* [GROUP_BY_DAY, GROUP_BY_DOMAIN] will give you a tree whose first level is
* a list of days, and whose second level is a list of domains, and whose
* third level is a list of pages in those domains. If you don't want a tree,
* you can specify an empty array.
*
* asVisits is what to return for the pages. If false, this will return "URL"
* results, one for each URL visited in the range. If true, this will return
* "visit" results, with one for each time a page was visited (this will
* often give you multiple results for one URL).
*/
nsINavHistoryResult executeQuery(in nsINavHistoryQuery aQuery,
[const,array,size_is(aGroupCount)] in PRInt32 aGroupingMode, in PRUint32 aGroupCount,
in PRInt32 aSortingMode, in PRBool aAsVisits);
in nsINavHistoryQueryOptions options);
/**
* Executes an array of queries. All of the query objects are ORed
@ -401,8 +446,7 @@ interface nsINavHistory : nsISupports
*/
nsINavHistoryResult executeQueries(
[const,array,size_is(aQueryCount)] in nsINavHistoryQuery aQueries, in PRUint32 aQueryCount,
[const,array,size_is(aGroupCount)] in PRInt32 aGroupingMode, in PRUint32 aGroupCount,
in PRInt32 aSortingMode, in PRBool aAsVisits);
in nsINavHistoryQueryOptions options);
/**
* Adds a history observer. The history service will keep an owning

View File

@ -66,11 +66,12 @@ REQUIRES = xpcom \
uconv \
$(NULL)
LOCAL_INCLUDES = -I$(DEPTH)/toolkit/components/autocomplete/src
LOCAL_INCLUDES = -I$(srcdir)/../../build
CPPSRCS = \
nsAnnotationService.cpp \
nsNavHistory.cpp \
nsNavBookmarks.cpp \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,784 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Places.
*
* The Initial Developer of the Original Code is
* Google Inc.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brian Ryner <bryner@brianryner.com> (original author)
*
* 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
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsNavBookmarks.h"
#include "nsNavHistory.h"
#include "mozStorageHelper.h"
#include "nsIServiceManager.h"
const PRInt32 nsNavBookmarks::kFindBookmarksIndex_ItemChild = 0;
const PRInt32 nsNavBookmarks::kFindBookmarksIndex_FolderChild = 1;
const PRInt32 nsNavBookmarks::kFindBookmarksIndex_Parent = 2;
const PRInt32 nsNavBookmarks::kFindBookmarksIndex_Position = 3;
const PRInt32 nsNavBookmarks::kGetFolderInfoIndex_FolderID = 0;
const PRInt32 nsNavBookmarks::kGetFolderInfoIndex_Title = 1;
// These columns sit to the right of the kGetInfoIndex_* columns.
const PRInt32 nsNavBookmarks::kGetChildrenIndex_Position = 6;
const PRInt32 nsNavBookmarks::kGetChildrenIndex_ItemChild = 7;
const PRInt32 nsNavBookmarks::kGetChildrenIndex_FolderChild = 8;
const PRInt32 nsNavBookmarks::kGetChildrenIndex_FolderTitle = 9;
nsNavBookmarks* nsNavBookmarks::sInstance = nsnull;
nsNavBookmarks::nsNavBookmarks()
{
NS_ASSERTION(!sInstance, "Multiple nsNavBookmarks instances!");
sInstance = this;
}
nsNavBookmarks::~nsNavBookmarks()
{
NS_ASSERTION(sInstance == this, "Expected sInstance == this");
sInstance = nsnull;
}
NS_IMPL_ISUPPORTS2(nsNavBookmarks,
nsINavBookmarksService, nsINavHistoryObserver)
nsresult
nsNavBookmarks::Init()
{
nsresult rv;
nsNavHistory *history = History();
NS_ENSURE_TRUE(history, NS_ERROR_UNEXPECTED);
history->AddObserver(this); // allows us to notify on title changes
mozIStorageConnection *dbConn = DBConn();
mozStorageTransaction transaction(dbConn, PR_FALSE);
PRBool exists = PR_FALSE;
dbConn->TableExists(NS_LITERAL_CSTRING("moz_bookmarks_assoc"), &exists);
if (!exists) {
rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_bookmarks_assoc (item_child INTEGER, folder_child INTEGER, parent INTEGER, position INTEGER)"));
NS_ENSURE_SUCCESS(rv, rv);
}
dbConn->TableExists(NS_LITERAL_CSTRING("moz_bookmarks_containers"), &exists);
if (!exists) {
rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_bookmarks_containers (id INTEGER PRIMARY KEY, name LONGVARCHAR)"));
NS_ENSURE_SUCCESS(rv, rv);
}
mRoot = 0;
{
nsCOMPtr<mozIStorageStatement> statement;
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT folder_child FROM moz_bookmarks_assoc WHERE parent IS NULL"),
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
PRBool results;
rv = statement->ExecuteStep(&results);
NS_ENSURE_SUCCESS(rv, rv);
if (results) {
mRoot = statement->AsInt64(0);
}
}
if (!mRoot) {
// Create the root container
rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks_containers (name) VALUES (NULL)"));
NS_ENSURE_SUCCESS(rv, rv);
rv = dbConn->GetLastInsertRowID(&mRoot);
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString buffer;
buffer.AssignLiteral("INSERT INTO moz_bookmarks_assoc (folder_child) VALUES(");
buffer.AppendInt(mRoot);
buffer.AppendLiteral(")");
rv = dbConn->ExecuteSimpleSQL(buffer);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT a.* FROM moz_bookmarks_assoc a, moz_history h WHERE h.url = ?1 AND a.item_child = h.id"),
getter_AddRefs(mDBFindURIBookmarks));
NS_ENSURE_SUCCESS(rv, rv);
// This gigantic statement constructs a result where the first columns exactly match
// those returned by mDBGetVisitPageInfo, and additionally contains columns for
// position, item_child, and folder_child from moz_bookmarks_assoc, and name from
// moz_bookmarks_containers. The end result is a list of all folder and item children
// for a given folder id, sorted by position.
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT h.id, h.url, h.title, h.visit_count, MAX(fullv.visit_date), h.rev_host, a.position, a.item_child, a.folder_child, null FROM moz_bookmarks_assoc a JOIN moz_history h ON a.item_child = h.id LEFT JOIN moz_historyvisit v ON h.id = v.page_id LEFT JOIN moz_historyvisit fullv ON h.id = fullv.page_id WHERE a.parent = ?1 GROUP BY h.id UNION ALL SELECT null, null, null, null, null, null, a.position, a.item_child, a.folder_child, c.name FROM moz_bookmarks_assoc a JOIN moz_bookmarks_containers c ON c.id = a.folder_child WHERE a.parent = ?1 ORDER BY a.position"),
getter_AddRefs(mDBGetChildren));
NS_ENSURE_SUCCESS(rv, rv);
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT COUNT(*) FROM moz_bookmarks_assoc WHERE parent = ?1"),
getter_AddRefs(mDBFolderCount));
NS_ENSURE_SUCCESS(rv, rv);
return transaction.Commit();
}
nsresult
nsNavBookmarks::AdjustIndices(PRInt64 aFolder,
PRInt32 aStartIndex, PRInt32 aEndIndex,
PRInt32 aDelta)
{
nsCAutoString buffer;
buffer.AssignLiteral("UPDATE moz_bookmarks_assoc SET position = position + ");
buffer.AppendInt(aDelta);
buffer.AppendLiteral(" WHERE parent = ");
buffer.AppendInt(aFolder);
if (aStartIndex != 0) {
buffer.AppendLiteral(" AND position >= ");
buffer.AppendInt(aStartIndex);
}
if (aEndIndex != -1) {
buffer.AppendLiteral(" AND position <= ");
buffer.AppendInt(aEndIndex);
}
// TODO notify observers about renumbering?
return DBConn()->ExecuteSimpleSQL(buffer);
}
NS_IMETHODIMP
nsNavBookmarks::GetBookmarksRoot(PRInt64 *aRoot)
{
*aRoot = mRoot;
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::InsertItem(PRInt64 aFolder, nsIURI *aItem, PRInt32 aIndex)
{
mozIStorageConnection *dbConn = DBConn();
mozStorageTransaction transaction(dbConn, PR_FALSE);
PRInt64 childID;
nsresult rv = History()->GetUrlIdFor(aItem, &childID, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 index = (aIndex == -1) ? FolderCount(aFolder) : aIndex;
rv = AdjustIndices(aFolder, index, -1, 1);
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString buffer;
buffer.AssignLiteral("INSERT INTO moz_bookmarks_assoc (item_child, parent, position) VALUES (");
buffer.AppendInt(childID);
buffer.AppendLiteral(", ");
buffer.AppendInt(aFolder);
buffer.AppendLiteral(", ");
buffer.AppendInt(index);
buffer.AppendLiteral(")");
rv = dbConn->ExecuteSimpleSQL(buffer);
NS_ENSURE_SUCCESS(rv, rv);
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
for (PRInt32 i = 0; i < mObservers.Count(); ++i) {
mObservers[i]->OnItemAdded(aItem, aFolder, index);
}
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::RemoveItem(PRInt64 aFolder, nsIURI *aItem)
{
mozIStorageConnection *dbConn = DBConn();
mozStorageTransaction transaction(dbConn, PR_FALSE);
PRInt64 childID;
nsresult rv = History()->GetUrlIdFor(aItem, &childID, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
if (childID == 0) {
return NS_OK; // the item isn't in history at all
}
PRInt32 childIndex;
nsCAutoString buffer;
{
buffer.AssignLiteral("SELECT position FROM moz_bookmarks_assoc WHERE parent = ");
buffer.AppendInt(aFolder);
buffer.AppendLiteral(" AND item_child = ");
buffer.AppendInt(childID);
nsCOMPtr<mozIStorageStatement> statement;
rv = dbConn->CreateStatement(buffer, getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
PRBool results;
rv = statement->ExecuteStep(&results);
NS_ENSURE_SUCCESS(rv, rv);
// We _should_ always have a result here, but maybe we don't if the table
// has become corrupted. Just silently skip adjusting indices.
childIndex = results ? statement->AsInt32(0) : -1;
}
buffer.AssignLiteral("DELETE FROM moz_bookmarks_assoc WHERE parent = ");
buffer.AppendInt(aFolder);
buffer.AppendLiteral(" AND item_child = ");
buffer.AppendInt(childID);
rv = dbConn->ExecuteSimpleSQL(buffer);
NS_ENSURE_SUCCESS(rv, rv);
if (childIndex != -1) {
rv = AdjustIndices(aFolder, childIndex + 1, -1, -1);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
for (PRInt32 i = 0; i < mObservers.Count(); ++i) {
mObservers[i]->OnItemRemoved(aItem, aFolder, childIndex);
}
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::CreateFolder(PRInt64 aParent, const nsAString &aName,
PRInt32 aIndex, PRInt64 *aNewFolder)
{
mozIStorageConnection *dbConn = DBConn();
mozStorageTransaction transaction(dbConn, PR_FALSE);
PRInt32 index = (aIndex == -1) ? FolderCount(aParent) : aIndex;
nsresult rv = AdjustIndices(aParent, index, -1, 1);
NS_ENSURE_SUCCESS(rv, rv);
rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks_containers (name) VALUES (") +
NS_ConvertUTF16toUTF8(aName) +
NS_LITERAL_CSTRING(")"));
NS_ENSURE_SUCCESS(rv, rv);
PRInt64 child;
rv = dbConn->GetLastInsertRowID(&child);
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString buffer;
buffer.AssignLiteral("INSERT INTO moz_bookmarks_assoc (folder_child, parent, position) VALUES (");
buffer.AppendInt(child);
buffer.AppendLiteral(", ");
buffer.AppendInt(aParent);
buffer.AppendLiteral(", ");
buffer.AppendInt(index);
buffer.AppendLiteral(")");
rv = dbConn->ExecuteSimpleSQL(buffer);
NS_ENSURE_SUCCESS(rv, rv);
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
for (PRInt32 i = 0; i < mObservers.Count(); ++i) {
mObservers[i]->OnFolderAdded(child, aParent, index);
}
*aNewFolder = child;
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::RemoveFolder(PRInt64 aFolder)
{
mozIStorageConnection *dbConn = DBConn();
mozStorageTransaction transaction(dbConn, PR_FALSE);
nsCAutoString buffer;
buffer.AssignLiteral("SELECT parent, position FROM moz_bookmarks_assoc WHERE folder_child = ");
buffer.AppendInt(aFolder);
nsresult rv;
PRInt64 parent;
PRInt32 index;
{
nsCOMPtr<mozIStorageStatement> statement;
rv = dbConn->CreateStatement(buffer, getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
PRBool results;
rv = statement->ExecuteStep(&results);
NS_ENSURE_SUCCESS(rv, rv);
if (!results) {
return NS_ERROR_INVALID_ARG; // folder is not in the hierarchy
}
parent = statement->AsInt64(0);
index = statement->AsInt32(1);
}
buffer.AssignLiteral("DELETE FROM moz_bookmarks_containers WHERE id = ");
buffer.AppendInt(aFolder);
rv = dbConn->ExecuteSimpleSQL(buffer);
NS_ENSURE_SUCCESS(rv, rv);
buffer.AssignLiteral("DELETE FROM moz_bookmarks_assoc WHERE folder_child = ");
buffer.AppendInt(aFolder);
rv = dbConn->ExecuteSimpleSQL(buffer);
NS_ENSURE_SUCCESS(rv, rv);
rv = AdjustIndices(parent, index + 1, -1, -1);
NS_ENSURE_SUCCESS(rv, rv);
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
for (PRInt32 i = 0; i < mObservers.Count(); ++i) {
mObservers[i]->OnFolderRemoved(aFolder, parent, index);
}
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::MoveFolder(PRInt64 aFolder, PRInt64 aNewParent, PRInt32 aIndex)
{
mozIStorageConnection *dbConn = DBConn();
mozStorageTransaction transaction(dbConn, PR_FALSE);
nsCAutoString buffer;
buffer.AssignLiteral("SELECT parent, position FROM moz_bookmarks_assoc WHERE folder_child = ");
buffer.AppendInt(aFolder);
nsresult rv;
PRInt64 parent;
PRInt32 index;
{
nsCOMPtr<mozIStorageStatement> statement;
rv = dbConn->CreateStatement(buffer, getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
PRBool results;
rv = statement->ExecuteStep(&results);
NS_ENSURE_SUCCESS(rv, rv);
if (!results) {
return NS_ERROR_INVALID_ARG; // folder is not in the hierarchy
}
parent = statement->AsInt64(0);
index = statement->AsInt32(1);
}
if (aNewParent == parent && aIndex == index) {
// Nothing to do!
return NS_OK;
}
if (parent == aNewParent) {
// We can optimize the updates if moving within the same container
if (index > aIndex) {
rv = AdjustIndices(parent, aIndex, index - 1, 1);
} else {
rv = AdjustIndices(parent, index + 1, aIndex, -1);
}
} else {
rv = AdjustIndices(parent, index + 1, -1, -1);
NS_ENSURE_SUCCESS(rv, rv);
rv = AdjustIndices(aNewParent, aIndex, -1, 1);
}
NS_ENSURE_SUCCESS(rv, rv);
buffer.AssignLiteral("UPDATE moz_bookmarks_assoc SET parent = ");
buffer.AppendInt(aNewParent);
buffer.AppendLiteral(", position = ");
buffer.AppendInt(aIndex);
buffer.AppendLiteral(" WHERE id = ");
buffer.AppendInt(aFolder);
rv = dbConn->ExecuteSimpleSQL(buffer);
NS_ENSURE_SUCCESS(rv, rv);
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
for (PRInt32 i = 0; i < mObservers.Count(); ++i) {
mObservers[i]->OnFolderMoved(aFolder, aNewParent, aIndex);
}
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::SetItemTitle(nsIURI *aURI, const nsAString &aTitle)
{
return History()->SetPageTitle(aURI, aTitle);
}
NS_IMETHODIMP
nsNavBookmarks::GetItemTitle(nsIURI *aURI, nsAString &aTitle)
{
mozIStorageStatement *statement = DBGetURLPageInfo();
nsresult rv = BindStatementURI(statement, 0, aURI);
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(statement);
PRBool results;
rv = statement->ExecuteStep(&results);
NS_ENSURE_SUCCESS(rv, rv);
if (!results) {
return NS_ERROR_INVALID_ARG;
}
return statement->GetString(nsNavHistory::kGetInfoIndex_Title, aTitle);
}
NS_IMETHODIMP
nsNavBookmarks::SetFolderTitle(PRInt64 aFolder, const nsAString &aTitle)
{
nsCAutoString buffer;
buffer.AssignLiteral("UPDATE moz_bookmarks_container SET title = ");
AppendUTF16toUTF8(aTitle, buffer);
buffer.AppendLiteral(" WHERE id = ");
buffer.AppendInt(aFolder);
nsresult rv = DBConn()->ExecuteSimpleSQL(buffer);
NS_ENSURE_SUCCESS(rv, rv);
for (PRInt32 i = 0; i < mObservers.Count(); ++i) {
mObservers[i]->OnFolderChanged(aFolder, NS_LITERAL_CSTRING("title"));
}
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::GetFolderTitle(PRInt64 aFolder, nsAString &aTitle)
{
mozStorageStatementScoper scope(mDBGetFolderInfo);
nsresult rv = mDBGetFolderInfo->BindInt64Parameter(0, aFolder);
NS_ENSURE_SUCCESS(rv, rv);
PRBool results;
rv = mDBGetFolderInfo->ExecuteStep(&results);
NS_ENSURE_SUCCESS(rv, rv);
if (!results) {
return NS_ERROR_INVALID_ARG;
}
return mDBGetFolderInfo->GetString(kGetFolderInfoIndex_Title, aTitle);
}
NS_IMETHODIMP
nsNavFolderResultNode::GetChildCount(PRInt32 *aChildCount)
{
if (!mQueriedChildren) {
nsresult rv = nsNavBookmarks::sInstance->FillFolderChildren(this);
NS_ENSURE_SUCCESS(rv, rv);
}
return nsNavHistoryResultNode::GetChildCount(aChildCount);
}
NS_IMETHODIMP
nsNavFolderResultNode::GetChild(PRInt32 aIndex,
nsINavHistoryResultNode **aChild)
{
if (!mQueriedChildren) {
nsresult rv = nsNavBookmarks::sInstance->FillFolderChildren(this);
NS_ENSURE_SUCCESS(rv, rv);
}
return nsNavHistoryResultNode::GetChild(aIndex, aChild);
}
nsresult
nsNavBookmarks::ResultNodeForFolder(PRInt64 aID,
const nsString &aTitle,
nsNavHistoryResultNode **aNode)
{
nsNavFolderResultNode *node = new nsNavFolderResultNode();
NS_ENSURE_TRUE(node, NS_ERROR_OUT_OF_MEMORY);
node->mID = aID;
node->mType = nsINavHistoryResultNode::RESULT_TYPE_FOLDER;
node->mTitle = aTitle;
node->mAccessCount = 0;
node->mTime = 0;
NS_ADDREF(*aNode = node);
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::GetBookmarks(nsINavHistoryResult **aResult)
{
*aResult = nsnull;
nsNavHistory *history = History();
nsRefPtr<nsNavHistoryResult> result(History()->NewHistoryResult());
NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = result->Init();
NS_ENSURE_SUCCESS(rv, rv);
// Fill the result with a single result node for the bookmarks root.
nsCOMPtr<nsNavHistoryResultNode> topNode;
rv = ResultNodeForFolder(mRoot, EmptyString(), getter_AddRefs(topNode));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(result->GetTopLevel()->AppendObject(topNode),
NS_ERROR_OUT_OF_MEMORY);
result->FilledAllResults();
NS_STATIC_CAST(nsRefPtr<nsINavHistoryResult>, result).swap(*aResult);
return NS_OK;
}
nsresult
nsNavBookmarks::FillFolderChildren(nsNavFolderResultNode *aNode)
{
NS_ASSERTION(!aNode->mQueriedChildren, "children already queried");
NS_ASSERTION(aNode->mChildren.Count() == 0, "already have child nodes");
mozStorageStatementScoper scope(mDBGetChildren);
mozStorageTransaction transaction(DBConn(), PR_FALSE);
nsresult rv = mDBGetChildren->BindInt64Parameter(0, aNode->mID);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMArray<nsNavHistoryResultNode>* children = &aNode->mChildren;
PRBool results;
while (NS_SUCCEEDED(mDBGetChildren->ExecuteStep(&results)) && results) {
nsCOMPtr<nsNavHistoryResultNode> node;
if (mDBGetChildren->IsNull(kGetChildrenIndex_FolderChild)) {
rv = History()->RowToResult(mDBGetChildren, PR_FALSE, getter_AddRefs(node));
} else {
PRInt64 folder = mDBGetChildren->AsInt64(kGetChildrenIndex_FolderChild);
nsAutoString title;
mDBGetChildren->GetString(kGetChildrenIndex_FolderTitle, title);
rv = ResultNodeForFolder(folder, title, getter_AddRefs(node));
}
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(children->AppendObject(node), NS_ERROR_OUT_OF_MEMORY);
}
aNode->mQueriedChildren = PR_TRUE;
return transaction.Commit();
}
PRInt32
nsNavBookmarks::FolderCount(PRInt64 aFolder)
{
mozStorageStatementScoper scope(mDBFolderCount);
nsresult rv = mDBFolderCount->BindInt64Parameter(0, aFolder);
NS_ENSURE_SUCCESS(rv, 0);
PRBool results;
rv = mDBFolderCount->ExecuteStep(&results);
NS_ENSURE_SUCCESS(rv, rv);
return mDBFolderCount->AsInt32(0);
}
NS_IMETHODIMP
nsNavBookmarks::IsBookmarked(nsIURI *aURI, PRBool *aBookmarked)
{
*aBookmarked = PR_FALSE;
mozStorageStatementScoper scope(mDBFindURIBookmarks);
nsresult rv = BindStatementURI(mDBFindURIBookmarks, 0, aURI);
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBFindURIBookmarks->ExecuteStep(aBookmarked);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::GetBookmarkCategories(nsIURI *aURI, PRUint32 *aCount,
PRInt64 **aCategories)
{
*aCount = 0;
*aCategories = nsnull;
mozStorageStatementScoper scope(mDBFindURIBookmarks);
mozStorageTransaction transaction(DBConn(), PR_FALSE);
nsresult rv = BindStatementURI(mDBFindURIBookmarks, 0, aURI);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 arraySize = 8;
PRInt64 *categories = NS_STATIC_CAST(PRInt64*,
nsMemory::Alloc(arraySize * sizeof(PRInt64)));
NS_ENSURE_TRUE(categories, NS_ERROR_OUT_OF_MEMORY);
PRUint32 count = 0;
PRBool more;
while (NS_SUCCEEDED((rv = mDBFindURIBookmarks->ExecuteStep(&more))) && more) {
if (count >= arraySize) {
arraySize <<= 1;
PRInt64 *res = NS_STATIC_CAST(PRInt64*, nsMemory::Realloc(categories,
arraySize * sizeof(PRInt64)));
if (!res) {
delete categories;
return NS_ERROR_OUT_OF_MEMORY;
}
categories = res;
}
categories[count++] =
mDBFindURIBookmarks->AsInt64(kFindBookmarksIndex_Parent);
}
NS_ENSURE_SUCCESS(rv, rv);
*aCount = count;
*aCategories = categories;
return transaction.Commit();
}
NS_IMETHODIMP
nsNavBookmarks::BeginUpdateBatch()
{
for (PRInt32 i = 0; i < mObservers.Count(); ++i) {
mObservers[i]->OnBeginUpdateBatch();
}
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::EndUpdateBatch()
{
for (PRInt32 i = 0; i < mObservers.Count(); ++i) {
mObservers[i]->OnEndUpdateBatch();
}
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::AddObserver(nsINavBookmarkObserver *aObserver)
{
NS_ENSURE_TRUE(mObservers.AppendObject(aObserver), NS_ERROR_OUT_OF_MEMORY);
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::RemoveObserver(nsINavBookmarkObserver *aObserver)
{
mObservers.RemoveObject(aObserver);
return NS_OK;
}
// nsNavBookmarks::nsINavHistoryObserver
NS_IMETHODIMP
nsNavBookmarks::OnBeginUpdateBatch()
{
// These aren't passed through to bookmark observers currently.
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::OnEndUpdateBatch()
{
// These aren't passed through to bookmark observers currently.
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::GetWantAllDetails(PRBool *aWant)
{
*aWant = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::OnAddURI(nsIURI *aURI, PRTime aTime)
{
// A new URI won't yet be bookmarked, so don't notify.
#ifdef DEBUG
PRBool bookmarked;
IsBookmarked(aURI, &bookmarked);
NS_ASSERTION(!bookmarked, "New URI shouldn't be bookmarked!");
#endif
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::OnDeleteURI(nsIURI *aURI)
{
// A deleted URI shouldn't be bookmarked.
#ifdef DEBUG
PRBool bookmarked;
IsBookmarked(aURI, &bookmarked);
NS_ASSERTION(!bookmarked, "Deleted URI shouldn't be bookmarked!");
#endif
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::OnClearHistory()
{
// Nothing being cleared should be bookmarked.
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::OnPageChanged(nsIURI *aURI, PRUint32 aWhat,
const nsAString &aValue)
{
if (aWhat == ATTRIBUTE_TITLE) {
for (PRInt32 i = 0; i < mObservers.Count(); ++i) {
mObservers[i]->OnItemChanged(aURI, NS_LITERAL_CSTRING("title"));
}
}
return NS_OK;
}

View File

@ -0,0 +1,123 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Places.
*
* The Initial Developer of the Original Code is
* Google Inc.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brian Ryner <bryner@brianryner.com> (original author)
*
* 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
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsINavBookmarksService.h"
#include "nsNavHistory.h"
#include "nsBrowserCompsCID.h"
class nsNavBookmarks;
class nsNavFolderResultNode : public nsNavHistoryResultNode
{
public:
nsNavFolderResultNode() :
nsNavHistoryResultNode(), mQueriedChildren(PR_FALSE) { }
NS_IMETHOD GetChildCount(PRInt32 *aChildCount);
NS_IMETHOD GetChild(PRInt32 aIndex, nsINavHistoryResultNode **aChild);
private:
friend class nsNavBookmarks;
PRBool mQueriedChildren;
};
class nsNavBookmarks : public nsINavBookmarksService,
public nsINavHistoryObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSINAVBOOKMARKSSERVICE
NS_DECL_NSINAVHISTORYOBSERVER
nsNavBookmarks();
nsresult Init();
static nsNavBookmarks* GetBookmarksService() {
if (!sInstance) {
nsresult rv;
nsCOMPtr<nsINavBookmarksService> serv(do_GetService(NS_NAVBOOKMARKSSERVICE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, nsnull);
NS_ASSERTION(sInstance, "Should have static instance pointer now");
}
return sInstance;
}
private:
static nsNavBookmarks *sInstance;
~nsNavBookmarks();
nsresult AdjustIndices(PRInt64 aFolder,
PRInt32 aStartIndex, PRInt32 aEndIndex,
PRInt32 aDelta);
nsresult ResultNodeForFolder(PRInt64 aFolder, const nsString &aTitle,
nsNavHistoryResultNode **aNode);
nsresult FillFolderChildren(nsNavFolderResultNode *aNode);
PRInt32 FolderCount(PRInt64 aFolder);
nsNavHistory* History() { return nsNavHistory::GetHistoryService(); }
mozIStorageStatement* DBGetURLPageInfo()
{ return History()->DBGetURLPageInfo(); }
mozIStorageConnection* DBConn() { return History()->GetStorageConnection(); }
nsCOMArray<nsINavBookmarkObserver> mObservers;
PRInt64 mRoot;
nsCOMPtr<mozIStorageStatement> mDBGetFolderInfo; // kGetFolderInfoIndex_* results
static const PRInt32 kGetFolderInfoIndex_FolderID;
static const PRInt32 kGetFolderInfoIndex_Title;
nsCOMPtr<mozIStorageStatement> mDBGetChildren; // kGetInfoIndex_* results + kGetChildrenIndex_* results
static const PRInt32 kGetChildrenIndex_Position;
static const PRInt32 kGetChildrenIndex_ItemChild;
static const PRInt32 kGetChildrenIndex_FolderChild;
static const PRInt32 kGetChildrenIndex_FolderTitle;
nsCOMPtr<mozIStorageStatement> mDBFindURIBookmarks; // kFindBookmarksIndex_* results
static const PRInt32 kFindBookmarksIndex_ItemChild;
static const PRInt32 kFindBookmarksIndex_FolderChild;
static const PRInt32 kFindBookmarksIndex_Parent;
static const PRInt32 kFindBookmarksIndex_Position;
nsCOMPtr<mozIStorageStatement> mDBFolderCount;
friend class nsNavFolderResultNode;
};

View File

@ -72,6 +72,7 @@ Extra:
#include <stdio.h>
#include "nsNavHistory.h"
#include "nsNavBookmarks.h"
#include "nsArray.h"
#include "nsDebug.h"
@ -235,7 +236,9 @@ nsNavHistory::nsNavHistory() : mNowValid(PR_FALSE),
nsNavHistory::~nsNavHistory()
{
gObserverService->RemoveObserver(this, gQuitApplicationMessage);
if (gObserverService) {
gObserverService->RemoveObserver(this, gQuitApplicationMessage);
}
// remove the static reference to the service. Check to make sure its us
// in case somebody creates an extra instance of the service.
@ -514,7 +517,7 @@ nsNavHistory::SaveCollapseItem(const nsAString& aTitle)
// added to the history.
nsresult
nsNavHistory::InternalAdd(nsIURI* aURI, PRUint32 aSessionID,
nsNavHistory::InternalAdd(nsIURI* aURI, PRInt64 aSessionID,
PRUint32 aTransitionType, const PRUnichar* aTitle,
PRTime aVisitDate, PRBool aRedirect,
PRBool aToplevel, PRInt64* aPageID)
@ -749,7 +752,7 @@ nsresult nsNavHistory::AddVisit(PRInt64 aFromStep, PRInt64 aPageID,
NS_ENSURE_SUCCESS(rv, rv);
rv = dbInsertStatement->BindInt32Parameter(3, aTransitionType);
NS_ENSURE_SUCCESS(rv, rv);
rv = dbInsertStatement->BindInt32Parameter(4, aSessionID);
rv = dbInsertStatement->BindInt64Parameter(4, aSessionID);
NS_ENSURE_SUCCESS(rv, rv);
rv = dbInsertStatement->Execute(); // should reset the statement
@ -1007,23 +1010,27 @@ NS_IMETHODIMP nsNavHistory::GetNewQuery(nsINavHistoryQuery **_retval)
*_retval = new nsNavHistoryQuery();
if (! *_retval)
return NS_ERROR_OUT_OF_MEMORY;
(*_retval)->AddRef();
NS_ADDREF(*_retval);
return NS_OK;
}
NS_IMETHODIMP nsNavHistory::GetNewQueryOptions(nsINavHistoryQueryOptions **_retval)
{
*_retval = new nsNavHistoryQueryOptions();
NS_ENSURE_TRUE(*_retval, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(*_retval);
return NS_OK;
}
// nsNavHistory::ExecuteQuery
//
NS_IMETHODIMP
nsNavHistory::ExecuteQuery(nsINavHistoryQuery *aQuery,
const PRInt32 *aGroupingMode, PRUint32 aGroupCount,
PRInt32 aSortingMode, PRBool aAsVisits,
nsNavHistory::ExecuteQuery(nsINavHistoryQuery *aQuery, nsINavHistoryQueryOptions *aOptions,
nsINavHistoryResult** _retval)
{
return ExecuteQueries(NS_CONST_CAST(const nsINavHistoryQuery**, &aQuery), 1,
aGroupingMode, aGroupCount,
aSortingMode, aAsVisits, _retval);
aOptions, _retval);
}
@ -1033,15 +1040,22 @@ nsNavHistory::ExecuteQuery(nsINavHistoryQuery *aQuery,
// it ANDed with the all the rest of the queries.
NS_IMETHODIMP
nsNavHistory::ExecuteQueries(const nsINavHistoryQuery** aQueries,
PRUint32 aQueryCount,
const PRInt32 *aGroupingMode, PRUint32 aGroupCount,
PRInt32 aSortingMode, PRBool aAsVisits,
nsNavHistory::ExecuteQueries(const nsINavHistoryQuery** aQueries, PRUint32 aQueryCount,
nsINavHistoryQueryOptions *aOptions,
nsINavHistoryResult** _retval)
{
nsresult rv;
if (aSortingMode < 0 || aSortingMode > SORT_BY_VISITCOUNT_DESCENDING)
nsCOMPtr<nsNavHistoryQueryOptions> options = do_QueryInterface(aOptions);
NS_ENSURE_TRUE(options, NS_ERROR_INVALID_ARG);
PRInt32 sortingMode = options->SortingMode();
if (sortingMode < 0 ||
sortingMode > nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING) {
return NS_ERROR_INVALID_ARG;
}
PRBool asVisits = options->ResultType() == nsINavHistoryQueryOptions::RESULT_TYPE_VISIT;
// conditions we want on all history queries, this just selects history
// entries that are "active"
@ -1052,7 +1066,7 @@ nsNavHistory::ExecuteQueries(const nsINavHistoryQuery** aQueries,
// Query string: Output parameters should be in order of kGetInfoIndex_*
nsCAutoString queryString;
nsCAutoString groupBy;
if (aAsVisits) {
if (asVisits) {
// if we want visits, this is easy, just combine all possible matches
// between the history and visits table and do our query.
queryString = NS_LITERAL_CSTRING("SELECT h.id, h.url, h.title, h.visit_count, v.visit_date, h.rev_host"
@ -1094,30 +1108,30 @@ nsNavHistory::ExecuteQueries(const nsINavHistoryQuery** aQueries,
// Sort clause: we will sort later, but if it comes out of the DB sorted,
// our later sort will be basically free. The DB can sort these for free
// most of the time anyway, because it has indices over these items.
switch(aSortingMode) {
case SORT_BY_NONE:
switch(sortingMode) {
case nsINavHistoryQueryOptions::SORT_BY_NONE:
break;
case SORT_BY_TITLE_ASCENDING:
case SORT_BY_TITLE_DESCENDING:
case nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING:
case nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING:
// the DB doesn't have indices on titles, and we need to do special
// sorting for locales. This type of sorting is done only at the end.
break;
case SORT_BY_DATE_ASCENDING:
case nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING:
queryString += NS_LITERAL_CSTRING(" ORDER BY v.visit_date ASC");
break;
case SORT_BY_DATE_DESCENDING:
case nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING:
queryString += NS_LITERAL_CSTRING(" ORDER BY v.visit_date DESC");
break;
case SORT_BY_URL_ASCENDING:
case nsINavHistoryQueryOptions::SORT_BY_URL_ASCENDING:
queryString += NS_LITERAL_CSTRING(" ORDER BY h.url ASC");
break;
case SORT_BY_URL_DESCENDING:
case nsINavHistoryQueryOptions::SORT_BY_URL_DESCENDING:
queryString += NS_LITERAL_CSTRING(" ORDER BY h.url DESC");
break;
case SORT_BY_VISITCOUNT_ASCENDING:
case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING:
queryString += NS_LITERAL_CSTRING(" ORDER BY h.visit_count ASC");
break;
case SORT_BY_VISITCOUNT_DESCENDING:
case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING:
queryString += NS_LITERAL_CSTRING(" ORDER BY h.visit_count DESC");
break;
default:
@ -1158,15 +1172,18 @@ nsNavHistory::ExecuteQueries(const nsINavHistoryQuery** aQueries,
aQueries[0])->GetHasSearchTerms(&hasSearchTerms);
NS_ENSURE_SUCCESS(rv, rv);
if (aGroupCount == 0 && ! hasSearchTerms) {
PRUint32 groupCount;
const PRInt32 *groupings = options->GroupingMode(&groupCount);
if (groupCount == 0 && ! hasSearchTerms) {
// optimize the case where we just want a list with no grouping: this
// directly fills in the results and we avoid a copy of the whole list
rv = ResultsAsList(statement, aAsVisits, result->GetTopLevel());
rv = ResultsAsList(statement, asVisits, result->GetTopLevel());
NS_ENSURE_SUCCESS(rv, rv);
} else {
// generate the toplevel results
nsCOMArray<nsNavHistoryResultNode> toplevel;
rv = ResultsAsList(statement, aAsVisits, &toplevel);
rv = ResultsAsList(statement, asVisits, &toplevel);
NS_ENSURE_SUCCESS(rv, rv);
if (hasSearchTerms) {
@ -1174,25 +1191,25 @@ nsNavHistory::ExecuteQueries(const nsINavHistoryQuery** aQueries,
nsAutoString searchTerms;
NS_CONST_CAST(nsINavHistoryQuery*, aQueries[0])
->GetSearchTerms(searchTerms);
if (aGroupCount == 0) {
if (groupCount == 0) {
FilterResultSet(toplevel, result->GetTopLevel(), searchTerms);
} else {
nsCOMArray<nsNavHistoryResultNode> filteredResults;
FilterResultSet(toplevel, &filteredResults, searchTerms);
rv = RecursiveGroup(filteredResults, aGroupingMode, aGroupCount,
rv = RecursiveGroup(filteredResults, groupings, groupCount,
result->GetTopLevel());
NS_ENSURE_SUCCESS(rv, rv);
}
} else {
// group unfiltered results
rv = RecursiveGroup(toplevel, aGroupingMode, aGroupCount,
rv = RecursiveGroup(toplevel, groupings, groupCount,
result->GetTopLevel());
NS_ENSURE_SUCCESS(rv, rv);
}
}
if (aSortingMode != SORT_BY_NONE)
result->RecursiveSort(aSortingMode);
if (sortingMode != nsINavHistoryQueryOptions::SORT_BY_NONE)
result->RecursiveSort(sortingMode);
// automatically expand things that were expanded before
if (gExpandedItems.Count() > 0)
@ -1201,7 +1218,7 @@ nsNavHistory::ExecuteQueries(const nsINavHistoryQuery** aQueries,
result->FilledAllResults();
*_retval = result;
(*_retval)->AddRef();
NS_ADDREF(*_retval);
return NS_OK;
}
@ -1363,14 +1380,20 @@ nsNavHistory::RemovePage(nsIURI *aURI)
NS_ENSURE_SUCCESS(rv, rv);
// history entries
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM moz_history WHERE url = ?1"),
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
rv = BindStatementURI(statement, 0, aURI);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService();
NS_ENSURE_TRUE(bookmarks, NS_ERROR_UNEXPECTED);
PRBool bookmarked;
bookmarks->IsBookmarked(aURI, &bookmarked);
if (!bookmarked) {
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM moz_history WHERE url = ?1"),
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
rv = BindStatementURI(statement, 0, aURI);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
}
// Observers: Be sure to finish transaction before calling observers. Note also
// that we always call the observers even though we aren't sure something
@ -2302,7 +2325,12 @@ nsNavHistory::QueryToSelectClause(nsINavHistoryQuery* aQuery, // const
// search terms FIXME
// onlyBookmarked FIXME
if (NS_SUCCEEDED(aQuery->GetOnlyBookmarked(&hasIt)) && hasIt) {
if (! aClause->IsEmpty())
*aClause += NS_LITERAL_CSTRING(" AND ");
*aClause += NS_LITERAL_CSTRING("EXISTS (SELECT b.item_child FROM moz_bookmarks_assoc b WHERE b.item_child = id)");
}
// domain
if (NS_SUCCEEDED(aQuery->GetHasDomain(&hasIt)) && hasIt) {
@ -2365,7 +2393,7 @@ nsNavHistory::BindQueryClauseParameters(mozIStorageStatement* statement,
// search terms FIXME
// onlyBookmarked FIXME
// onlyBookmarked: nothing to bind
// domain (see GetReversedHostname for more info on reversed host names)
if (NS_SUCCEEDED(aQuery->GetHasDomain(&hasIt)) && hasIt) {
@ -2436,13 +2464,13 @@ nsNavHistory::RecursiveGroup(const nsCOMArray<nsNavHistoryResultNode>& aSource,
nsresult rv;
switch (aGroupingMode[0]) {
case GROUP_BY_DAY:
case nsINavHistoryQueryOptions::GROUP_BY_DAY:
rv = GroupByDay(aSource, aDest);
break;
case GROUP_BY_HOST:
case nsINavHistoryQueryOptions::GROUP_BY_HOST:
rv = GroupByHost(aSource, aDest);
break;
case GROUP_BY_DOMAIN:
case nsINavHistoryQueryOptions::GROUP_BY_DOMAIN:
rv = GroupByDomain(aSource, aDest);
break;
default:
@ -3042,7 +3070,7 @@ GetReversedHostname(const nsString& aForward, nsAString& aRevHost)
void
GetUnreversedHostname(const nsString& aBackward, nsAString& aForward)
{
NS_ASSERTION(! aBackward.IsEmpty() > 0 && aBackward[aBackward.Length()-1] == '.',
NS_ASSERTION(! aBackward.IsEmpty() && aBackward[aBackward.Length()-1] == '.',
"Malformed reversed hostname with no trailing dot");
aForward.Truncate(0);
@ -3076,7 +3104,7 @@ PRBool IsNumericHostName(const nsString& aHost)
char cur = aHost[i];
if (cur == '.')
periodCount ++;
else if (cur < '0' || cur > '9')
else if (cur < PRUnichar('0') || cur > PRUnichar('9'))
return PR_FALSE;
}
return (periodCount == 3);
@ -3259,15 +3287,15 @@ nsresult BindStatementURI(mozIStorageStatement* statement, PRInt32 index,
NS_IMPL_ISUPPORTS1(nsNavHistoryResultNode, nsINavHistoryResultNode)
nsNavHistoryResultNode::nsNavHistoryResultNode() : mID(0), mExpanded(PR_FALSE)
nsNavHistoryResultNode::nsNavHistoryResultNode() : mID(0), mExpanded(PR_FALSE),
mQueriedChildren(PR_FALSE)
{
}
/* attribute nsINavHistoryResultNode parent */
NS_IMETHODIMP nsNavHistoryResultNode::GetParent(nsINavHistoryResultNode** parent)
{
*parent = mParent;
(*parent)->AddRef();
NS_IF_ADDREF(*parent = mParent);
return NS_OK;
}
@ -3326,8 +3354,7 @@ NS_IMETHODIMP nsNavHistoryResultNode::GetChild(PRInt32 aIndex,
{
if (aIndex < 0 || aIndex >= mChildren.Count())
return NS_ERROR_INVALID_ARG;
*_retval = mChildren[aIndex];
(*_retval)->AddRef();
NS_ADDREF(*_retval = mChildren[aIndex]);
return NS_OK;
}
@ -3465,7 +3492,7 @@ nsNavHistoryResult::nsNavHistoryResult(nsNavHistory* aHistoryService,
nsIStringBundle* aHistoryBundle)
: mBundle(aHistoryBundle), mHistoryService(aHistoryService),
mCollapseDuplicates(PR_TRUE),
mTimesIncludeDates(PR_TRUE), mCurrentSort(nsNavHistory::SORT_BY_NONE)
mTimesIncludeDates(PR_TRUE), mCurrentSort(nsINavHistoryQueryOptions::SORT_BY_NONE)
{
}
@ -3560,7 +3587,7 @@ nsNavHistoryResult::GetTopLevel(nsIArray** aTopLevel)
NS_IMETHODIMP
nsNavHistoryResult::RecursiveSort(PRUint32 aSortingMode)
{
if (aSortingMode > nsNavHistory::SORT_BY_VISITCOUNT_DESCENDING)
if (aSortingMode > nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING)
return NS_ERROR_INVALID_ARG;
mCurrentSort = aSortingMode;
@ -3624,30 +3651,30 @@ nsNavHistoryResult::RecursiveSortArray(
{
switch (aSortingMode)
{
case nsNavHistory::SORT_BY_NONE:
case nsINavHistoryQueryOptions::SORT_BY_NONE:
break;
case nsNavHistory::SORT_BY_TITLE_ASCENDING:
case nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING:
aSources.Sort(SortComparison_TitleLess, NS_STATIC_CAST(void*, this));
break;
case nsNavHistory::SORT_BY_TITLE_DESCENDING:
case nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING:
aSources.Sort(SortComparison_TitleGreater, NS_STATIC_CAST(void*, this));
break;
case nsNavHistory::SORT_BY_DATE_ASCENDING:
case nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING:
aSources.Sort(SortComparison_DateLess, NS_STATIC_CAST(void*, this));
break;
case nsNavHistory::SORT_BY_DATE_DESCENDING:
case nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING:
aSources.Sort(SortComparison_DateGreater, NS_STATIC_CAST(void*, this));
break;
case nsNavHistory::SORT_BY_URL_ASCENDING:
case nsINavHistoryQueryOptions::SORT_BY_URL_ASCENDING:
aSources.Sort(SortComparison_URLLess, NS_STATIC_CAST(void*, this));
break;
case nsNavHistory::SORT_BY_URL_DESCENDING:
case nsINavHistoryQueryOptions::SORT_BY_URL_DESCENDING:
aSources.Sort(SortComparison_URLGreater, NS_STATIC_CAST(void*, this));
break;
case nsNavHistory::SORT_BY_VISITCOUNT_ASCENDING:
case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING:
aSources.Sort(SortComparison_VisitCountLess, NS_STATIC_CAST(void*, this));
break;
case nsNavHistory::SORT_BY_VISITCOUNT_DESCENDING:
case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING:
aSources.Sort(SortComparison_VisitCountGreater, NS_STATIC_CAST(void*, this));
break;
default:
@ -3749,8 +3776,7 @@ nsNavHistoryResult::NodeForTreeIndex(PRUint32 index,
{
if (index >= (PRUint32)mVisibleElements.Count())
return NS_ERROR_INVALID_ARG;
*aResult = VisibleElementAt(index);
(*aResult)->AddRef();
NS_ADDREF(*aResult = VisibleElementAt(index));
return NS_OK;
}
@ -3783,7 +3809,7 @@ nsNavHistoryResult::SetTreeSortingIndicator()
}
// set new sorting indicator by looking through all columns for ours
if (mCurrentSort == nsNavHistory::SORT_BY_NONE)
if (mCurrentSort == nsINavHistoryQueryOptions::SORT_BY_NONE)
return;
PRBool desiredIsDescending;
ColumnType desiredColumn = SortTypeToColumnType(mCurrentSort,
@ -4129,8 +4155,7 @@ NS_IMETHODIMP nsNavHistoryResult::GetSelection(nsITreeSelection** aSelection)
*aSelection = nsnull;
return NS_ERROR_FAILURE;
}
*aSelection = mSelection;
(*aSelection)->AddRef();
NS_ADDREF(*aSelection = mSelection);
return NS_OK;
}
NS_IMETHODIMP nsNavHistoryResult::SetSelection(nsITreeSelection* aSelection)
@ -4164,7 +4189,10 @@ NS_IMETHODIMP nsNavHistoryResult::IsContainer(PRInt32 index, PRBool *_retval)
{
if (index < 0 || index >= mVisibleElements.Count())
return NS_ERROR_INVALID_ARG;
*_retval = (VisibleElementAt(index)->mChildren.Count() > 0);
nsNavHistoryResultNode *node = VisibleElementAt(index);
*_retval = (node->mChildren.Count() > 0 ||
node->mType == nsINavHistoryResultNode::RESULT_TYPE_FOLDER);
return NS_OK;
}
@ -4430,29 +4458,29 @@ NS_IMETHODIMP nsNavHistoryResult::CycleHeader(nsITreeColumn *col)
PRInt32 newSort;
switch (GetColumnType(col)) {
case Column_Title:
if (mCurrentSort == nsNavHistory::SORT_BY_TITLE_ASCENDING)
newSort = nsNavHistory::SORT_BY_TITLE_DESCENDING;
if (mCurrentSort == nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING)
newSort = nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING;
else
newSort = nsNavHistory::SORT_BY_TITLE_ASCENDING;
newSort = nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING;
break;
case Column_URL:
if (mCurrentSort == nsNavHistory::SORT_BY_URL_ASCENDING)
newSort = nsNavHistory::SORT_BY_URL_DESCENDING;
if (mCurrentSort == nsINavHistoryQueryOptions::SORT_BY_URL_ASCENDING)
newSort = nsINavHistoryQueryOptions::SORT_BY_URL_DESCENDING;
else
newSort = nsNavHistory::SORT_BY_URL_ASCENDING;
newSort = nsINavHistoryQueryOptions::SORT_BY_URL_ASCENDING;
break;
case Column_Date:
if (mCurrentSort == nsNavHistory::SORT_BY_DATE_ASCENDING)
newSort = nsNavHistory::SORT_BY_DATE_DESCENDING;
if (mCurrentSort == nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING)
newSort = nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING;
else
newSort = nsNavHistory::SORT_BY_DATE_ASCENDING;
newSort = nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING;
break;
case Column_VisitCount:
// visit count default is unusual because it is descending
if (mCurrentSort == nsNavHistory::SORT_BY_VISITCOUNT_DESCENDING)
newSort = nsNavHistory::SORT_BY_VISITCOUNT_ASCENDING;
if (mCurrentSort == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING)
newSort = nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING;
else
newSort = nsNavHistory::SORT_BY_VISITCOUNT_DESCENDING;
newSort = nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING;
break;
default:
return NS_ERROR_INVALID_ARG;
@ -4542,31 +4570,73 @@ nsNavHistoryResult::SortTypeToColumnType(PRUint32 aSortType,
PRBool* aDescending)
{
switch(aSortType) {
case nsINavHistory::SORT_BY_TITLE_ASCENDING:
case nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING:
*aDescending = PR_FALSE;
return Column_Title;
case nsINavHistory::SORT_BY_TITLE_DESCENDING:
case nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING:
*aDescending = PR_TRUE;
return Column_Title;
case nsINavHistory::SORT_BY_DATE_ASCENDING:
case nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING:
*aDescending = PR_FALSE;
return Column_Date;
case nsINavHistory::SORT_BY_DATE_DESCENDING:
case nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING:
*aDescending = PR_TRUE;
return Column_Date;
case nsINavHistory::SORT_BY_URL_ASCENDING:
case nsINavHistoryQueryOptions::SORT_BY_URL_ASCENDING:
*aDescending = PR_FALSE;
return Column_URL;
case nsINavHistory::SORT_BY_URL_DESCENDING:
case nsINavHistoryQueryOptions::SORT_BY_URL_DESCENDING:
*aDescending = PR_TRUE;
return Column_URL;
case nsINavHistory::SORT_BY_VISITCOUNT_ASCENDING:
case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING:
*aDescending = PR_FALSE;
return Column_VisitCount;
case nsINavHistory::SORT_BY_VISITCOUNT_DESCENDING:
case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING:
*aDescending = PR_TRUE;
return Column_VisitCount;
default:
return Column_Unknown;
}
}
// nsNavHistoryQueryOptions
NS_IMPL_ISUPPORTS2(nsNavHistoryQueryOptions, nsNavHistoryQueryOptions, nsINavHistoryQueryOptions)
NS_IMETHODIMP
nsNavHistoryQueryOptions::SetGroupingMode(const PRInt32 *aGroupingMode,
PRUint32 aGroupCount)
{
delete[] mGroupings;
mGroupCount = 0;
mGroupings = new PRInt32[aGroupCount];
NS_ENSURE_TRUE(mGroupings, NS_ERROR_OUT_OF_MEMORY);
for (PRUint32 i = 0; i < aGroupCount; ++i) {
mGroupings[i] = aGroupingMode[i];
}
mGroupCount = aGroupCount;
return NS_OK;
}
NS_IMETHODIMP
nsNavHistoryQueryOptions::SetSortingMode(PRInt32 aMode)
{
mSort = aMode;
return NS_OK;
}
NS_IMETHODIMP
nsNavHistoryQueryOptions::SetResultType(PRInt32 aType)
{
mResultType = aType;
return NS_OK;
}
NS_IMETHODIMP
nsNavHistoryQueryOptions::SetExpandPlaces(PRBool aExpand)
{
mExpandPlaces = aExpand;
return NS_OK;
}

View File

@ -36,10 +36,13 @@
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsNavHistory_h_
#define nsNavHistory_h_
#include "mozIStorageService.h"
#include "mozIStorageConnection.h"
#include "mozIStorageValueArray.h"
#include "mozIStorageStatement.h"
#include "nsAutoPtr.h"
#include "nsCOMArray.h"
#include "nsCOMPtr.h"
@ -71,7 +74,7 @@
#define AUTOCOMPLETE_NONPAGE_VISIT_COUNT_BOOST 5
class mozIAnnotationService;
class nsNavHistory;
// nsNavHistoryQuery
//
@ -100,6 +103,7 @@ protected:
PRInt32 mGroupingMode;
PRInt32 mSortingMode;
PRBool mAsVisits;
PRBool mExpandPlaces;
};
@ -114,7 +118,7 @@ public:
NS_DECL_NSINAVHISTORYRESULTNODE
private:
~nsNavHistoryResultNode() {}
virtual ~nsNavHistoryResultNode() {}
protected:
// parent of this element, NULL if no parent. Filled in by FillAllElements
@ -150,12 +154,14 @@ protected:
// this is set to the default in the constructor
PRBool mExpanded;
// for bookmark folders, stores whether we've queried for the child list yet
PRBool mQueriedChildren;
friend class nsNavHistory;
friend class nsNavHistoryResult;
};
class nsIDateTimeFormat;
class nsNavHistory;
// nsNavHistoryResult
//
@ -271,9 +277,41 @@ protected:
class AutoCompleteIntermediateResultSet;
#define NS_NAVHISTORYQUERYOPTIONS_IID \
{0x95f8ba3b, 0xd681, 0x4d89, {0xab, 0xd1, 0xfd, 0xae, 0xf2, 0xa3, 0xde, 0x18}}
class nsNavHistoryQueryOptions : public nsINavHistoryQueryOptions
{
public:
nsNavHistoryQueryOptions() : mSort(0), mResultType(0),
mGroupCount(0), mGroupings(nsnull), mExpandPlaces(PR_FALSE)
{ }
NS_DECLARE_STATIC_IID_ACCESSOR(NS_INAVHISTORYQUERYOPTIONS_IID)
NS_DECL_ISUPPORTS
NS_DECL_NSINAVHISTORYQUERYOPTIONS
PRInt32 SortingMode() const { return mSort; }
PRInt32 ResultType() const { return mResultType; }
const PRInt32* GroupingMode(PRUint32 *count) const {
*count = mGroupCount; return mGroupings;
}
PRBool ExpandPlaces() const { return mExpandPlaces; }
private:
~nsNavHistoryQueryOptions() { delete[] mGroupings; }
PRInt32 mSort;
PRInt32 mResultType;
PRUint32 mGroupCount;
PRInt32 *mGroupings;
PRBool mExpandPlaces;
};
// nsNavHistory
class nsNavHistory : nsSupportsWeakReference,
class nsNavHistory : public nsSupportsWeakReference,
public nsINavHistory,
public nsIObserver,
public nsIBrowserHistory,
@ -301,9 +339,9 @@ public:
static nsNavHistory* GetHistoryService()
{
if (! gHistoryService) {
// don't want the return value, since that's the interface. We want the
// pointer to the implementation.
do_GetService("@mozilla.org/browser/nav-history;1");
nsresult rv;
nsCOMPtr<nsINavHistory> serv(do_GetService("@mozilla.org/browser/nav-history;1", &rv));
NS_ENSURE_SUCCESS(rv, nsnull);
// our constructor should have set the static variable. If it didn't,
// something is wrong.
@ -333,6 +371,26 @@ public:
void SaveExpandItem(const nsAString& aTitle);
void SaveCollapseItem(const nsAString& aTitle);
// get the statement for selecting a history row by id
mozIStorageStatement* DBGetURLPageInfo() { return mDBGetURLPageInfo; }
// Constants for the columns returned by the above statement.
static const PRInt32 kGetInfoIndex_PageID;
static const PRInt32 kGetInfoIndex_URL;
static const PRInt32 kGetInfoIndex_Title;
static const PRInt32 kGetInfoIndex_VisitCount;
static const PRInt32 kGetInfoIndex_VisitDate;
static const PRInt32 kGetInfoIndex_RevHost;
// Take a result returned from DBGetURLPageInfo and construct a
// ResultNode.
nsresult RowToResult(mozIStorageValueArray* aRow, PRBool aAsVisits,
nsNavHistoryResultNode** aResult);
// Construct a new HistoryResult object.
nsNavHistoryResult* NewHistoryResult()
{ return new nsNavHistoryResult(this, mBundle); }
private:
~nsNavHistory();
@ -358,12 +416,6 @@ protected:
nsCOMPtr<mozIStorageStatement> mDBGetVisitPageInfo; // kGetInfoIndex_* results
nsCOMPtr<mozIStorageStatement> mDBGetURLPageInfo; // kGetInfoIndex_* results
nsCOMPtr<mozIStorageStatement> mDBFullAutoComplete; // kAutoCompleteIndex_* results, 1 arg (max # results)
static const PRInt32 kGetInfoIndex_PageID;
static const PRInt32 kGetInfoIndex_URL;
static const PRInt32 kGetInfoIndex_Title;
static const PRInt32 kGetInfoIndex_VisitCount;
static const PRInt32 kGetInfoIndex_VisitDate;
static const PRInt32 kGetInfoIndex_RevHost;
static const PRInt32 kAutoCompleteIndex_URL;
static const PRInt32 kAutoCompleteIndex_Title;
static const PRInt32 kAutoCompleteIndex_VisitCount;
@ -378,7 +430,7 @@ protected:
nsresult InitMemDB();
nsresult InternalAdd(nsIURI* aURI, PRUint32 aSessionID,
nsresult InternalAdd(nsIURI* aURI, PRInt64 aSessionID,
PRUint32 aTransitionType, const PRUnichar* aTitle,
PRTime aVisitDate, PRBool aRedirect,
PRBool aToplevel, PRInt64* aPageID);
@ -410,8 +462,6 @@ protected:
nsresult ResultsAsList(mozIStorageStatement* statement, PRBool aAsVisits,
nsCOMArray<nsNavHistoryResultNode>* aResults);
nsresult RowToResult(mozIStorageValueArray* aRow, PRBool aAsVisits,
nsNavHistoryResultNode** aResult);
void TitleForDomain(const nsString& domain, nsAString& aTitle);
nsresult RecursiveGroup(const nsCOMArray<nsNavHistoryResultNode>& aSource,
@ -486,3 +536,5 @@ protected:
*/
nsresult BindStatementURI(mozIStorageStatement* statement, PRInt32 index,
nsIURI* aURI);
#endif // nsNavHistory_h_

View File

@ -0,0 +1,188 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Places.
*
* The Initial Developer of the Original Code is
* Google Inc.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Brian Ryner <bryner@brianryner.com>
*
* 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
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/**
* Unit test for the bookmarks service. Invoke the test like this:
* xpcshell -f testbookmarks.js
*/
const NS_STORAGE_FILE = "UStor";
const nsIFile = Components.interfaces.nsIFile;
// If there's no location registered for the storage file, register one now.
var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties);
var storageFile = null;
try {
storageFile = dirSvc.get(NS_STORAGE_FILE, nsIFile);
} catch (e) {}
if (!storageFile) {
// Register our own provider for the storage file. It will create the file
// "storage.sdb" in the current directory.
var provider = {
getFile: function(prop, persistent) {
persistent.value = true;
if (prop == NS_STORAGE_FILE) {
var file = dirSvc.get("CurProcD", nsIFile);
file.append("storage.sdb");
return file;
}
throw Components.results.NS_ERROR_FAILURE;
},
QueryInterface: function(iid) {
if (iid.equals(Components.interfaces.nsIDirectoryServiceProvider) ||
iid.equals(Components.interfaces.nsISupports)) {
return this;
}
throw Components.results.NS_ERROR_NO_INTERFACE;
}
};
dirSvc.QueryInterface(Components.interfaces.nsIDirectoryService).registerProvider(provider);
}
var iosvc = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
function uri(spec) {
return iosvc.newURI(spec, null, null);
}
dump("starting tests\n");
var bmsvc = Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Components.interfaces.nsINavBookmarksService);
var observer = {
onBeginUpdateBatch: function() {
this._beginUpdateBatch = true;
},
onEndUpdateBatch: function() {
this._endUpdateBatch = true;
},
get wantAllDetails() { return this._wantAllDetails; },
onItemAdded: function(uri, folder, index) {
this._itemAdded = uri;
this._itemAddedFolder = folder;
this._itemAddedIndex = index;
},
onItemRemoved: function(uri, folder, index) {
this._itemRemoved = uri;
this._itemRemovedFolder = folder;
this._itemRemovedIndex = index;
},
onItemChanged: function(uri, property) {
this._itemChanged = uri;
this._itemChangedProperty = property;
},
onFolderAdded: function(folder, parent, index) {
this._folderAdded = folder;
this._folderAddedParent = parent;
this._folderAddedIndex = index;
},
onFolderRemoved: function(folder, parent, index) {
this._folderRemoved = folder;
this._folderRemovedParent = parent;
this._folderRemovedIndex = index;
},
onFolderMoved: function(folder, parent, index) {
this._folderMoved = folder;
this._folderMovedParent = parent;
this._folderMovedIndex = index;
},
QueryInterface: function(iid) {
if (iid.equals(Components.interfaces.nsINavBookmarkObserver) ||
iid.equals(Components.interfaces.nsISupports)) {
return this;
}
throw Components.results.NS_ERROR_NO_INTERFACE;
},
_wantAllDetails: true
};
bmsvc.addObserver(observer);
var root = bmsvc.bookmarksRoot;
// add some bookmarks and folders
bmsvc.insertItem(root, uri("http://www.mozilla.org/"), 0);
if (observer._itemAdded.spec != "http://www.mozilla.org/" ||
observer._itemAddedFolder != root || observer._itemAddedIndex != 0) {
dump("insertItem notification FAILED\n");
}
bmsvc.setItemTitle(uri("http://www.mozilla.org/"), "Mozilla.org");
if (observer._itemChanged.spec != "http://www.mozilla.org/" ||
observer._itemChangedProperty != "title") {
dump("setItemTitle notification FAILED\n");
}
bmsvc.insertItem(root, uri("http://google.com/"), -1);
if (observer._itemAdded.spec != "http://google.com/" ||
observer._itemAddedFolder != root || observer._itemAddedIndex != 1) {
dump("insertItem notification FAILED\n");
}
bmsvc.setItemTitle(uri("http://google.com/"), "Google");
if (observer._itemChanged.spec != "http://google.com/" ||
observer._itemChangedProperty != "title") {
dump("setItemTitle notification FAILED\n");
}
bmsvc.removeItem(root, uri("http://www.mozilla.org/"));
if (observer._itemRemoved.spec != "http://www.mozilla.org/" ||
observer._itemRemovedFolder != root || observer._itemRemovedIndex != 0) {
dump("removeItem notification FAILED\n");
}
bmsvc.insertItem(root, uri("http://www.mozilla.org/"), -1);
if (observer._itemAdded.spec != "http://www.mozilla.org/" ||
observer._itemAddedFolder != root || observer._itemAddedIndex != 1) {
dump("insertItem notification FAILED\n");
}
/// EXPECTED TABLE RESULTS
/// moz_bookmarks_assoc:
/// item_child folder_child parent position
/// ---------- ------------ ------ --------
/// 1
/// 2 1 0
/// 1 1 1
///
/// moz_history:
/// id url
/// -- ------------------------
/// 1 http://www.mozilla.org/
/// 2 http://google.com/
///
/// moz_bookmarks_containers:
/// id
/// --
// 1