diff --git a/mozilla/browser/components/build/nsBrowserCompsCID.h b/mozilla/browser/components/build/nsBrowserCompsCID.h index b8cab73ad2f..68567775bfe 100644 --- a/mozilla/browser/components/build/nsBrowserCompsCID.h +++ b/mozilla/browser/components/build/nsBrowserCompsCID.h @@ -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" diff --git a/mozilla/browser/components/build/nsModule.cpp b/mozilla/browser/components/build/nsModule.cpp index c2fa0159109..a24c2491324 100644 --- a/mozilla/browser/components/build/nsModule.cpp +++ b/mozilla/browser/components/build/nsModule.cpp @@ -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", diff --git a/mozilla/browser/components/places/public/Makefile.in b/mozilla/browser/components/places/public/Makefile.in index 95466d35746..a8b6465d874 100644 --- a/mozilla/browser/components/places/public/Makefile.in +++ b/mozilla/browser/components/places/public/Makefile.in @@ -49,6 +49,7 @@ XPIDL_MODULE = places XPIDLSRCS = \ nsIAnnotationService.idl \ nsINavHistory.idl \ + nsINavBookmarksService.idl \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/mozilla/browser/components/places/public/nsINavBookmarksService.idl b/mozilla/browser/components/places/public/nsINavBookmarksService.idl new file mode 100644 index 00000000000..a14a06a4366 --- /dev/null +++ b/mozilla/browser/components/places/public/nsINavBookmarksService.idl @@ -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 (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(); +}; diff --git a/mozilla/browser/components/places/public/nsINavHistory.idl b/mozilla/browser/components/places/public/nsINavHistory.idl index 46519be1677..f9f62cc6957 100644 --- a/mozilla/browser/components/places/public/nsINavHistory.idl +++ b/mozilla/browser/components/places/public/nsINavHistory.idl @@ -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 diff --git a/mozilla/browser/components/places/public/nsINavHistoryService.idl b/mozilla/browser/components/places/public/nsINavHistoryService.idl index 46519be1677..f9f62cc6957 100644 --- a/mozilla/browser/components/places/public/nsINavHistoryService.idl +++ b/mozilla/browser/components/places/public/nsINavHistoryService.idl @@ -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 diff --git a/mozilla/browser/components/places/src/Makefile.in b/mozilla/browser/components/places/src/Makefile.in index 7bf21ebec76..4b0a9ce7fb5 100644 --- a/mozilla/browser/components/places/src/Makefile.in +++ b/mozilla/browser/components/places/src/Makefile.in @@ -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 diff --git a/mozilla/browser/components/places/src/nsNavBookmarks.cpp b/mozilla/browser/components/places/src/nsNavBookmarks.cpp new file mode 100644 index 00000000000..7f7fac43f2b --- /dev/null +++ b/mozilla/browser/components/places/src/nsNavBookmarks.cpp @@ -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 (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 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 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 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 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 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 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, 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* children = &aNode->mChildren; + + PRBool results; + while (NS_SUCCEEDED(mDBGetChildren->ExecuteStep(&results)) && results) { + nsCOMPtr 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; +} diff --git a/mozilla/browser/components/places/src/nsNavBookmarks.h b/mozilla/browser/components/places/src/nsNavBookmarks.h new file mode 100644 index 00000000000..37373e72985 --- /dev/null +++ b/mozilla/browser/components/places/src/nsNavBookmarks.h @@ -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 (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 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 mObservers; + PRInt64 mRoot; + + nsCOMPtr mDBGetFolderInfo; // kGetFolderInfoIndex_* results + static const PRInt32 kGetFolderInfoIndex_FolderID; + static const PRInt32 kGetFolderInfoIndex_Title; + + nsCOMPtr 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 mDBFindURIBookmarks; // kFindBookmarksIndex_* results + static const PRInt32 kFindBookmarksIndex_ItemChild; + static const PRInt32 kFindBookmarksIndex_FolderChild; + static const PRInt32 kFindBookmarksIndex_Parent; + static const PRInt32 kFindBookmarksIndex_Position; + + nsCOMPtr mDBFolderCount; + + friend class nsNavFolderResultNode; +}; diff --git a/mozilla/browser/components/places/src/nsNavHistory.cpp b/mozilla/browser/components/places/src/nsNavHistory.cpp index dbb2827c9aa..e054326e55c 100644 --- a/mozilla/browser/components/places/src/nsNavHistory.cpp +++ b/mozilla/browser/components/places/src/nsNavHistory.cpp @@ -72,6 +72,7 @@ Extra: #include #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 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 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 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& 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; +} diff --git a/mozilla/browser/components/places/src/nsNavHistory.h b/mozilla/browser/components/places/src/nsNavHistory.h index dee51dc9e73..6d36ab053c4 100644 --- a/mozilla/browser/components/places/src/nsNavHistory.h +++ b/mozilla/browser/components/places/src/nsNavHistory.h @@ -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 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 mDBGetVisitPageInfo; // kGetInfoIndex_* results nsCOMPtr mDBGetURLPageInfo; // kGetInfoIndex_* results nsCOMPtr 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* aResults); - nsresult RowToResult(mozIStorageValueArray* aRow, PRBool aAsVisits, - nsNavHistoryResultNode** aResult); void TitleForDomain(const nsString& domain, nsAString& aTitle); nsresult RecursiveGroup(const nsCOMArray& aSource, @@ -486,3 +536,5 @@ protected: */ nsresult BindStatementURI(mozIStorageStatement* statement, PRInt32 index, nsIURI* aURI); + +#endif // nsNavHistory_h_ diff --git a/mozilla/browser/components/places/tests/testbookmarks.js b/mozilla/browser/components/places/tests/testbookmarks.js new file mode 100755 index 00000000000..a5fe8af1d81 --- /dev/null +++ b/mozilla/browser/components/places/tests/testbookmarks.js @@ -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 + * + * 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