From 46c823803dc3e2c941a486b20d6e014c90ca556a Mon Sep 17 00:00:00 2001 From: "benjamin%smedbergs.us" Date: Thu, 20 Jul 2006 20:16:15 +0000 Subject: [PATCH] Bug 321219 - separate places backend into toolkit, removing old files which now live in toolkit/components/places, r=beng git-svn-id: svn://10.0.0.236/trunk@203286 18797224-902f-48f8-a5cc-f745e15eee43 --- .../components/places/public/Makefile.in | 58 - .../places/public/nsIAnnotationService.idl | 308 - .../places/public/nsIFaviconService.idl | 261 - .../places/public/nsILivemarkService.idl | 146 - .../places/public/nsINavBookmarksService.idl | 507 -- .../places/public/nsINavHistoryService.idl | 1307 ---- .../places/public/nsIRemoteContainer.idl | 147 - .../browser/components/places/src/Makefile.in | 107 - .../places/src/nsAnnoProtocolHandler.cpp | 245 - .../places/src/nsAnnoProtocolHandler.h | 69 - .../places/src/nsAnnotationService.cpp | 1145 ---- .../places/src/nsAnnotationService.h | 124 - .../places/src/nsBookmarksFeedHandler.cpp | 940 --- .../components/places/src/nsBookmarksHTML.cpp | 1709 ------ .../places/src/nsFaviconService.cpp | 951 --- .../components/places/src/nsFaviconService.h | 129 - .../places/src/nsLivemarkService.cpp | 778 --- .../components/places/src/nsLivemarkService.h | 170 - .../components/places/src/nsMaybeWeakPtr.cpp | 114 - .../components/places/src/nsMaybeWeakPtr.h | 124 - .../places/src/nsMorkHistoryImporter.cpp | 255 - .../places/src/nsMorkHistoryImporter.h | 66 - .../components/places/src/nsNavBookmarks.cpp | 2355 ------- .../components/places/src/nsNavBookmarks.h | 263 - .../components/places/src/nsNavHistory.cpp | 4302 ------------- .../components/places/src/nsNavHistory.h | 610 -- .../places/src/nsNavHistoryAutoComplete.cpp | 711 --- .../places/src/nsNavHistoryExpire.cpp | 643 -- .../places/src/nsNavHistoryExpire.h | 114 - .../places/src/nsNavHistoryQuery.cpp | 1328 ---- .../components/places/src/nsNavHistoryQuery.h | 180 - .../places/src/nsNavHistoryResult.cpp | 5395 ----------------- .../places/src/nsNavHistoryResult.h | 803 --- 33 files changed, 26364 deletions(-) delete mode 100644 mozilla/browser/components/places/public/Makefile.in delete mode 100644 mozilla/browser/components/places/public/nsIAnnotationService.idl delete mode 100644 mozilla/browser/components/places/public/nsIFaviconService.idl delete mode 100755 mozilla/browser/components/places/public/nsILivemarkService.idl delete mode 100644 mozilla/browser/components/places/public/nsINavBookmarksService.idl delete mode 100644 mozilla/browser/components/places/public/nsINavHistoryService.idl delete mode 100644 mozilla/browser/components/places/public/nsIRemoteContainer.idl delete mode 100644 mozilla/browser/components/places/src/Makefile.in delete mode 100644 mozilla/browser/components/places/src/nsAnnoProtocolHandler.cpp delete mode 100644 mozilla/browser/components/places/src/nsAnnoProtocolHandler.h delete mode 100644 mozilla/browser/components/places/src/nsAnnotationService.cpp delete mode 100644 mozilla/browser/components/places/src/nsAnnotationService.h delete mode 100755 mozilla/browser/components/places/src/nsBookmarksFeedHandler.cpp delete mode 100644 mozilla/browser/components/places/src/nsBookmarksHTML.cpp delete mode 100644 mozilla/browser/components/places/src/nsFaviconService.cpp delete mode 100644 mozilla/browser/components/places/src/nsFaviconService.h delete mode 100755 mozilla/browser/components/places/src/nsLivemarkService.cpp delete mode 100755 mozilla/browser/components/places/src/nsLivemarkService.h delete mode 100644 mozilla/browser/components/places/src/nsMaybeWeakPtr.cpp delete mode 100644 mozilla/browser/components/places/src/nsMaybeWeakPtr.h delete mode 100644 mozilla/browser/components/places/src/nsMorkHistoryImporter.cpp delete mode 100644 mozilla/browser/components/places/src/nsMorkHistoryImporter.h delete mode 100644 mozilla/browser/components/places/src/nsNavBookmarks.cpp delete mode 100644 mozilla/browser/components/places/src/nsNavBookmarks.h delete mode 100644 mozilla/browser/components/places/src/nsNavHistory.cpp delete mode 100644 mozilla/browser/components/places/src/nsNavHistory.h delete mode 100644 mozilla/browser/components/places/src/nsNavHistoryAutoComplete.cpp delete mode 100644 mozilla/browser/components/places/src/nsNavHistoryExpire.cpp delete mode 100644 mozilla/browser/components/places/src/nsNavHistoryExpire.h delete mode 100644 mozilla/browser/components/places/src/nsNavHistoryQuery.cpp delete mode 100644 mozilla/browser/components/places/src/nsNavHistoryQuery.h delete mode 100755 mozilla/browser/components/places/src/nsNavHistoryResult.cpp delete mode 100644 mozilla/browser/components/places/src/nsNavHistoryResult.h diff --git a/mozilla/browser/components/places/public/Makefile.in b/mozilla/browser/components/places/public/Makefile.in deleted file mode 100644 index 1e729168643..00000000000 --- a/mozilla/browser/components/places/public/Makefile.in +++ /dev/null @@ -1,58 +0,0 @@ -# -# ***** 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 code. -# -# 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): -# Brett Wilson -# -# 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 ***** - -DEPTH = ../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -MODULE = places -XPIDL_MODULE = places - -XPIDLSRCS = \ - nsIAnnotationService.idl \ - nsIFaviconService.idl \ - nsINavHistoryService.idl \ - nsINavBookmarksService.idl \ - nsILivemarkService.idl \ - nsIRemoteContainer.idl \ - $(NULL) - -include $(topsrcdir)/config/rules.mk diff --git a/mozilla/browser/components/places/public/nsIAnnotationService.idl b/mozilla/browser/components/places/public/nsIAnnotationService.idl deleted file mode 100644 index 7fd7e60a334..00000000000 --- a/mozilla/browser/components/places/public/nsIAnnotationService.idl +++ /dev/null @@ -1,308 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** 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 Annotation code - * - * 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): - * Brett Wilson - * - * 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" - -%{C++ -#include "nsTArray.h" -#include "nsCOMArray.h" -#include "nsString.h" -%} - -interface nsIURI; -interface nsIVariant; - -[ptr] native CStringArray(nsTArray); -[ptr] native URIArray(nsCOMArray); - -[scriptable, uuid(d41c9510-2377-4728-b275-bdad6a0521f8)] -interface nsIAnnotationObserver : nsISupports -{ - /** - * Called when an annotation value is set. It could be a new annotation, - * or it could be a new value for an existing annotation. - */ - void onAnnotationSet(in nsIURI aURI, in AUTF8String aName); - - /** - * Called when an annotation is deleted. If aName is empty, then ALL - * annotations for the given URI have been deleted. This is not called when - * annotations are expired (normally happens when the app exits). - */ - void onAnnotationRemoved(in nsIURI aURI, in AUTF8String aName); -}; - -[scriptable, uuid(a857c97f-7705-4376-b374-bd8799f69d51)] -interface nsIAnnotationService : nsISupports -{ - /** - * Valid values for aExpiration, which sets the expiration policy for your - * annotation. These times are measured since the last visit date of the - * page in question. This means that if you set an annotation with anything - * but session expiration, it will not expire so long as the user keeps - * visiting the page from time to time. - */ - - /* EXPIRATION IS CURRENTLY UNIMPLEMENTED. This is here as a proposed - expiration policy. May be changed. Currently, use 0 for expiration. - */ - // For temporary stuff that can be discarded when the user exists - const PRInt32 EXPIRE_SESSION = 0; - - // for short-lived temporary data that you still want to outlast a session - const PRInt32 EXPIRE_DAYS = 1; - - // for general page settings, things the user is interested in seeing - // if they come back to this page some time in the future. - const PRInt32 EXPIRE_WEEKS = 2; - - // Something that the user will be interested in seeing in their - // history like favicons. If they haven't visited a page in a couple - // of months, they probably aren't interested in much other annotation, - // the positions of things, or other stuff you create, so put that in - // the weeks policy. - const PRInt32 EXPIRE_MONTHS = 3; - - // For small, user-entered data like notes that should never expire. - const PRInt32 EXPIRE_NEVER = 4; - - /** - * Sets an annotation, overwriting any previous annotation with the same - * URL/name. IT IS YOUR JOB TO NAMESPACE YOUR ANNOTATION NAMES. - * Use the form "namespace/value", so your name would be like - * "bills_extension/page_state" or "history/thumbnail". - * - * Do not use characters that are not valid in URLs such as spaces, ":", - * commas, or most other symbols. You should stick to ASCII letters and - * numbers plus "_", "-", and "/". - * - * aExpiration is one of EXPIRE_* above. aFlags should be 0 for now, some - * flags will be defined in the future. - * - * NOTE: ALL ANNOTATIONS WILL GET DELETED WHEN THE PAGE IS REMOVED FROM - * HISTORY, regardless of expiration date. This means that if you create an - * annotation on a random unvisited URI, it will get deleted when the - * browser shuts down. Otherwise, things can exist in history as - * annotations but the user has no way of knowing it, potentially violating - * their privacy expectations about actions such as "Clear history." If - * there is an important annotation that the user wants to keep, you should - * make sure that URL is bookmarked. This will ensure the item is never - * completely deleted from the history database. - * - * The annotation "favicon" is special. favicons are stored in the favicon - * service, but are special cased in the protocol handler so they look like - * annotations. Do not set favicons using this service, it will not work. - */ - void setAnnotationString(in nsIURI aURI, in AUTF8String aName, - in AString aValue, in PRInt32 aFlags, - in PRInt32 aExpiration); - - /** - * Sets an annotation just like setAnnotationString, but takes an Int32 as - * input. - */ - void setAnnotationInt32(in nsIURI aURI, in AUTF8String aName, - in PRInt32 aValue, in PRInt32 aFlags, - in PRInt32 aExpiration); - - /** - * Sets an annotation just like setAnnotationString, but takes an Int64 as - * input. - */ - void setAnnotationInt64(in nsIURI aURI, in AUTF8String aName, - in PRInt64 aValue, in PRInt32 aFlags, - in PRInt32 aExpiration); - - /** - * Sets an annotation just like setAnnotationString, but takes a double as - * input. - */ - void setAnnotationDouble(in nsIURI aURI, in AUTF8String aName, - in double aValue, in PRInt32 aFlags, - in PRInt32 aExpiration); - - /* - * Sets an annotation just like setAnnotationString, but takes binary data - * as input. You MUST supply a valid MIME type. - */ - void setAnnotationBinary(in nsIURI aURI, in AUTF8String aName, - [const,array,size_is(aDataLen)] in octet aData, - in PRUint32 aDataLen, in AUTF8String aMimeType, - in PRInt32 aFlags, in PRInt32 aExpiration); - - /** - * Retrieves the string value of a given annotation. Throws an error if the - * annotation does not exist. If the annotation was set as a different - * type than you are retrieving it as, the value will be converted as best - * as we can. You aren't always guaranteed a good conversion, however, - * and errors will not be thrown in this case. (For example, doubles will - * lose precision when stringified.) - */ - AString getAnnotationString(in nsIURI aURI, in AUTF8String aName); - - /** - * Same as getAnnotationString but for ints. If the value doesn't look like - * an int, returns 0. (this is current sqlite behavior when asking for an - * int when there is not one, it will possibly change in the future if we - * start caching stuff). - */ - PRInt32 getAnnotationInt32(in nsIURI aURI, in AUTF8String aName); - - /** - * Same as getAnnotationString for Int64s. If the value doesn't look like - * an int, returns 0. (this is current sqlite behavior when asking for an - * int when there is not one, it will possibly change in the future if we - * start caching stuff). - */ - PRInt64 getAnnotationInt64(in nsIURI aURI, in AUTF8String aName); - - /** - * Same as getAnnotationString but returns a double-precision float. If the - * value doesn't look like an float, returns 0. (this is current sqlite - * behavior when asking for an number when there is not one, it will - * possibly change in the future if we start caching stuff). - */ - double getAnnotationDouble(in nsIURI aURI, in AUTF8String aName); - - /** - * Same as getAnnotationString but for binary data. This also returns the - * MIME type. - */ - void getAnnotationBinary(in nsIURI aURI, in AUTF8String aName, - [array,size_is(aDataLen)] out octet aData, - out PRUint32 aDataLen, - out AUTF8String aMimeType); - - /** - * Retrieves info about an existing annotation. aMimeType will be empty - * if the value was not binary data. - * - * aStorageType will be one of mozIStorageValueArray.VALUE_TYPE_* and - * indicates how the value is stored (if you want to determine whether - * the data is binary, etc.) - * - * example JS: - * var flags = {}, exp = {}, mimeType = {}; - * annotator.getAnnotationInfo(myURI, "foo", flags, exp, mimeType); - * // now you can use 'exp.value' and 'flags.value' - */ - void getAnnotationInfo(in nsIURI aURI, in AUTF8String aName, - out PRInt32 aFlags, out PRInt32 aExpiration, - out AUTF8String aMimeType, out PRInt32 aStorageType); - - /** - * Returns a list of all URIs having a given annotation. - */ - void getPagesWithAnnotation(in AUTF8String name, - out PRUint32 resultCount, - [retval, array, size_is(resultCount)] out nsIURI results); - - /** - * COMArray version of getPagesWithAnnotation for easier memory - * management from C++ code; - */ - [noscript] void GetPagesWithAnnotationCOMArray(in AUTF8String aName, - in URIArray aResults); - - /** - * Get the names of all annotations for this URI. - * - * example JS: - * var annotations = annotator.getPageAnnotations(myURI, {}); - * You probably don't want to use this from C++, use - * GetPageAnnotationsTArray instead. - */ - void getPageAnnotationNames(in nsIURI aURI, out PRUint32 count, - [retval, array, size_is(count)] out nsIVariant result); - - /** - * TArray version of getPageAnnotationNames for ease of use in C++ code. - */ - [noscript] void GetPageAnnotationNamesTArray(in nsIURI aURI, in CStringArray aResult); - - /** - * Test for annotation existance. - */ - boolean hasAnnotation(in nsIURI aURI, in AUTF8String aName); - - /** - * Removes a specific annotation. Succeeds even if the annotation is - * not found. - */ - void removeAnnotation(in nsIURI aURI, in AUTF8String aName); - - /** - * Removes all annotations for the given page. - * We may want some other similar functions to get annotations with given - * flags (once we have flags defined). - */ - void removePageAnnotations(in nsIURI aURI); - - /** - * Copies all annotations from the source to the destination URI. If the - * destination already has an annotation with the same name as one on the - * source, it will be overwritten if aOverwriteDest is set. Otherwise, - * the destination URIs will be preferred. - * - * All the source annotations will stay as-is. If you don't want them - * any more, use removePageAnnotations on that URI. - */ - void copyAnnotations(in nsIURI aSourceURI, in nsIURI aDestURI, - in boolean aOverwriteDest); - - /** - * Adds an annotation observer. The annotation service will keep an owning - * reference to the observer object. - */ - void addObserver(in nsIAnnotationObserver aObserver); - - - /** - * Removes an annotaton observer previously registered by addObserver. - */ - void removeObserver(in nsIAnnotationObserver aObserver); - - /** - * Returns a URI that can be used to access the given binary annotation. - * This function does NOT check that the annotation exists. Also, note that - * you can only load URIs for annotations that have have a valid MIME type - * set by setAnnotationBinary. No non-URI valid chars in name, especially - * colon, which will mess up parsing. - */ - nsIURI getAnnotationURI(in nsIURI aURI, in AUTF8String aName); -}; diff --git a/mozilla/browser/components/places/public/nsIFaviconService.idl b/mozilla/browser/components/places/public/nsIFaviconService.idl deleted file mode 100644 index 4066aea724c..00000000000 --- a/mozilla/browser/components/places/public/nsIFaviconService.idl +++ /dev/null @@ -1,261 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** 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 code - * - * 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): - * Brett Wilson - * - * 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; - -[scriptable, uuid(b7fc8b9b-d85f-4d89-9b2a-0e67bd6e8d78)] -interface nsIFaviconService : nsISupports -{ - /** - * Declares that a given page uses a favicon with the given URI. - * - * You needn't have specified any data at this point. An entry linking the - * favicon with the page will be create with no data. You can populate it - * later with SetFaviconData. However, any favicons not associated with a - * visited web page, a bookmark, or a "place:" URI will be expired when - * history cleanup is done * (typically at app shutdown, but also possibly - * if the user clears their * cache or history). - * - * This will send out history notifications if the new favicon has any data. - * This means that you should try to set data first if you have it, otherwise - * the page might not get a notification sent for it since data setting does - * not send notifications. - * - * @param aPage - * URI of the page whose favicon is being set. - * @param aFavicon - * URI of the favicon to associate with the page. - */ - void setFaviconUrlForPage(in nsIURI aPage, in nsIURI aFavicon); - - /** - * Same as SetFaviconUrlForPage except that this also attempts to set the - * data by loading the favicon URI. An async request will be created for - * this URI and if the data is available, it will asynchronously get - * saved in the database without any further work from the caller. - * - * If the icon data already exists, we won't normally try to re-load the - * icon from the net (or more likely the cache). If the icon is in the - * failed favicon cache we won't do anything. Use forceReload to force - * a reload of the data. This will remove the favicon from the failed - * cache. If it then fails again, it will be re-added to the failed cache. - * - * SetFaviconUrlForPage and SetFaviconData will take any URL you provide - * and save it. This function is intended for automatic usage, and will - * only save favicons for "good" URLs, as defined by what gets added to - * history. For "bad" URLs, this function will succeed and do nothing. - * This function will also ignore favicons that are data URLs. Icons that - * fail to load will automatically be added to the failed favicon cache. - * - * This function will not save favicons for non-bookmarked URLs when - * history is disabled (expiration time is 0 days). The rest of the functions - * here will always store favicons even when history is disabled. - * - * @param aPage - * URI of the page whose favicon is being set. - * @param aFavicon - * URI of the favicon to associate with the page. - * @param aForceReload - * Unset is normal behavior, we will only try to reload the favicon - * if we don't have it or if it has expired from the cache. If set, - * it will always try to reload the favicon. - */ - void setAndLoadFaviconForPage(in nsIURI aPage, in nsIURI aFavicon, - in boolean aForceReload); - - /** - * Stores the data of a given favicon. You must specify the MIME type - * unless you're clearing the data. - * - * You can set the data even if you haven't called SetFaviconUrlForPage - * yet. It will be stored but will not be associated with any page. - * However, any favicons not associated with a visited web page, bookmark, - * or "place:" URI will be expired when history cleanup is done. This might - * be done at any time on a timer, so you should not let the message loop - * run between calls or your icon may get deleted. - * - * It is best to set the favicon data, and then associate it with a page. - * This will make the notifications more efficient since the icon will - * already have data when the set favicon observer messages goes out. - * - * The expiration time is stored. This will be used if you call - * SetAndLoadFaviconForPage to see whether the data needs reloading. - * - * Do not use this function for chrome: icon URLs. You should reference the - * chrome image yourself. The GetFaviconLinkForIcon/Page will ignore any - * associated data if the favicon URI is "chrome:" and just return the same - * chrome URI. - * - * This function does NOT send out notifications that the data has changed. - * Potentially, many pages could be referencing the favicon and they could - * be visible in a history view or toolbar. But sending out those - * notifications is very intensive. Those pages will keep the old icon - * until they have been refreshed by other means. - * - * @param aFavicon - * URI of the favicon whose data is being set. - * @param aData - * Binary contents of the favicon to save - * @param aDataLength - * Length of binary data - * @param aMimeType - * MIME type of the data to store. This is important so that we know - * what to report when the favicon is used. - * @param aExpiration - * Time in microseconds since the epoch when this favicon expires. - * Until this time, we won't try to load it again. - */ - void setFaviconData(in nsIURI aFavicon, - [const,array,size_is(aDataLen)] in octet aData, - in unsigned long aDataLen, in AUTF8String aMimeType, - in PRTime aExpiration); - - /** - * Retrieves the given favicon data. Throws if we don't have data. - * - * If there is no data but we have an entry for this favicon, aDataLen will - * be 0 and aData will be NULL. - * - * @param aFavicon - * URL of the favicon whose data is being read - * @param aData - * Output parameter where the binary favicon data will be placed. - * This will be null if we have this URL but have no data associated - * with it. - * @param aDataLen - * Output parameter where the size of the binary data will be placed. - * @param aMimeType - * Output parameter where the MIME type will be placed. - * @throws NS_ERROR_NOT_AVAILABLE - * Thrown when we have never heard of this favicon URL. - */ - void getFaviconData(in nsIURI aFavicon, - out AUTF8String aMimeType, - out unsigned long aDataLen, - [array,retval,size_is(aDataLen)] out octet aData); - - /** - * Retrieves the URL of the favicon for the given page. - * - * @param aPage - * URI of the page whose favicon is desired - * @returns The URI of the favicon associated with that page. Returning a - * URI here does NOT mean that we have data for this favicon, only - * that we know what the favicon should be. - * @throws NS_ERROR_NOT_AVAILABLE - * When the page is not found or it has no favicon. - */ - nsIURI getFaviconForPage(in nsIURI aPage); - - /** - * For a given page, this will give you a URI that, when displayed in chrome, - * will result in the given page's favicon. Unlike the other get functions, - * we needn't have heard of the page or its favicon: the default one will - * be returned in this case. - * - * @see getFaviconLinkForIcon - * This function only adds the extra level of indirection, looking up - * the favicon based on the page URI and using the default if not found. - * - * @param aPage - * URI of the page whose favicon is desired - * @returns A URI that will give you the icon image. This is NOT the URI of - * the icon as set on the page, but a URI that will give you the - * data out of the favicon service. For a normal page with a - * favicon we've stored, this will be an annotation URI which will - * then cause the corresponding favicon data to be loaded from this - * service. For pages where we don't have a favicon, this will be a - * chrome URI of the default icon for a web page. - */ - nsIURI getFaviconImageForPage(in nsIURI aPage); - - /** - * For a given icon URI, this will return a URI that will result in the image. - * In most cases, this is an annotation URI. For chrome, this will do nothing - * and return the input URI. For NULL input, this will return the URI of - * the default favicon. - * - * @param aIcon - * The URL of an icon in the favicon service. Can be NULL. - * @returns A URI that will load the desired icon. This is NOT the URI of the - * icon as set on the page, but a URI that will give you the data - * out of the favicon service. For a normal page with a favicon - * we've stored, this will be an annotation URI which will then - * cause the corresponding favicon data to be loaded from this - * service. For pages where we don't have a favicon, this will be a - * chrome URI of the default icon for a web page. For chrome, the - * output will be the same as the input. For NULL input, this will - * be the URI of the default favicon. - * - * No validity checking is done. If you pass an icon URI that we've - * never seen, you'll get back a URI that references an invalid - * icon. The moz-anno protocol handler's special case for "favicon" - * annotations will detect most invalid icons and it will resolve to - * the default icon, although without caching. For invalid chrome - * URIs, you'll get a broken image. - */ - nsIURI getFaviconLinkForIcon(in nsIURI aIcon); - - /** - * Adds a given favicon's URI to the failed favicon cache. - * - * The lifespan of the favicon cache is up to the caching system. This cache - * will also be written to if you use setAndLoadFaviconForPage and it - * encounters an error. - * - * @see isFailedFavicon - */ - void addFailedFavicon(in nsIURI aIcon); - - /** - * Removes the given favicon from the failed favicon cache. If the icon is - * not in the cache, this function will silently succeed. - */ - void removeFailedFavicon(in nsIURI aIcon); - - /** - * Checks to see if this favicon is in the failed favicon cache. Returns true - * if the favicon is in the failed cache, meaning you probably shouldn't try - * to load it. A false return value means that it's worth trying to load it. - * This allows you to avoid trying to load "foo.com/favicon.ico" for every - * page on a site that doesn't have a favicon. - */ - boolean isFailedFavicon(in nsIURI aIcon); -}; diff --git a/mozilla/browser/components/places/public/nsILivemarkService.idl b/mozilla/browser/components/places/public/nsILivemarkService.idl deleted file mode 100755 index 07dabe7e0d0..00000000000 --- a/mozilla/browser/components/places/public/nsILivemarkService.idl +++ /dev/null @@ -1,146 +0,0 @@ -/* -*- 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 code - * - * 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): - * Annie Sullivan - * Joe Hughes - * - * 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" -#include "nsIRemoteContainer.idl" - -interface nsIURI; - -[scriptable, uuid(1e66ec55-87fa-45c2-a912-dd44afb06922)] -interface nsILivemarkService : nsIRemoteContainer -{ - /** - * Creates a new livemark - * @param folder The id of the parent folder - * @param name The name to show when displaying the livemark - * @param siteURI The URI of the site the livemark was created from - * @param feedURI The URI of the actual RSS feed - * @param index The index to insert at, or -1 to append - * @returns the ID of the folder for the livemark - */ - PRInt64 createLivemark(in PRInt64 folder, - in AString name, - in nsIURI siteURI, - in nsIURI feedURI, - in PRInt32 index); - - /** - * Determines whether the folder with the given folder ID identifies - * a livemark container. - * - * @param folder A folder ID - * - * @returns true if the given folder is a livemark folder, or - * false otherwise - * - * @throws NS_ERROR_INVALID_ARG if the folder ID isn't known - */ - boolean isLivemark(in PRInt64 folder); - - /** - * Gets the URI of the website associated with a livemark container. - * - * @param container The folder ID of a livemark container - * - * @returns nsIURI representing the URI of the website; if the livemark - * container doesn't have a valid site URI, null will be returned - * - * @throws NS_ERROR_INVALID_ARG if the folder ID isn't known or identifies - * a folder that isn't a livemark container - * @throws NS_ERROR_MALFORMED_URI if the site URI annotation has - * somehow been corrupted (and can't be turned into an nsIURI) - */ - nsIURI getSiteURI(in PRInt64 container); - - /** - * Sets the URI of the website associated with a livemark container. - * - * @param container The folder ID of a livemark container - * @param siteURI nsIURI object representing the site URI, or null - * to clear the site URI for this livemark container - * - * @throws NS_ERROR_INVALID_ARG if the folder ID isn't known or identifies - * a folder that isn't a livemark container; also if the siteURI - * argument isn't a valid nsIURI object (or null) - */ - void setSiteURI(in PRInt64 container, in nsIURI siteURI); - - /** - * Gets the URI of the syndication feed associated with a livemark container. - * - * @param container The folder ID of a livemark container - * - * @returns nsIURI representing the URI of the feed; if the livemark - * container doesn't have a valid feed URI, null will be returned - * of the nsIURI object returned will be the empty string. - * - * @throws NS_ERROR_INVALID_ARG if the folder ID isn't known or identifies - * a folder that isn't a livemark container - * @throws NS_ERROR_MALFORMED_URI if the site URI annotation has - * somehow been corrupted (and can't be turned into an nsIURI) - */ - nsIURI getFeedURI(in PRInt64 container); - - /** - * Sets the URI of the feed associated with a livemark container. - * - * NOTE: The caller is responsible for reloading the livemark after - * changing its feed URI (since the contents are likely to be different - * given a different feed). - * - * @param container The folder ID of a livemark container - * @param feedURI nsIURI object representing the syndication feed URI - * - * @throws NS_ERROR_INVALID_ARG if the folder ID isn't known or identifies - * a folder that isn't a livemark container; also if the feedURI - * argument isn't a valid nsIURI object - */ - void setFeedURI(in PRInt64 container, in nsIURI feedURI); - - /** - * Reloads all livemark subscriptions, whether or not they've expired. - */ - void reloadAllLivemarks(); - - /** - * Reloads the livemark with this folder ID, whether or not it's expired. - * @param folderID The ID of the folder to be reloaded - */ - void reloadLivemarkFolder(in PRInt64 folderID); -}; diff --git a/mozilla/browser/components/places/public/nsINavBookmarksService.idl b/mozilla/browser/components/places/public/nsINavBookmarksService.idl deleted file mode 100644 index 59da7331c01..00000000000 --- a/mozilla/browser/components/places/public/nsINavBookmarksService.idl +++ /dev/null @@ -1,507 +0,0 @@ -/* -*- 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) - * Joe Hughes - * - * 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" - -%{C++ -#include "nsTArray.h" -#include "prtypes.h" -%} - -interface nsIFile; -interface nsIURI; -interface nsITransaction; - -[ptr] native PRInt64Array(nsTArray); - -/** - * Observer for bookmark changes. - */ - -[scriptable, uuid(224f8162-4da8-45eb-8187-a8ba3a3db8e5)] -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(); - - /** - * Notify this observer that the bookmark was added. Called after the actual - * add took place. The rest of the bookmarks will be shifted down, but no - * additional notifications will be sent. - * - * @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 PRInt32 index); - - /** - * Notify this observer that the bookmark was removed. Called after the - * actual remove took place. The bookmarks following the index will be - * shifted up, but no additional notifications will be sent. - * - * @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 folder, in PRInt32 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. - * - * property = "cleartime" (history was deleted, there is no last visit date): - * value = none - * property = "title": value = new title - * property = "favicon": value = new "moz-anno" URL of favicon image - */ - void onItemChanged(in nsIURI bookmark, in ACString property, - in AString value); - - /** - * Notify that the item was visited. Normally in bookmarks we use the last - * visit date, and normally the time will be a new visit that will be more - * recent, but this is not guaranteed. You should check to see if it's - * actually more recent before using this new time. - * - * @see onItemChanged properth = "cleartime" for when all visit dates are - * deleted for the URI. - */ - void onItemVisited(in nsIURI bookmark, in PRInt64 aVisitID, in PRTime time); - - /** - * Notify this observer that a bookmark has been replaced. - * - * @param folder The folder in which the bookmark was replaced - * @param item The item which was replaced - * @param newItem The new item which replaced item - */ - void onItemReplaced(in PRInt64 folder, in nsIURI item, in nsIURI newItem); - - /** - * 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 PRInt32 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 PRInt32 index); - - /** - * Notify this observer that a bookmark folder has been moved. - * @param folder The id of the folder that was moved. - * @param oldParent The id of the folder's old parent. - * @param oldIndex The folder's old index inside oldParent. - * @param newParent The id of the folder's new parent. - * @param newIndex The folder's index inside newParent. - */ - void onFolderMoved(in PRInt64 folder, - in PRInt64 oldParent, in PRInt32 oldIndex, - in PRInt64 newParent, in PRInt32 newIndex); - - /** - * 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); - - /** - * Notify this observer that a separator has been added. - * @param parent The id of the separator's parent. - * @param index The separator's index inside its parent. - */ - void onSeparatorAdded(in PRInt64 parent, in PRInt32 index); - - /** - * Notify this observer that a separator has been removed. - * @param parent The id of the separator's parent. - * @param index The separator's old index in its parent. - */ - void onSeparatorRemoved(in PRInt64 parent, in PRInt32 index); -}; - -/** - * 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(860d786d-9bba-4011-a396-486a87af8f07)] -interface nsINavBookmarksService : nsISupports -{ - /** - * The folder ID of the Places root. - */ - readonly attribute PRInt64 placesRoot; - - /** - * The folder ID of the bookmarks root. - */ - readonly attribute PRInt64 bookmarksRoot; - - /** - * The folder ID of the personal toolbar root. - */ - readonly attribute PRInt64 toolbarRoot; - - /** - * The folder ID of the top-level folders that contain the tag "folders". - * - * NOTE: This isn't wired up yet, so don't try to use it for anything until - * bug 329961 is marked resolved. - */ - readonly attribute PRInt64 tagRoot; - - /** - * Inserts a child item into the given folder. If this item already exists in - * the given folder, it will be moved to the new position. - * @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); - - /** - * Replace an item with a different item. - * @param folder The folder which contains the item to replace - * @param item The item which should be replaced - * @param newItem The new item - */ - void replaceItem(in PRInt64 folder, in nsIURI item, in nsIURI newItem); - - /** - * 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); - - /** - * Wrapper for container services. Creates a folder under the given - * parent and sets the container type. - * @param parent The id of the parent folder - * @param name The name of the new folder - * @param type The type of container to insert - * @param index The index to insert at, or -1 to append - * @returns the ID of the newly-inserted folder - */ - PRInt64 createContainer(in PRInt64 parent, in AString name, - in AString type, in PRInt32 index); - - /** - * Removes a folder from the bookmarks tree. - * @param folder The id of the folder to remove. - */ - void removeFolder(in PRInt64 folder); - - /** - * Gets an undo-able transaction for removing a folder from the bookmarks - * tree. - * @param folder The id of the folder to remove. - * @returns An object implementing nsITransaction that can be used to undo - * or redo the action. - * - * This method exists because complex delete->undo operations rely on - * recreated folders to have the same ID they had before they were deleted, - * so that any other items deleted in different transactions can be - * re-inserted correctly. This provides a safe encapsulation of this - * functionality without exposing the ability to recreate folders with - * specific IDs (potentially dangerous if abused by other code!) in the - * public API. - */ - nsITransaction getRemoveFolderTransaction(in PRInt64 folder); - - /** - * Convenience function for container services. Removes - * all children of the given folder. - * @param folder The id of the folder to remove children from. - */ - void removeFolderChildren(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); - - /** - * Returns the ID of a child folder with the given name. This does not - * recurse, you have to give it an immediate sibling of the given folder. - * If the given subfolder doesn't exist, it will return 0. - * @param folder Parent folder whose children we will search - * @param subFolder Name of the folder to search for in folder - */ - PRInt64 getChildFolder(in PRInt64 folder, in AString subFolder); - - /** - * Inserts a bookmark separator into the given folder at the given index. - * The separator can be removed using removeChildAt(). - * @param folder Parent folder of the separator - * @param index The separator's index under folder, or -1 to append - */ - void insertSeparator(in PRInt64 folder, in PRInt32 index); - - /** - * Removes any type of child (item, folder, or separator) at the given index. - * @param folder The folder to remove a child from - * @param index The index of the child to remove - */ - void removeChildAt(in PRInt64 folder, 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. - * - * If no item title is available (for instance, if the URI doesn't appear - * in the bookmarks or history), it will return a void string (null in JS). - * - * @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. Throws an invalid argument - * exception if it can't find a folder with the given ID. - * - * @param folder The folder whose title should be retrieved - * @returns The title for the folder - */ - AString getFolderTitle(in PRInt64 folder); - - /** - * Get the place: url for a bookmark folder. You can use this value to - * get/set the icon for a folder or to associate other data with it using - * the annotation service. Its important that you use this function instead - * of just serializing the query/options for a given folder because you - * may have different options or query parameters. This function will give - * the canonical value. - * @param folder The folder whose URI should be retrieved - * @returns The URI for the folder - */ - nsIURI getFolderURI(in PRInt64 folder); - - /** - * Checks whether a folder has read-only children. This property is - * defined by the nsIBookmarsContainer for the folder, if one exists. - * If this is set to true, UI should not allow the user to add, remove, - * or reorder children in this folder. The default for all folders is false. - */ - boolean getFolderReadonly(in PRInt64 folder); - - /** - * Returns true if the given URI is in any bookmark folder. If you want the - * results to be redirect-aware, use getBookmarkedURIFor() - */ - boolean isBookmarked(in nsIURI uri); - - /** - * Used to see if the given URI is bookmarked, or any page that redirected to - * it is bookmarked. For example, if I bookmark "mozilla.org" by manually - * typing it in, and follow the bookmark, I will get redirected to - * "www.mozilla.org". Logically, this new page is also bookmarked. This - * function, if given "www.mozilla.org", will return the URI of the bookmark, - * in this case "mozilla.org". - * - * If there is no bookmarked page found, it will return NULL. - */ - nsIURI getBookmarkedURIFor(in nsIURI uri); - - /** - * "Changes" the URI of a bookmark. Since URIs are the unique - * identifiers of bookmarks in this system, what this really means is - * that all the metadata attached to oldURI will be copied onto newURI, - * and oldURI will be deleted from the bookmark system. If newURI - * is already bookmarked, the metadata from oldURI will overwrite - * the corresponding metadata on newURI. Any annotations on oldURI - * will be copied to newURI (a copy will remain on oldURI). - */ - - void changeBookmarkURI(in nsIURI oldURI, in nsIURI newURI); - - /** - * Returns the list of folder ids that contain the given URI. - */ - void getBookmarkFolders(in nsIURI uri, out PRUint32 count, - [array, retval, size_is(count)] out PRInt64 folders); - - /** - * TArray version of getBookmarkFolders for ease of use in C++ code. - * Pass in a reference to a TArray; it will get cleared and filled with - * the resulting list of folder IDs. - */ - [noscript] void getBookmarkFoldersTArray(in nsIURI aURI, - in PRInt64Array aResult); - - /** - * Returns the index of the given item in the given folder. - * Returns -1 if the item is not present in the folder. - */ - PRInt32 indexOfItem(in PRInt64 folder, in nsIURI uri); - - /** - * Returns the index of the given subfolder in its parent. - * Returns -1 if the subfolder is not present in the parent folder. - */ - PRInt32 indexOfFolder(in PRInt64 parent, in PRInt64 folder); - - /** - * Associates the given keyword with the given URI. - * - * Use an empty keyword to clear the keyword associated with the URI. Use an - * empty URI to clear the URI associated with that keyword. In both of these - * cases, succeeds but does nothing if the URL/keyword is not found. - * - * When setting a keyword (both URI and keyword are specified), the URI must - * be bookmarked for the keyword to be persistent. - */ - void setKeywordForURI(in nsIURI uri, in AString keyword); - - /** - * Retrieves the keyword for the given URI. Will be void string - * (null in JS) if no such keyword is found. - */ - AString getKeywordForURI(in nsIURI uri); - - /** - * Returns the URI associated with the given keyword. Empty if no such - * keyword is found. - */ - nsIURI getURIForKeyword(in AString keyword); - - /** - * Adds a bookmark observer. If ownsWeak is false, the bookmark service will - * keep an owning reference to the observer. If ownsWeak is true, then - * aObserver must implement nsISupportsWeakReference, and the bookmark - * service will keep a weak reference to the observer. - */ - void addObserver(in nsINavBookmarkObserver observer, in boolean ownsWeak); - - /** - * Removes a bookmark observer. - */ - void removeObserver(in nsINavBookmarkObserver observer); - - /** - * 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. - * - * It is EXTREMELY IMPORTANT that you call EndUpdateBatch for each call to - * beginUpdateBatch. If you don't do this, certain parts of the UI will not - * get updated and any changes to bookmarks will not get written to disk. - * From C++ code inside the places component, use nsBookmarksUpdateBatcher - * defined in nsNavBookmarks.h to scope batches. For JS or from other - * components, just please be very careful to close the batch, especially - * when encountering an error and returning early. - */ - 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(); - - - /** - * Loads the given bookmarks.html file and merges it with the current - * bookmarks hierarchy. - */ - void importBookmarksHTML(in nsIURI url); - - - /** - * Loads the given bookmarks.html file and puts it in the given folder - */ - void importBookmarksHTMLToFolder(in nsIURI url, in PRInt64 folder); - - - /** - * Saves the current bookmarks hierarchy to a bookmarks.html file. - */ - void exportBookmarksHTML(in nsIFile file); -}; diff --git a/mozilla/browser/components/places/public/nsINavHistoryService.idl b/mozilla/browser/components/places/public/nsINavHistoryService.idl deleted file mode 100644 index 0b30ea76a60..00000000000 --- a/mozilla/browser/components/places/public/nsINavHistoryService.idl +++ /dev/null @@ -1,1307 +0,0 @@ -/* -*- 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 code. - * - * 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): - * Brett Wilson - * - * 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" -#include "nsIArray.idl" -#include "nsIURI.idl" - -interface nsIFile; -interface nsINavHistoryContainerResultNode; -interface nsINavHistoryQueryResultNode; -interface nsINavHistoryFolderResultNode; -interface nsINavHistoryQuery; -interface nsINavHistoryQueryOptions; -interface nsINavHistoryResult; -interface nsITreeColumn; -interface nsIWritablePropertyBag; - -[scriptable, uuid(acae2b2d-5fcd-4419-b1bc-b7dc92a1836c)] -interface nsINavHistoryResultNode : nsISupports -{ - /** - * Indentifies the parent result node in the result set. This is null for - * top level nodes. - */ - readonly attribute nsINavHistoryContainerResultNode parent; - - /** - * URI of the resource in question. For visits and URLs, this is the URL of - * the page. For folders and queries, this is the place: URI of the - * corresponding folder or query. This may be empty for other types of - * objects like host containers. - */ - readonly attribute AUTF8String uri; - - /** - * Identifies the type of this node. This node can then be QI-ed to the - * corresponding specialized result node interface. The remote_container one - * is a little weird, it is a temportary folder generated by the remote - * container API. Note that bookmark folders can also be filled by the - * remote container API (these are the entrypoints for the API) and may - * have a remote container type as well. - */ - const PRUint32 RESULT_TYPE_URI = 0; // nsINavHistoryResultNode - const PRUint32 RESULT_TYPE_VISIT = 1; // nsINavHistoryVisitResultNode - const PRUint32 RESULT_TYPE_FULL_VISIT = 2; // nsINavHistoryFullVisitResultNode - const PRUint32 RESULT_TYPE_HOST = 3; // nsINavHistoryContainerResultNode - const PRUint32 RESULT_TYPE_REMOTE_CONTAINER = 4; // nsINavHistoryContainerResultNode - const PRUint32 RESULT_TYPE_QUERY = 5; // nsINavHistoryQueryResultNode - const PRUint32 RESULT_TYPE_FOLDER = 6; // nsINavHistoryFolderResultNode - const PRUint32 RESULT_TYPE_SEPARATOR = 7; // nsINavHistoryResultNode - readonly attribute PRUint32 type; - - /** - * Title of the web page, or of the node's grouping (day, host, folder, etc) - */ - readonly attribute AUTF8String title; - - /** - * Total number of times the URI has ever been accessed. For hosts, this - * is the total of the children under it, NOT the total times the host has - * been accessed (this would require an additional query, so is not given - * by default when most of the time it is never needed). - */ - readonly attribute PRUint32 accessCount; - - /** - * This is the time the user accessed the page. - * - * If this is a visit, it is the exact time that the page visit occurred. - * - * If this is a URI, it is the most recent time that the URI was visited. - * Even if you ask for all URIs for a given date range long ago, this might - * contain today's date if the URI was visited today. - * - * For hosts, or other node types with children, this is the most recent - * access time for any of the children. - * - * For days, this is midnight on the morning of the day in question in - * UTC time. - */ - readonly attribute PRTime time; - - /** - * This URI can be used as an image source URI and will give you the favicon - * for the page. It is *not* the URI of the favicon, but rather something - * that will resolve to the actual image. - * - * In most cases, this is an annotation URI that will query the favicon - * service. If the entry has no favicon, this is the chrome URI of the - * default favicon. If the favicon originally lived in chrome, this will - * be the original chrome URI of the icon. - */ - readonly attribute nsIURI icon; - - /** - * This is the number of levels between this node and the top of the - * hierarchy. The members of result.children have indentLevel = 0, their - * children have indentLevel = 1, etc. - */ - readonly attribute PRUint32 indentLevel; - - /** - * Value with undefined meaning for use by the view. Its initial value will - * be -1. The tree view uses this value to indicate the row in the tree that - * this node is at. Other views may use this value however they choose. - */ - attribute PRInt32 viewIndex; - - /** - * You can use this to associate temporary information with the result node. - * This property bag is associated with the result node and is not persisted - * in any way. - */ - readonly attribute nsIWritablePropertyBag propertyBag; - - /** - * When this item is in a bookmark folder (parent is of type folder), this is - * the index into that folder of this node. These indices start at 0 and - * increase in the order that they appear in the bookmark folder. For items - * that are not in a bookmark folder, this value is -1. - */ - readonly attribute PRInt32 bookmarkIndex; -}; - - -/** - * When you request RESULT_TYPE_VISIT from query options, you will get this - * interface for each item, which includes the session ID so that we can - * group items from the same session together. - */ -[scriptable, uuid(8e2c5a86-b33d-4fa6-944b-559af7e95fcd)] -interface nsINavHistoryVisitResultNode : nsINavHistoryResultNode -{ - /** - * This indicates the session ID of the * visit. This is used for session - * grouping when a tree view is sorted by date. - */ - readonly attribute PRInt64 sessionId; -}; - - -/** - * This structure will be returned when you request RESULT_TYPE_FULL_VISIT in - * the query options. This includes uncommonly used information about each - * visit. - */ -[scriptable, uuid(c49fd9d5-56e2-43eb-932c-f933f28cba85)] -interface nsINavHistoryFullVisitResultNode : nsINavHistoryVisitResultNode -{ - /** - * This indicates the visit ID of the visit. - */ - readonly attribute PRInt64 visitId; - - /** - * This indicates the referring visit ID of the visit. The referrer should - * have the same sessionId. - */ - readonly attribute PRInt64 referringVisitId; - - /** - * Indicates the transition type of the visit. - * One of nsINavHistoryService.TRANSITION_* - */ - readonly attribute PRInt32 transitionType; -}; - - -/** - * Base class for container results. This includes all types of groupings. - * Bookmark folders and places queries will be QueryResultNodes which extends - * these items. - */ -[scriptable, uuid(957065fd-76e7-4c24-9284-c31b5decbaaa)] -interface nsINavHistoryContainerResultNode : nsINavHistoryResultNode -{ - - /** - * Set this to allow descent into the container. When closed, attempting - * to call getChildren or childCount will result in an error. You should - * set this to false when you are done reading. - * - * For HOST and DAY groupings, doing this is free since the children have - * been precomputed. For queries and bookmark folders, being open means they - * will keep themselves up-to-date by listening for updates and re-querying - * as needed. - */ - attribute boolean containerOpen; - - /** - * This indicates whether this node "may" have children, and can be used - * when the container is open or closed. When the container is closed, it - * will give you an exact answer if the node can easily be populated (for - * example, a bookmark folder). If not (for example, a complex history query), - * it will return true. When the container is open, it will always be - * accurate. It is intended to be used to see if we should draw the "+" next - * to a tree item. - */ - readonly attribute boolean hasChildren; - - /** - * This gives you the children of the nodes. It is preferrable to use this - * interface over the array one, since it avoids creating an nsIArray object - * and the interface is already the correct type. - */ - readonly attribute PRUint32 childCount; - nsINavHistoryResultNode getChild(in PRUint32 index); - - /** - * Returns false if this node's list of children can be modified - * (adding or removing children, or reordering children), or true if - * the UI should not allow the list of children to be modified. - * This is false for bookmark folder nodes unless setFolderReadOnly() has - * been called to override it, and true for non-folder nodes. If this - * container is a remote container this flag may be redefined by the - * remote container provider. - */ - readonly attribute boolean childrenReadOnly; - - // -------------------------------------------------------------------------- - // Remote container - - /** - * This is a string representing the remote container API service that is - * responsible for this container. It is empty if there is none. The - * container may be a RESULT_TYPE_REMOTE_CONTAINER node that has been - * dynamically generated by the remote container API. It may also be a - * bookmark folder (RESULT_TYPE_FOLDER) for which some service (such as - * livemarks) has registered to provide certain operations. - */ - readonly attribute AUTF8String remoteContainerType; - - /** - * Appends a full visit node to this container and returns it. For the remote - * container API. TO BE CALLED FROM nsIRemoteContainer::OnContainerOpening() - * ONLY, and only for non-bookmark-folder containers. - * - * @see nsINavHistoryURIResultNode for parameters. - * - * UNTESTED: Container API functions are commented out until we can test - */ - /*nsINavHistoryResultNode appendURINode( - in AUTF8String aURI, in AUTF8String aTitle, in PRUint32 aAccessCount, - in PRTime aTime, in AUTF8String aIconURI);*/ - - /** - * Appends a full visit node to this container and returns it. For the remote - * container API. TO BE CALLED FROM nsIRemoteContainer::OnContainerOpening() - * ONLY, and only for non-bookmark-folder containers. - * - * @see nsINavHistoryVisitResultNode for parameters. - * - * UNTESTED: Container API functions are commented out until we can test - */ - /*nsINavHistoryVisitResultNode appendVisitNode( - in AUTF8String aURI, in AUTF8String aTitle, in PRUint32 aAccessCount, - in PRTime aTime, in AUTF8String aIconURI, in PRInt64 aSession);*/ - - /** - * Appends a full visit node to this container and returns it. For the remote - * container API. TO BE CALLED FROM nsIRemoteContainer::OnContainerOpening() - * ONLY, and only for non-bookmark-folder containers. - * - * @see nsINavHistoryFullVisitResultNode for parameters. - * - * UNTESTED: Container API functions are commented out until we can test - */ - /*nsINavHistoryFullVisitResultNode appendFullVisitNode( - in AUTF8String aURI, in AUTF8String aTitle, in PRUint32 aAccessCount, - in PRTime aTime, in AUTF8String aIconURI, in PRInt64 aSession, - in PRInt64 aVisitId, in PRInt64 aReferringVisitId, - in PRInt32 aTransitionType);*/ - - /** - * Appends a container node to this container and returns it. For the remote - * container API. TO BE CALLED FROM nsIRemoteContainer::OnContainerOpening() - * ONLY, and only for non-bookmark-folder containers. - * - * aContainerType should be either RESULT_TYPE_HOST or - * RESULT_TYPE_REMOTE_CONTAINER. When type is remote container you must - * specify a remote container type, otherwise, the remote container type must - * be null. Use appendQueryNode and appendFolderNode for the other container - * types. - * - * UNTESTED: Container API functions are commented out until we can test - */ - /*nsINavHistoryContainerResultNode appendContainerNode( - in AUTF8String aTitle, in AUTF8String aIconURI, in PRUint32 aContainerType, - in AUTF8String aRemoteContainerType);*/ - - /** - * Appends a query node to this container and returns it. For the remote - * container API. TO BE CALLED FROM nsIRemoteContainer::OnContainerOpening() - * ONLY, and only for non-bookmark-folder containers. - * - * Normally you should supply an empty string for IconURI and it will take - * the default query icon for the current theme. - * - * UNTESTED: Container API functions are commented out until we can test - */ - /*nsINavHistoryQueryResultNode appendQueryNode( - in AUTF8String aQueryURI, in AUTF8String aTitle, in AUTF8String aIconURI);*/ - - /** - * Appends a bookmark folder node to this container and returns it. For the - * remote container API. TO BE CALLED FROM nsIRemoteContainer::OnContainerOpening() - * ONLY, and only for non-bookmark-folder containers. - * - * All container attributes will come from the boomkarks service for this - * folder. - * - * UNTESTED: Container API functions are commented out until we can test - */ - /*nsINavHistoryFolderResultNode appendFolderNode(in PRInt64 aFolderId);*/ - - /** - * Clears all children of this container. For the remote container API. - * TO BE CALLED FROM nsIRemoteContainer::OnContainerOpening and - * nsIRemoteContainer::OnContainerClosed ONLY, and valid only for - * non-bookmark-folder containers. - * - * UNTESTED: Container API functions are commented out until we can test - */ - /*void clearContents();*/ -}; - - -/** - * Used for places queries and as a base for bookmark folders. - * - * Note that if you request places to *not* be expanded in the options that - * generated this node, this item will report it has no children and never try - * to populate itself. - */ -[scriptable, uuid(dcd6a2b7-3d50-4c78-b1cb-2f0f18ac5864)] -interface nsINavHistoryQueryResultNode : nsINavHistoryContainerResultNode -{ - /** - * Get the queries which build this node's children. - * Only valid for RESULT_TYPE_QUERY nodes. - */ - void getQueries(out unsigned long queryCount, - [retval,array,size_is(queryCount)] out nsINavHistoryQuery queries); - - /** - * Get the options which group this node's children. - * Only valid for RESULT_TYPE_QUERY nodes. - */ - readonly attribute nsINavHistoryQueryOptions queryOptions; -}; - - -/** - * Represents a bookmark folder (type == RESULT_TYPE_FOLDER). It derives from a - * query result because it can provide a query that will result in the - * contents. - */ -[scriptable, uuid(f3160bfe-ebb3-453b-b25e-19d0b9c425b1)] -interface nsINavHistoryFolderResultNode : nsINavHistoryQueryResultNode -{ - /** - * ID of the folder corresponding to this node. - * Only valid for RESULT_TYPE_QUERY nodes where exactly one folder - * has been specified in the query. 0 in all other cases. - */ - readonly attribute PRInt64 folderId; -}; - - -/** - * Allows clients of the HistoryResult to define domain specific handling - * of specific nsITreeView methods that the HistoryResult does not implement. - * - * @see nsINavHistoryResult for where this fits in - */ -[scriptable, uuid(b34d5ca4-ce8e-49a6-9201-a59ae2128d2c)] -interface nsINavHistoryResultViewObserver : nsISupports -{ - const long DROP_BEFORE = -1; - const long DROP_ON = 0; - const long DROP_AFTER = 1; - /** - * Methods used by the drag feedback code to determine if a drag is allowable at - * the current location. To get the behavior where drops are only allowed on - * items, such as the mailNews folder pane, always return false whe - * the orientation is not DROP_ON. Implementors should not change the content of - * the observer list! - */ - boolean canDrop(in long index, in long orientation); - - /** - * Called when the user drops something on this view. The |orientation| param - * specifies before/on/after the given |row|. - */ - void onDrop(in long row, in long orientation); - - /** - * Called when an item is opened or closed. - */ - void onToggleOpenState (in long index); - - /** - * Called when a header is clicked. - */ - void onCycleHeader(in nsITreeColumn column); - - /** - * Called when a cell in a non-selectable cycling column (e.g. - * unread/flag/etc.) is clicked. - */ - void onCycleCell(in long row, in nsITreeColumn column); - - /** - * Called when selection in the tree changes - */ - void onSelectionChanged(); - - /** - * A command API that can be used to invoke commands on the selection. - * The tree will automatically invoke this method when certain keys - * are pressed. For example, when the DEL key is pressed, performAction - * will be called with the "delete" string. - */ - void onPerformAction(in wstring action); - - /** - * A command API that can be used to invoke commands on a specific row. - */ - void onPerformActionOnRow(in wstring action, in long row); - - /** - * A command API that can be used to invoke commands on a specific cell. - */ - void onPerformActionOnCell(in wstring action, in long row, in nsITreeColumn column); -}; - - -/** - * Allows clients to observe what is happening to a result as it updates itself - * according to history and bookmark system events. Register this observer on a - * result using registerView - * - * @see nsINavHistoryResult for where this fits in - */ -[scriptable, uuid(11032c23-a9fd-4918-bc8f-004726a748da)] -interface nsINavHistoryResultViewer : nsISupports -{ - /** - * Called when 'item' is inserted into 'parent' at index 'newIndex'. The item - * previously at index (if any) and everything below it will have been - * shifted down by one. The item may be a container or a leaf. - */ - void itemInserted(in nsINavHistoryContainerResultNode parent, - in nsINavHistoryResultNode item, - in PRUint32 newIndex); - - /** - * Called whan 'item' is removed from 'parent' at 'oldIndex'. The item may be - * a container or a leaf. This function will be called after the item has - * been removed from its parent list, but before anything else (including - * NULLing out the item's parent) has happened. - */ - void itemRemoved(in nsINavHistoryContainerResultNode parent, - in nsINavHistoryResultNode item, - in PRUint32 oldIndex); - - /** - * Called when an item has been changed and should be repainted. This only - * refers to the specific item. If it is a container, getting this message - * does not imply anything happened to the children. You'll get separate - * messages for those. Also, this may be called for container nodes at times - * when the result thinks it's possible that a twisty mey need to bw redrawn. - */ - void itemChanged(in nsINavHistoryResultNode item); - - /** - * Called when an item is being replaced with another item at the exact - * same position. - */ - void itemReplaced(in nsINavHistoryContainerResultNode parent, - in nsINavHistoryResultNode oldItem, - in nsINavHistoryResultNode newItem, - in PRUint32 index); - - /** - * Called after a container node went from closed to opened. - */ - void containerOpened(in nsINavHistoryContainerResultNode item); - - /** - * Called after a container node went from opened to closed. This will be - * called for the topmost container that is closing, and implies that any - * child containers have closed as well. - */ - void containerClosed(in nsINavHistoryContainerResultNode item); - - /** - * Called when something significant has happened within the container. The - * contents of the container should be re-built. - */ - void invalidateContainer(in nsINavHistoryContainerResultNode item); - - /** - * Called when something significant is changing that requires everything - * to be recomputed. For example, changing sorting or changing collapse - * duplicates can affect every row. - */ - void invalidateAll(); - - /** - * This is called to indicate to the UI that the sort has changed to the - * given mode. For trees, for example, this would update the column headers - * to reflect the sorting. For many other types of views, this won't be - * applicable. - * - * @param sortingMode One of nsINavHistoryQueryOptions.SORT_BY_* that - * indicates the new sorting mode. - * - * This only is expected to update the sorting UI. invalidateAll() will also - * get called if the sorting changes to update everything. - */ - void sortingChanged(in PRUint32 sortingMode); - - /** - * Called by the result when this object is set using - * nsINavHistoryResult.viewer. This will be set to NULL when the result - * is being deallocated. This should not be set by other code. - */ - attribute nsINavHistoryResult result; - - /** - * Adds the specified view observer to the view. In ownsWeak is false, then - * the result will hold a strong reference to the observer. If ownsWeak is - * true, then the observer must implement nsISupportsWeakReference, and the - * result will hold a weak reference to the observer. - */ - void addViewObserver(in nsINavHistoryResultViewObserver observer, - in boolean ownsWeak); - - /** - * Removes the specified view observer from this view. This observer must - * have been previously registered using addViewObserver. - */ - void removeViewObserver(in nsINavHistoryResultViewObserver observer); -}; - - -/** - * A predefined view adaptor for interfacing results with an nsITree. This - * object will remove itself from its associated result when the tree has been - * detached. This prevents circular references. Users should be aware of this, - * if you want to re-use the same viewer, you will need to keep your own - * reference to it and re-initialize it when the tree changes. If you use this - * object, attach it to a result, never attach it to a tree, and forget about - * it, it will leak! - */ -[scriptable, uuid(ecd6ad22-8eb0-4824-8a57-5edad9a08ae6)] -interface nsINavHistoryResultTreeViewer : nsINavHistoryResultViewer -{ - /** - * Controls whether duplicate adjacent elements are collapsed into a single - * item in the tree. This prevents you from seeing multiple entries for - * things when you have selected to get visits. When you sort by date, the - * multiple entries will then appear because they will be separated (unless - * you clicked reload a bunch of times in a row). If you know you'll only - * ever want one entry per site, you should ask for URIs back instead of - * visits so it will be more efficient. - * Default = true - * - * Changing this value is somewhat heavyweight since it will force a tree - * refresh. - */ - attribute boolean collapseDuplicates; - - /** - * This tells you how many items are in the flattened list of results, i.e. - * how many rows are in this tree right now. The tree adaptor will also - * QI to nsITreeView, and this will be the same as nsITreeView.rowCount. - * This is only valid when a tree is attached, the result will be 0 otherwise. - */ - readonly attribute PRUint32 flatItemCount; - - /** - * This allows you to get at the real node for a given row index. This is - * only valid when a tree is attached. - */ - nsINavHistoryResultNode nodeForTreeIndex(in PRUint32 aIndex); - - /** - * Reverse of nodeForFlatIndex, returns the row index for a given result node. - * Returns INDEX_INVISIBLE if the item is not visible (for example, its - * parent is collapsed). This is only valid when a tree is attached. The - * the result will always be INDEX_INVISIBLE if not. - * - * Note: This sounds sort of obvious, but it got me: aNode must be a node - * retrieved from the same result that this viewer is for. If you - * execute another query and get a node from a _different_ result, this - * function will always return the index of that node in the tree that - * is attached to that result. - */ - const PRUint32 INDEX_INVISIBLE = 0xffffffff; - PRUint32 treeIndexForNode(in nsINavHistoryResultNode aNode); -}; - - -/** - * The result of a history/bookmark query. - * - * Use the "root" element to access the children of this query. - * - * The basic design of the system is a model-view-controller. This result object - * represents the model where the data is stored. External components - * provide the view and controller which define how the data looks and how - * interaction happens. - * +-- nsINavHistoryResultViewObserver - * | - * [RESULT]----->[viewer]----->[controller] - * | - * +-- nsINavHistoryResultViewer - * - * The result indicates to the view when something changes through the - * nsINavHistoryResultViewer interface. Viewers register themselves through - * the nsINavHistoryResult.RegisterViewer. The viewer interface would then tell - * the actual view object what to do. A nsINavHistoryResultTreeViewer is - * provided which provides pre-packaged interaction with a nsITreeBoxObject. - * Other views will need to provide their own viewer interfaces. - * - * The viewer provides notifications to the controller when view events occur - * through the nsINavHistoryResultViewObserver interface. - */ - -[scriptable, uuid(25b45a94-3323-4c7b-910a-315f2c59bfb4)] -interface nsINavHistoryResult : nsISupports -{ - /** - * Sorts all nodes recursively by the given parameter, one of - * nsINavHistoryQueryOptions.SORT_BY_* This will update the corresponding - * options for this result, so that re-using the current options/queries will - * always give you the current view. - */ - attribute PRUint32 sortingMode; - - /** - * The viewer for this result (see comment for the class for how these - * objects are related). This may be null, in which case you can still - * manually walk the tree using the root node. When this is non-null, you - * can access the flattened list of items (flatItemCount, nodeForFlatIndex, - * flatIndexForNode). - */ - attribute nsINavHistoryResultViewer viewer; - - /** - * This is the root of the results. It will either be a - * nsINavHistoryFolderResultNode (if the query is for bookmarks matching a - * single folder) or just a nsINavHistoryQueryResultNode (for everything - * else). Remember that you need to open all containers for their contents - * to be valid. - */ - readonly attribute nsINavHistoryQueryResultNode root; -}; - - -/** - * Similar to nsIRDFObserver for history. Note that we don't pass the data - * source since that is always the global history. - * - * DANGER! If you are in the middle of a batch transaction, there may be a - * database transaction active. You can still access the DB, but be careful. - */ -[scriptable, uuid(849e2184-3dee-416f-91cd-6a619ca49d1c)] -interface nsINavHistoryObserver : nsISupports -{ - /** - * Notifies you that a bunch of things are about to change, don't do any - * heavy-duty processing until onEndUpdateBatch is called. - */ - void onBeginUpdateBatch(); - - /** - * Notifies you that we are done doing a bunch of things and you should go - * ahead and update UI, etc. - */ - void onEndUpdateBatch(); - - /** - * Called when a resource is visited. This is called the first time a - * resource (page, image, etc.) is seen as well as every subsequent time. - * - * Normally, transition types of TRANSITION_EMBED (corresponding to images in - * a page, for example) are not displayed in history results (unless - * includeHidden is set). Many observers can ignore _EMBED notifications - * (which will comprise the majority of visit notifications) to save work. - * - * @param aVisitID ID of the visit that was just created. - * @param aTime Time of the visit - * @param aSessionID The ID of one connected sequence of visits. - * @param aReferringID The ID of the visit the user came from. 0 if empty. - * @param aTransitionType One of nsINavHistory.TRANSITION_* - */ - void onVisit(in nsIURI aURI, in PRInt64 aVisitID, in PRTime aTime, - in PRInt64 aSessionID, in PRInt64 aReferringID, - in PRUint32 aTransitionType); - - /** - * Called whenever either the "real" title or the custom title of the page - * changed. BOTH TITLES ARE ALWAYS INCLUDED in this notification, even though - * only one will change at a time. Often, consumers will want to display the - * user title if it is available, and fall back to the page title (the one - * specified in the tag of the page). - * - * Note that there is a difference between an empty title and a NULL title. - * An empty string means that somebody specifically set the title to be - * nothing. NULL means nobody set it. From C++: use IsVoid() and SetIsVoid() - * to see whether an empty string is "null" or not (it will always be an - * empty string in either case). - * - * @param aUserTitleChanged Is true if the user title was the thing that was - * changed. If false, that means the "real" page - * title was changed instead. - */ - void onTitleChanged(in nsIURI aURI, in AString aPageTitle, - in AString aUserTitle, in PRBool aUserTitleChanged); - - /** - * This page and all of its visits are being deleted. Note: the page may not - * necessarily have actually existed for this function to be called. - * - * Delete notifications are only 99.99% accurate. Batch delete operations - * must be done in two steps, so first come notifications, then a bulk - * delete. If there is some error in the middle (for example, out of memory) - * then you'll get a notification and it won't get deleted. There's no easy - * way around this. - */ - void onDeleteURI(in nsIURI aURI); - - /** - * Notification that all of history is being deleted. - */ - void onClearHistory(); - - /** - * A page has had some attribute on it changed. Note that for TYPED and - * HIDDEN, the page may not necessarily have been added yet. - */ - const PRUint32 ATTRIBUTE_FAVICON = 3; // favicon updated, aString = favicon annotation URI - void onPageChanged(in nsIURI aURI, in PRUint32 aWhat, in AString aValue); - - /** - * Called when a history entry expires. You will recieve notifications that - * a specific visit has expired with the time of that visit. When the last - * visit for a history entry expires, the history entry itself is deleted - * and aWholeEntry is set. (If your observer only cares about URLs and not - * specific visits, it needs only to listen for aWholeEntry notifications). - * - * It is possible for a history entry to be deleted that has no visits if - * something is out of sync or after a bookmark is deleted that has no - * visits (thus freeing the history entry). In these cases, aVisitTime will - * be 0. - */ - void onPageExpired(in nsIURI aURI, in PRTime aVisitTime, in boolean aWholeEntry); -}; - - -/** - * This object encapsulates all the query parameters you're likely to need - * when building up history UI. All parameters are ANDed together. - * - * This is not intended to be a super-general query mechanism. This was designed - * so that most queries can be done in only one SQL query. This is important - * because, if the user has their profile on a networked drive, query latency - * can be non-negligible. - */ - -[scriptable, uuid(884819a6-1860-4a71-8fd7-89d962c1d984)] -interface nsINavHistoryQuery : nsISupports -{ - /** - * Time range for results (INCLUSIVE). The *TimeReference is one of the - * constants TIME_RELATIVE_* which indicates how to interpret the - * corresponding time value. - * TIME_RELATIVE_EPOCH (default): - * The time is relative to Jan 1 1970 GMT, (this is a normal PRTime) - * TIME_RELATIVE_TODAY: - * The time is relative to this morning at midnight. Normally used for - * queries relative to today. For example, a "past week" query would be - * today-6 days -> today+1 day - * TIME_RELATIVE_NOW: - * The time is relative to right now. - * - * Note: PRTime is in MICROseconds since 1 Jan 1970. Javascript date objects - * are expressed in MILLIseconds since 1 Jan 1970. - * - * As a special case, a 0 time relative to TIME_RELATIVE_EPOCH indicates that - * the time is not part of the query. This is the default, so an empty query - * will match any time. The has* functions return whether the corresponding - * time is considered. - * - * You can read absolute*Time to get the time value that the currently loaded - * reference points + offset resolve to. - */ - const PRUint32 TIME_RELATIVE_EPOCH = 0; - const PRUint32 TIME_RELATIVE_TODAY = 1; - const PRUint32 TIME_RELATIVE_NOW = 2; - - attribute PRTime beginTime; - attribute PRUint32 beginTimeReference; - readonly attribute boolean hasBeginTime; - readonly attribute PRTime absoluteBeginTime; - - attribute PRTime endTime; - attribute PRUint32 endTimeReference; - readonly attribute boolean hasEndTime; - readonly attribute PRTime absoluteEndTime; - - /** - * Text search terms. - */ - attribute AString searchTerms; - readonly attribute boolean hasSearchTerms; - - /** - * When set, returns only bookmarked items, when unset, returns anything. Setting this - * is equivalent to listing all bookmark folders in the 'folders' parameter. - */ - attribute boolean onlyBookmarked; - - /** - * This controls the meaning of 'domain', and whether it is an exact match - * 'domainIsHost' = true, or hierarchical (= false). - */ - attribute boolean domainIsHost; - - /** - * This is the host or domain name (controlled by domainIsHost). When - * domainIsHost, domain only does exact matching on host names. Otherwise, - * it will return anything whose host name ends in 'domain'. - * - * This one is a little different than most. Setting it to an empty string - * is a real query and will match any URI that has no host name (local files - * and such). Set this to NULL (in C++ use SetIsVoid) if you don't want - * domain matching. - */ - attribute AUTF8String domain; - readonly attribute boolean hasDomain; - - /** - * Controls the interpretation of 'uri'. When unset (default), the URI will - * request an exact match of the specified URI. When set, any history entry - * beginning in 'uri' will match. For example "http://bar.com/foo" will match - * "http://bar.com/foo" as well as "http://bar.com/foo/baz.gif". - */ - attribute boolean uriIsPrefix; - - /** - * This is a URI to match, to, for example, find out every time you visited - * a given URI. Use uriIsPrefix to control whether this is an exact match. - */ - attribute nsIURI uri; - readonly attribute boolean hasUri; - - /** - * Test for existance or non-existance of a given annotation. We don't - * currently support >1 annotation name per query. If 'annotationIsNot' is - * true, we test for the non-existance of the specified annotation. - * - * Testing for not annotation will do the same thing as a normal query and - * remove everything that doesn't have that annotation. Asking for things - * that DO have a given annotation is a little different. It also includes - * things that have never been visited. This allows place queries to be - * returned (which might include bookmark folders -- use the bookmark - * service's GetFolderURI) as well as anything else that may have been - * tagged with an annotation. This will only work for RESULTS_AS_URI since - * there will be no visits for these items. - */ - attribute boolean annotationIsNot; - attribute AUTF8String annotation; - readonly attribute boolean hasAnnotation; - - /** - * Limit results to items that are in all of the given folders. - */ - void getFolders(out PRUint32 count, - [retval,array,size_is(count)] out PRInt64 folders); - readonly attribute PRUint32 folderCount; - - void setFolders([const,array, size_is(folderCount)] in PRInt64 folders, - in PRUint32 folderCount); - - /** - * Creates a new query item with the same parameters of this one. - */ - nsINavHistoryQuery clone(); -}; - -/** - * This object represents the global options for executing a query. - */ -[scriptable, uuid(25fd4de4-33b0-475e-a63d-2bcb1d123e0d)] -interface nsINavHistoryQueryOptions : nsISupports -{ - /** - * 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; - - /** - * Group by bookmark folder. Since this determines the entire subtree - * hierarchy, it must be the last grouping option given. This option - * requires the query to have onlyBookmarked set, and for there to be - * at least one parent folder specified via nsINavHistoryQuery::setFolders. - * If all of the top-level results belong to a single folder, the folder will - * be omitted and its children will become the toplevel result nodes. - */ - const PRUint32 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. - * - * For bookmark items, SORT_BY_NONE means sort by the natural bookmark order. - */ - 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_URI_ASCENDING = 5; - const PRUint32 SORT_BY_URI_DESCENDING = 6; - const PRUint32 SORT_BY_VISITCOUNT_ASCENDING = 7; - const PRUint32 SORT_BY_VISITCOUNT_DESCENDING = 8; - - /** - * "URI" results, one for each URI visited in the range. Individual result - * nodes will be of type "URI". - */ - const PRUint32 RESULTS_AS_URI = 0; - - /** - * "Visit" results, with one for each time a page was visited (this will - * often give you multiple results for one URI). Individual result nodes will - * have type "Visit" - */ - const PRUint32 RESULTS_AS_VISIT = 1; - - /** - * This is identical to RESULT_TYPE_VISIT except that individual result nodes - * will have type "FullVisit". This is used for the attributes that are not - * commonly accessed to save space in the common case (the lists can be very - * long). - */ - const PRUint32 RESULTS_AS_FULL_VISIT = 2; - - /** - * 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. If you don't want grouping, you can - * specify an empty array. - */ - void getGroupingMode(out PRUint32 groupCount, - [retval,array,size_is(groupCount)] out PRUint32 groupingMode); - void setGroupingMode([const,array,size_is(groupCount)] in PRUint32 groupingMode, - in PRUint32 groupCount); - - /** - * The sorting mode to be used for this query. - * mode is one of SORT_BY_* - */ - attribute PRUint32 sortingMode; - - /** - * Sets the result type. One of RESULT_TYPE_* which includes how URIs are - * represented. - */ - attribute PRUint32 resultType; - - /** - * This option excludes all URIs and separators from a bookmarks query. - * This would be used if you just wanted a list of bookmark folders and - * queries (such as the left pane of the places page). - * Ignored for queries over history. Defaults to false. - */ - attribute boolean excludeItems; - - /** - * Set to true to exclude queries ("place:" URIs) from the query results. - * Simple folder queries (bookmark folder symlinks) will still be included. - * Defaults to false. - */ - attribute boolean excludeQueries; - - /** - * Set to true to exclude read-only folders from the query results. This is - * designed for cases where you want to give the user the option of filing - * something into a list of folders. It only affects cases where the actual - * folder result node would appear in its parent folder and filters it out. - * It doesn't affect the query at all, and doesn't affect more complex - * queries (such as "folders with annotation X"). - */ - attribute boolean excludeReadOnlyFolders; - - /** - * 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. Doesn't do anything if - * excludeQueries is set. Defaults to false. - * - * Note that this has no effect on folder links, which are place: URIs - * returned by nsINavBookmarkService.GetFolderURI. These are always expanded - * and will appear as bookmark folders. - */ - attribute boolean expandQueries; - - /** - * Normally the title of a result will be the user's custom title if there is - * one, falling back on the default page title. If this is set, we will not - * do this operation and always use the original page title extracted from - * the HTML of the page. Defaults to false. - */ - attribute boolean forceOriginalTitle; - - /** - * Most items in history are marked "hidden." Only toplevel pages that the - * user sees in the URL bar are not hidden. Hidden things include the content - * of iframes and all images on web pages. Normally, you don't want these - * things. If you do, set this flag and you'll get all items, even hidden - * ones. Does nothing for bookmark queries. Defaults to false. - */ - attribute boolean includeHidden; - - /** - * This is the maximum number of results that you want. The query is exeucted, - * the results are sorted, and then the top 'maxResults' results are taken - * and returned. Set to 0 (the default) to get all results. - * - * THIS DOES NOT WORK IN CONJUNCTION WITH SORTING BY TITLE. This is because - * sorting by title requires us to sort after using locale-sensetive sorting - * (as opposed to letting the database do it for us). - * - * Instead, we get the result ordered by date, pick the maxResult most recent - * ones, and THEN sort by title. - */ - attribute PRUint32 maxResults; - - /** - * Creates a new options item with the same parameters of this one. - */ - nsINavHistoryQueryOptions clone(); -}; - -[scriptable, uuid(C51F54CB-5E89-4B20-A37C-1343888935B7)] -interface nsINavHistoryService : nsISupports -{ - /** - * This transition type means the user followed a link and got a new toplevel - * window. - */ - const PRUint32 TRANSITION_LINK = 1; - - /** - * This transition type means that the user typed the page's URL in the - * URL bar. - */ - const PRUint32 TRANSITION_TYPED = 2; - - /** - * This transition is set when the user followed a bookmark to get to the - * page. - */ - const PRUint32 TRANSITION_BOOKMARK = 3; - - /** - * This transition type is set when some inner content is loaded. This is - * true of all images on a page, and the contents of the iframe. It is also - * true of any content in a frame, regardless if whether or not the user - * clicked something to get there. - */ - const PRUint32 TRANSITION_EMBED = 4; - - /** - * Set when the transition was a permanent redirect. - */ - const PRUint32 TRANSITION_REDIRECT_PERMANENT = 5; - - /** - * Set when the transition was a temporary redirect. - */ - const PRUint32 TRANSITION_REDIRECT_TEMPORARY = 6; - - /** - * True if there is any history. This can be used in UI to determine whether - * the "clear history" button should be enabled or not. This is much better - * than using BrowserHistory.count since that can be very slow if there is - * a lot of history (it must enumerate each item). This is pretty fast. - */ - readonly attribute boolean hasHistoryEntries; - - /** - * This sets the user-defined title of the page. Silently fails if we have - * no knowledge of this page. Set to empty string to clear the user title and - * only use the "real" title as declared on the page. - */ - void setPageUserTitle(in nsIURI aURI, in AString aUserTitle); - - /** - * This is just like markPageAsTyped (in nsIBrowserHistory, also implemented - * by the history service), but for bookmarks. It declares that the given URI - * is being opened as a result of following a bookmark. If this URI is loaded - * soon after this message has been received, that transition will be marked - * as following a bookmark. - */ - void markPageAsFollowedBookmark(in nsIURI aURI); - - /** - * Returns true if this URI would be added to the history. You don't have to - * worry about calling this, addPageToSession/addURI will always check before - * actually adding the page. This function is public because some components - * may want to check if this page would go in the history (i.e. for - * annotations). - */ - boolean canAddURI(in nsIURI aURI); - - /** - * Call to set the full information for a given page. If the page does not - * exist, it will be added to the database. If it does, the existing values - * WILL BE OVERWRITTEN. This is an updated version of addPageWithDetails - * for backup/restore type operations. - * - * @param aURI Page to add/change. - * @param aTitle Title as specified by the page. - * @param aUserTitle Custom title provided by user. Set to NULL if none. - * @param aVisitCount Number of times this page has been visited. Setting this - * to 0 may make the page invisible in some views. - * @param aHidden Whether the page is hidden. If the page has only - * TRANSITION_EMBED visits, this will be true, otherwise - * false. - * @param aTyped True if this URI has ever been typed. - */ - void setPageDetails(in nsIURI aURI, in AString aTitle, in AString aUserTitle, - in PRUint32 aVisitCount, in boolean aHidden, - in boolean aTyped); - - /** - * Call to manually add a visit for a specific page. This will probably not - * be commonly used other than for backup/restore type operations. If the URI - * does not have an entry in the history database already, one will be created - * with no visits, no title, hidden, not typed. Adding a visit will - * automatically increment the visit count for the visited page and will unhide - * it and/or mark it typed according to the transition type. - * - * @param aURI Visited page - * @param aTime Time page was visited (microseconds) - * @param aReferringVisit The ID of the visit that generated this one. Use 0 - * for no referrer. This must be a valid visit already - * in the DB or 0. - * @param aTranstitionType Type of transition: one of TRANSITION_* above - * @param aIsRedirect True if the given visit redirects to somewhere else. - * (ie you will create an visit out of here that is a - * redirect transition). This causes this page to be - * hidden in normal history views (unless it has been - * unhidden by visiting it with a non-redirect). - * @param aSessionID The session ID that this page belongs to. Use 0 for - * no session. - * @returns The ID of the created visit. This will be 0 if the URI is not - * valid for adding to history (canAddURI = false). - */ - PRInt64 addVisit(in nsIURI aURI, in PRTime aTime, in PRInt64 aReferringVisit, - in PRInt32 aTransitionType, in boolean aIsRedirect, - in PRInt64 aSessionID); - - /** - * 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. - */ - nsINavHistoryResult executeQuery(in nsINavHistoryQuery aQuery, - in nsINavHistoryQueryOptions options); - - /** - * Executes an array of queries. All of the query objects are ORed - * together. Within a query, all the terms are ANDed together as in - * executeQuery. See executeQuery() - */ - nsINavHistoryResult executeQueries( - [array,size_is(aQueryCount)] in nsINavHistoryQuery aQueries, in PRUint32 aQueryCount, - in nsINavHistoryQueryOptions options); - - /** - * Converts a query URI-like string to an array of actual query objects for - * use to executeQueries(). The output query array may be empty if there is - * no information. However, there will always be an options structure returned - * (if nothing is defined, it will just have the default values). - */ - void queryStringToQueries(in AUTF8String aQueryString, - [array, size_is(aResultCount)] out nsINavHistoryQuery aQueries, out PRUint32 aResultCount, - out nsINavHistoryQueryOptions options); - - /** - * Converts a query into an equivalent string that can be persisted. Inverse - * of queryStringToQueries() - */ - AUTF8String queriesToQueryString( - [array, size_is(aQueryCount)] in nsINavHistoryQuery aQueries, in PRUint32 aQueryCount, - in nsINavHistoryQueryOptions options); - - /** - * Adds a history observer. If ownsWeak is false, the history service will - * keep an owning reference to the observer. If ownsWeak is true, then - * aObserver must implement nsISupportsWeakReference, and the history service - * will keep a weak reference to the observer. - */ - void addObserver(in nsINavHistoryObserver observer, in boolean ownsWeak); - - /** - * Removes a history observer. - */ - void removeObserver(in nsINavHistoryObserver observer); - - /** - * 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(); -}; - -/** - * nsIMorkHistoryImporter is a service which handles importing old - * history files. - */ -[scriptable, uuid(be935aed-6929-4e44-be29-17ec89e5c8bd)] -interface nsIMorkHistoryImporter : nsISupports -{ - /** - * Import the given Mork history file. - * @param file The Mork history file to import - * @param history A reference to the nsINavHistoryService. This is - * supplied since the importer is invoked during - * history service initialization. - */ - void importHistory(in nsIFile file, in nsINavHistoryService history); -}; diff --git a/mozilla/browser/components/places/public/nsIRemoteContainer.idl b/mozilla/browser/components/places/public/nsIRemoteContainer.idl deleted file mode 100644 index 67c2b6270c8..00000000000 --- a/mozilla/browser/components/places/public/nsIRemoteContainer.idl +++ /dev/null @@ -1,147 +0,0 @@ -/* -*- 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): - * Annie Sullivan <annie.sullivan@gmail.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 nsINavHistoryContainerResultNode; -interface nsINavHistoryQueryOptions; - -/** - * The Remote Container interface provides a base class for services that want - * to provide containers for bookmarks. Some examples of possible services are - * the livemarks service and the filesystem. - * - * There are two primary modes of operation: container services might create - * actual bookmarks, or they might fill containers on the fly as needed. The - * livemarks service, for example, queries the feed from time to time and - * creates actual bookmarks in the folder corresponding to the feed. This way - * the items are persistent even if the user is offline, and can be searched. - * In this mode, the service just looks for moves and deletes to update the - * corresponding bookkeeping information. It can use the normal population - * method provided by the bookmark service and need not do any work for the - * onContainerOpen message. - * - * Such a bookmark-based container service might listen for onContainerOpening - * notifications messages to see whether - - * - * Persistent bookmarks are not appropriate for more short-lived data, such as - * the filesystem interface. In this case, the service can fill result nodes - * directly into the container when it is being opened. It can use the property - * bag on every result node to store data associated with each item, such as - * full path on disk. It would create additional containers for each folder, - * resgistered to its service. These dynamic containers are not bookmark - * folders in contrast to the initial item. - */ - -[scriptable, uuid(45bf2020-9683-498c-9638-f08130c4151d)] -interface nsIRemoteContainer : nsISupports -{ - - /** - * Called when the given container is about to be populated so that the - * service can populate the container if necessary. - * - * @param container The container node for the container being opened. - * If the node type is a bookmarks container, you can - * QI it to nsINavHistoryFolderResultNode and access the - * folder ID, etc. Note that all result nodes implement - * a property bag if you need to store state. - * @param options The options used to generate this query. Containers - * should follow these when possible, for example, - * whether to expand queries, etc. Implementations should - * use this when possible if adding query and folder nodes - * to the container. DO NOT MODIFY THIS VALUE. - * - * UNTESTED container API functions are commented out until they can be - * adequately tested. - */ - /*void onContainerOpening(in nsINavHistoryContainerResultNode container, - in nsINavHistoryQueryOptions options);*/ - - /** - * Called when the given container has just been hidden so that the service - * can do any necessary cleanup. This is NOT guaranteed to get called. In - * particular, if the query just goes away (like the user switched views on - * the places page) you will not get this call. This only happens when the - * container itself goes from the open state to the closed state. A serviced - * with large numbers of dynamically populated items might use this to do - * some cleanup so those items don't hang around - * - * @param container The container node of the container being closed. The - * service need not worry about removing any created nodes, - * they will be automatically removed when this call - * completes. - * - * UNTESTED container API functions are commented out until they can be - * adequately tested. - */ - /*void onContainerClosed(in nsINavHistoryContainerResultNode container);*/ - - /** - * Called when the given container is about to be deleted, so - * that the service can do any necessary cleanup. - * Called BEFORE the container is deleted, so that the service - * can still reference it. - * @param container The folderId of the bookmark folder - * representing the container to be deleted. - */ - void onContainerRemoving(in PRInt64 container); - - /** - * Called when the given container has just been moved, in case - * the service needs to do any bookkeeping. - * Called AFTER the container has been moved, so the service can - * get the new URI. - * @param container The folderId of the bookmark folder - * representing the container to be moved. - * @param newFolder The folderId of the new parent folder - * for the container. - * @param newIndex The index the container will be inserted at, - * or -1 for append. - */ - void onContainerMoved(in PRInt64 container, - in PRInt64 newFolder, in PRInt32 newIndex); - - /** - * Returns true if containers of this type should not expose UI for - * inserting, moving, or deleting children. - */ - readonly attribute boolean childrenReadOnly; -}; diff --git a/mozilla/browser/components/places/src/Makefile.in b/mozilla/browser/components/places/src/Makefile.in deleted file mode 100644 index 812ff512635..00000000000 --- a/mozilla/browser/components/places/src/Makefile.in +++ /dev/null @@ -1,107 +0,0 @@ -# -# ***** 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 code -# -# 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): -# Brett Wilson <brettw@gmail.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 ***** - -DEPTH = ../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -MODULE = places -LIBRARY_NAME = places -LIBXUL_LIBRARY = 1 -EXPORT_LIBRARY = 1 -MODULE_NAME = nsPlacesModule -IS_COMPONENT = 1 - -REQUIRES = xpcom \ - string \ - history \ - docshell \ - rdf \ - dom \ - morkreader \ - pref \ - necko \ - intl \ - layout \ - locale \ - unicharutil \ - autocomplete \ - storage \ - uconv \ - htmlparser \ - content \ - txmgr \ - caps \ - xpconnect \ - js \ - nkcache \ - widget \ - url-classifier \ - microsummaries \ - $(NULL) - -CPPSRCS = \ - nsAnnoProtocolHandler.cpp \ - nsAnnotationService.cpp \ - nsBookmarksHTML.cpp \ - nsFaviconService.cpp \ - nsNavHistory.cpp \ - nsNavHistoryAutoComplete.cpp \ - nsNavHistoryExpire.cpp \ - nsNavHistoryQuery.cpp \ - nsNavHistoryResult.cpp \ - nsNavBookmarks.cpp \ - nsLivemarkService.cpp \ - nsBookmarksFeedHandler.cpp \ - nsMaybeWeakPtr.cpp \ - nsMorkHistoryImporter.cpp \ - nsPlacesModule.cpp \ - $(NULL) - -include $(topsrcdir)/config/rules.mk - -EXTRA_DSO_LDOPTS = \ - $(DEPTH)/db/morkreader/$(LIB_PREFIX)morkreader_s.$(LIB_SUFFIX) \ - $(MOZ_UNICHARUTIL_LIBS) \ - $(MOZ_COMPONENT_LIBS) \ - $(NULL) - -LOCAL_INCLUDES += -I$(srcdir)/../../build diff --git a/mozilla/browser/components/places/src/nsAnnoProtocolHandler.cpp b/mozilla/browser/components/places/src/nsAnnoProtocolHandler.cpp deleted file mode 100644 index eac355303f2..00000000000 --- a/mozilla/browser/components/places/src/nsAnnoProtocolHandler.cpp +++ /dev/null @@ -1,245 +0,0 @@ -//* -*- 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 Mozilla Annotation Service - * - * 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): - * Brett Wilson <brettw@gmail.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 ***** */ - -/** - * Implementation of moz-anno: URLs for accessing annotation values. This just - * reads binary data from the annotation service. - * - * There is a special case for favicons. Annotation URLs with the name "favicon" - * will be sent to the favicon service. If the favicon service doesn't have the - * data, a stream containing the default favicon will be returned. - */ - -#include "nsAnnoProtocolHandler.h" -#include "nsFaviconService.h" -#include "nsIChannel.h" -#include "nsIInputStreamChannel.h" -#include "nsILoadGroup.h" -#include "nsIStandardURL.h" -#include "nsIStringStream.h" -#include "nsISupportsUtils.h" -#include "nsIURI.h" -#include "nsNetUtil.h" -#include "nsServiceManagerUtils.h" -#include "nsStringStream.h" - -NS_IMPL_ISUPPORTS1(nsAnnoProtocolHandler, nsIProtocolHandler) - -// nsAnnoProtocolHandler::GetScheme - -NS_IMETHODIMP -nsAnnoProtocolHandler::GetScheme(nsACString& aScheme) -{ - aScheme.AssignLiteral("moz-anno"); - return NS_OK; -} - - -// nsAnnoProtocolHandler::GetDefaultPort -// -// There is no default port for annotation URLs - -NS_IMETHODIMP -nsAnnoProtocolHandler::GetDefaultPort(PRInt32 *aDefaultPort) -{ - *aDefaultPort = -1; - return NS_OK; -} - - -// nsAnnoProtocolHandler::GetProtocolFlags -// -// No special protocol flags. - -NS_IMETHODIMP -nsAnnoProtocolHandler::GetProtocolFlags(PRUint32 *aProtocolFlags) -{ - *aProtocolFlags = (URI_NORELATIVE | URI_NOAUTH); - return NS_OK; -} - - -// nsAnnoProtocolHandler::NewURI - -NS_IMETHODIMP -nsAnnoProtocolHandler::NewURI(const nsACString& aSpec, - const char *aOriginCharset, - nsIURI *aBaseURI, nsIURI **_retval) -{ - nsCOMPtr <nsIURI> uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID); - if (!uri) - return NS_ERROR_OUT_OF_MEMORY; - nsresult rv = uri->SetSpec(aSpec); - NS_ENSURE_SUCCESS(rv, rv); - - *_retval = nsnull; - uri.swap(*_retval); - return NS_OK; -} - - -// nsAnnoProtocolHandler::NewChannel -// - -NS_IMETHODIMP -nsAnnoProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval) -{ - NS_ENSURE_ARG_POINTER(aURI); - nsresult rv; - - nsCAutoString path; - rv = aURI->GetPath(path); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<nsIAnnotationService> annotationService = do_GetService( - "@mozilla.org/browser/annotation-service;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - // annotation info - nsCOMPtr<nsIURI> annoURI; - nsCAutoString annoName; - rv = ParseAnnoURI(aURI, getter_AddRefs(annoURI), annoName); - NS_ENSURE_SUCCESS(rv, rv); - - // get the data from the annotation service and hand it off to the stream - PRUint8* data; - PRUint32 dataLen; - nsCAutoString mimeType; - - if (annoName.EqualsLiteral(FAVICON_ANNOTATION_NAME)) { - // special handling for favicons: ask favicon service - nsFaviconService* faviconService = nsFaviconService::GetFaviconService(); - if (! faviconService) { - NS_WARNING("Favicon service is unavailable."); - return GetDefaultIcon(_retval); - } - rv = faviconService->GetFaviconData(annoURI, mimeType, &dataLen, &data); - if (NS_FAILED(rv)) - return GetDefaultIcon(_retval); - - // don't allow icons without MIME types - if (mimeType.IsEmpty()) { - NS_Free(data); - return GetDefaultIcon(_retval); - } - } else { - // normal handling for annotations - rv = annotationService->GetAnnotationBinary(annoURI, annoName, &data, - &dataLen, mimeType); - NS_ENSURE_SUCCESS(rv, rv); - - // disallow annotations with no MIME types - if (mimeType.IsEmpty()) { - NS_Free(data); - return NS_ERROR_NOT_AVAILABLE; - } - } - - nsCOMPtr<nsIStringInputStream> stream = do_CreateInstance( - NS_STRINGINPUTSTREAM_CONTRACTID, &rv); - if (NS_FAILED(rv)) { - NS_Free(data); - return rv; - } - rv = stream->AdoptData((char*)data, dataLen); - if (NS_FAILED(rv)) { - NS_Free(data); - return rv; - } - - nsCOMPtr<nsIChannel> channel; - rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, stream, mimeType); - NS_ENSURE_SUCCESS(rv, rv); - - *_retval = channel; - NS_ADDREF(*_retval); - return NS_OK; -} - - -// nsAnnoProtocolHandler::AllowPort -// -// Don't override any bans on bad ports. - -NS_IMETHODIMP -nsAnnoProtocolHandler::AllowPort(PRInt32 port, const char *scheme, - PRBool *_retval) -{ - *_retval = PR_FALSE; - return NS_OK; -} - - -// nsAnnoProtocolHandler::ParseAnnoURI -// -// Splits an annotation URL into its URI and name parts - -nsresult -nsAnnoProtocolHandler::ParseAnnoURI(nsIURI* aURI, - nsIURI** aResultURI, nsCString& aName) -{ - nsresult rv; - nsCAutoString path; - rv = aURI->GetPath(path); - NS_ENSURE_SUCCESS(rv, rv); - - PRInt32 firstColon = path.FindChar(':'); - if (firstColon <= 0) - return NS_ERROR_MALFORMED_URI; - - rv = NS_NewURI(aResultURI, Substring(path, firstColon + 1)); - NS_ENSURE_SUCCESS(rv, rv); - - aName = Substring(path, 0, firstColon); - return NS_OK; -} - - -// nsAnnoProtocolHandler::GetDefaultIcon -// -// This creates a channel for the default web page favicon. - -nsresult -nsAnnoProtocolHandler::GetDefaultIcon(nsIChannel** aChannel) -{ - nsresult rv; - nsCOMPtr<nsIURI> uri; - rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL)); - NS_ENSURE_SUCCESS(rv, rv); - return NS_NewChannel(aChannel, uri); -} diff --git a/mozilla/browser/components/places/src/nsAnnoProtocolHandler.h b/mozilla/browser/components/places/src/nsAnnoProtocolHandler.h deleted file mode 100644 index 9630574a1a4..00000000000 --- a/mozilla/browser/components/places/src/nsAnnoProtocolHandler.h +++ /dev/null @@ -1,69 +0,0 @@ -//* -*- 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 Mozilla Annotation Service - * - * 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): - * Brett Wilson <brettw@gmail.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 ***** */ - -#ifndef nsAnnoProtocolHandler_h___ -#define nsAnnoProtocolHandler_h___ - -#include "nsCOMPtr.h" -#include "nsIAnnotationService.h" -#include "nsIProtocolHandler.h" -#include "nsIURI.h" -#include "nsString.h" -#include "nsWeakReference.h" - -// {e8b8bdb7-c96c-4d82-9c6f-2b3c585ec7ea} -#define NS_ANNOPROTOCOLHANDLER_CID \ -{ 0xe8b8bdb7, 0xc96c, 0x4d82, { 0x9c, 0x6f, 0x2b, 0x3c, 0x58, 0x5e, 0xc7, 0xea } } - -class nsAnnoProtocolHandler : public nsIProtocolHandler, public nsSupportsWeakReference -{ -public: - nsAnnoProtocolHandler() {} - - NS_DECL_ISUPPORTS - NS_DECL_NSIPROTOCOLHANDLER - -private: - ~nsAnnoProtocolHandler() {} - -protected: - nsresult ParseAnnoURI(nsIURI* aURI, nsIURI** aResultURI, nsCString& aName); - nsresult GetDefaultIcon(nsIChannel** aChannel); -}; - -#endif /* nsAnnoProtocolHandler_h___ */ diff --git a/mozilla/browser/components/places/src/nsAnnotationService.cpp b/mozilla/browser/components/places/src/nsAnnotationService.cpp deleted file mode 100644 index e7f6950f1ef..00000000000 --- a/mozilla/browser/components/places/src/nsAnnotationService.cpp +++ /dev/null @@ -1,1145 +0,0 @@ -//* -*- 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 Mozilla Annotation Service - * - * 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): - * Brett Wilson <brettw@gmail.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 "nsAnnotationService.h" -#include "mozStorageCID.h" -#include "nsNavHistory.h" -#include "nsNetUtil.h" -#include "mozIStorageValueArray.h" -#include "mozIStorageStatement.h" -#include "mozIStorageFunction.h" -#include "mozStorageHelper.h" -#include "nsIServiceManager.h" -#include "nsIVariant.h" -#include "nsString.h" -#include "nsVariant.h" - -const PRInt32 nsAnnotationService::kAnnoIndex_ID = 0; -const PRInt32 nsAnnotationService::kAnnoIndex_Page = 1; -const PRInt32 nsAnnotationService::kAnnoIndex_Name = 2; -const PRInt32 nsAnnotationService::kAnnoIndex_MimeType = 3; -const PRInt32 nsAnnotationService::kAnnoIndex_Content = 4; -const PRInt32 nsAnnotationService::kAnnoIndex_Flags = 5; -const PRInt32 nsAnnotationService::kAnnoIndex_Expiration = 6; - -nsAnnotationService* nsAnnotationService::gAnnotationService; - -NS_IMPL_ISUPPORTS1(nsAnnotationService, - nsIAnnotationService) - -// nsAnnotationService::nsAnnotationService - -nsAnnotationService::nsAnnotationService() -{ - NS_ASSERTION(!gAnnotationService, - "ATTEMPTING TO CREATE TWO INSTANCES OF THE ANNOTATION SERVICE!"); - gAnnotationService = this; -} - - -// nsAnnotationService::~nsAnnotationService - -nsAnnotationService::~nsAnnotationService() -{ - NS_ASSERTION(gAnnotationService == this, - "Deleting a non-singleton annotation service"); - if (gAnnotationService == this) - gAnnotationService = nsnull; -} - - -// nsAnnotationService::Init - -nsresult -nsAnnotationService::Init() -{ - nsresult rv; - - // The history service will normally already be created and will call our - // static InitTables function. It will get autocreated here if it hasn't - // already been created. - nsNavHistory* history = nsNavHistory::GetHistoryService(); - if (! history) - return NS_ERROR_FAILURE; - mDBConn = history->GetStorageConnection(); - - // annotation statements - - // mDBSetAnnotation - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "UPDATE moz_anno " - "SET mime_type = ?4, content = ?5, flags = ?6, expiration = ?7 " - "WHERE anno_id = ?1"), - getter_AddRefs(mDBSetAnnotation)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBGetAnnotation - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT * " - "FROM moz_anno " - "WHERE page = ?1 AND name_id = " - "(SELECT name_id FROM moz_anno_name WHERE name = ?2)"), - getter_AddRefs(mDBGetAnnotation)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBGetAnnotationNames - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT n.name " - "FROM moz_anno a LEFT JOIN moz_anno_name n ON a.name_id = n.name_id " - "WHERE a.page = ?1"), - getter_AddRefs(mDBGetAnnotationNames)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBGetAnnotationFromURI - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT a.anno_id, a.page, ?2, a.mime_type, a.content, a.flags, a.expiration " - "FROM moz_history h JOIN moz_anno a ON h.id = a.page " - "WHERE h.url = ?1 AND a.name_id = " - "(SELECT name_id FROM moz_anno_name WHERE name = ?2)"), - getter_AddRefs(mDBGetAnnotationFromURI)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBGetAnnotationNameID - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT name_id FROM moz_anno_name WHERE name = ?1"), - getter_AddRefs(mDBGetAnnotationNameID)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBAddAnnotationName - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "INSERT INTO moz_anno_name (name) VALUES (?1)"), - getter_AddRefs(mDBAddAnnotationName)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBAddAnnotation - // Note: kAnnoIndex_Name here is a name ID and not a string like the getters - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "INSERT INTO moz_anno " - "(page, name_id, mime_type, content, flags, expiration) " - "VALUES (?2, ?3, ?4, ?5, ?6, ?7)"), - getter_AddRefs(mDBAddAnnotation)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBRemoveAnnotation - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "DELETE FROM moz_anno WHERE page = ?1 AND name_id = " - "(SELECT name_id FROM moz_anno_name WHERE name = ?2)"), - getter_AddRefs(mDBRemoveAnnotation)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - - -// nsAnnotationService::InitTables -// -// All commands that initialize the schema of the DB go in here. This is -// called from history init before the dummy DB connection is started that -// will prevent us from modifying the schema. -// -// The history service will always be created before us (we get it at the -// beginning of the init function which covers us if it's not). - -nsresult // static -nsAnnotationService::InitTables(mozIStorageConnection* aDBConn) -{ - nsresult rv; - PRBool exists; - PRBool migrateFromAlpha1 = PR_FALSE; - rv = aDBConn->TableExists(NS_LITERAL_CSTRING("moz_anno"), &exists); - NS_ENSURE_SUCCESS(rv, rv); - if (! exists) { - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_anno (" - "anno_id INTEGER PRIMARY KEY," - "page INTEGER NOT NULL," - "name_id INTEGER," - "mime_type VARCHAR(32) DEFAULT NULL," - "content LONGVARCHAR, flags INTEGER DEFAULT 0," - "expiration INTEGER DEFAULT 0)")); - NS_ENSURE_SUCCESS(rv, rv); - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX moz_anno_pageindex ON moz_anno (page)")); - NS_ENSURE_SUCCESS(rv, rv); - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX moz_anno_nameindex ON moz_anno (name_id)")); - NS_ENSURE_SUCCESS(rv, rv); - } else { - rv = aDBConn->TableExists(NS_LITERAL_CSTRING("moz_anno_name"), &exists); - NS_ENSURE_SUCCESS(rv, rv); - if (! exists) - migrateFromAlpha1 = PR_TRUE; - } - - rv = aDBConn->TableExists(NS_LITERAL_CSTRING("moz_anno_name"), &exists); - NS_ENSURE_SUCCESS(rv, rv); - if (! exists) { - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE moz_anno_name (" - "name_id INTEGER PRIMARY KEY," - "name VARCHAR(32) UNIQUE NOT NULL)")); - NS_ENSURE_SUCCESS(rv, rv); - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX moz_anno_name_nameindex ON moz_anno_name (name)")); - NS_ENSURE_SUCCESS(rv, rv); - } - - // this needs to happen after moz_anno_name gets created - if (migrateFromAlpha1) { - rv = MigrateFromAlpha1(aDBConn); - NS_ENSURE_SUCCESS(rv, rv); - } - return NS_OK; -} - - -// nsAnnotationService::SetAnnotationString - -NS_IMETHODIMP -nsAnnotationService::SetAnnotationString(nsIURI* aURI, - const nsACString& aName, - const nsAString& aValue, - PRInt32 aFlags, PRInt32 aExpiration) -{ - mozStorageTransaction transaction(mDBConn, PR_FALSE); - mozIStorageStatement* statement; // class var, not owned by this function - nsresult rv = StartSetAnnotation(aURI, aName, aFlags, aExpiration, &statement); - NS_ENSURE_SUCCESS(rv, rv); - mozStorageStatementScoper statementResetter(statement); - - rv = statement->BindStringParameter(kAnnoIndex_Content, aValue); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindNullParameter(kAnnoIndex_MimeType); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - transaction.Commit(); - - // should reset the statement; observers may call our service back to get - // annotation values! - statement->Reset(); - statementResetter.Abandon(); - CallSetObservers(aURI, aName); - return NS_OK; -} - - -// nsAnnotationService::SetAnnotationInt32 - -NS_IMETHODIMP -nsAnnotationService::SetAnnotationInt32(nsIURI* aURI, - const nsACString& aName, - PRInt32 aValue, - PRInt32 aFlags, PRInt32 aExpiration) -{ - mozStorageTransaction transaction(mDBConn, PR_FALSE); - mozIStorageStatement* statement; // class var, not owned by this function - nsresult rv = StartSetAnnotation(aURI, aName, aFlags, aExpiration, &statement); - NS_ENSURE_SUCCESS(rv, rv); - mozStorageStatementScoper statementResetter(statement); - - rv = statement->BindInt32Parameter(kAnnoIndex_Content, aValue); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindNullParameter(kAnnoIndex_MimeType); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - transaction.Commit(); - - // should reset the statement; observers may call our service back to get - // annotation values! - statement->Reset(); - statementResetter.Abandon(); - CallSetObservers(aURI, aName); - return NS_OK; -} - - -// nsAnnotationService::SetAnnotationInt64 - -NS_IMETHODIMP -nsAnnotationService::SetAnnotationInt64(nsIURI* aURI, - const nsACString& aName, - PRInt64 aValue, - PRInt32 aFlags, PRInt32 aExpiration) -{ - mozStorageTransaction transaction(mDBConn, PR_FALSE); - mozIStorageStatement* statement; // class var, not owned by this function - nsresult rv = StartSetAnnotation(aURI, aName, aFlags, aExpiration, &statement); - NS_ENSURE_SUCCESS(rv, rv); - mozStorageStatementScoper statementResetter(statement); - - rv = statement->BindInt64Parameter(kAnnoIndex_Content, aValue); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindNullParameter(kAnnoIndex_MimeType); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - transaction.Commit(); - - // should reset the statement; observers may call our service back to get - // annotation values! - statement->Reset(); - statementResetter.Abandon(); - CallSetObservers(aURI, aName); - return NS_OK; -} - - -// nsAnnotationService::SetAnnotationDouble - -NS_IMETHODIMP -nsAnnotationService::SetAnnotationDouble(nsIURI* aURI, - const nsACString& aName, - double aValue, - PRInt32 aFlags, PRInt32 aExpiration) -{ - mozStorageTransaction transaction(mDBConn, PR_FALSE); - mozIStorageStatement* statement; // class var, not owned by this function - nsresult rv = StartSetAnnotation(aURI, aName, aFlags, aExpiration, &statement); - NS_ENSURE_SUCCESS(rv, rv); - mozStorageStatementScoper statementResetter(statement); - - rv = statement->BindDoubleParameter(kAnnoIndex_Content, aValue); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindNullParameter(kAnnoIndex_MimeType); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - transaction.Commit(); - - // should reset the statement; observers may call our service back to get - // annotation values! - statement->Reset(); - statementResetter.Abandon(); - CallSetObservers(aURI, aName); - return NS_OK; -} - - -// nsAnnotationService::SetAnnotationBinary - -NS_IMETHODIMP -nsAnnotationService::SetAnnotationBinary(nsIURI* aURI, - const nsACString& aName, - const PRUint8 *aData, - PRUint32 aDataLen, - const nsACString& aMimeType, - PRInt32 aFlags, PRInt32 aExpiration) -{ - if (aMimeType.Length() == 0) - return NS_ERROR_INVALID_ARG; - - mozStorageTransaction transaction(mDBConn, PR_FALSE); - mozIStorageStatement* statement; // class var, not owned by this function - nsresult rv = StartSetAnnotation(aURI, aName, aFlags, aExpiration, &statement); - NS_ENSURE_SUCCESS(rv, rv); - mozStorageStatementScoper statementResetter(statement); - - rv = statement->BindBlobParameter(kAnnoIndex_Content, aData, aDataLen); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindUTF8StringParameter(kAnnoIndex_MimeType, aMimeType); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - transaction.Commit(); - - // should reset the statement; observers may call our service back to get - // annotation values! - statement->Reset(); - statementResetter.Abandon(); - CallSetObservers(aURI, aName); - return NS_OK; -} - - -// nsAnnotationService::GetAnnotationString - -NS_IMETHODIMP -nsAnnotationService::GetAnnotationString(nsIURI* aURI, - const nsACString& aName, - nsAString& _retval) -{ - nsresult rv = StartGetAnnotationFromURI(aURI, aName); - if (NS_FAILED(rv)) - return rv; - rv = mDBGetAnnotationFromURI->GetString(kAnnoIndex_Content, _retval); - mDBGetAnnotationFromURI->Reset(); - return rv; -} - - -// nsAnnotationService::GetAnnotationInt32 - -NS_IMETHODIMP -nsAnnotationService::GetAnnotationInt32(nsIURI* aURI, - const nsACString& aName, - PRInt32 *_retval) -{ - nsresult rv = StartGetAnnotationFromURI(aURI, aName); - if (NS_FAILED(rv)) - return rv; - *_retval = mDBGetAnnotationFromURI->AsInt32(kAnnoIndex_Content); - mDBGetAnnotationFromURI->Reset(); - return NS_OK; -} - - -// nsAnnotationService::GetAnnotationInt64 - -NS_IMETHODIMP -nsAnnotationService::GetAnnotationInt64(nsIURI* aURI, - const nsACString& aName, - PRInt64 *_retval) -{ - nsresult rv = StartGetAnnotationFromURI(aURI, aName); - if (NS_FAILED(rv)) - return rv; - *_retval = mDBGetAnnotationFromURI->AsInt64(kAnnoIndex_Content); - mDBGetAnnotationFromURI->Reset(); - return NS_OK; -} - - -// nsAnnotationService::GetAnnotationDouble - -NS_IMETHODIMP -nsAnnotationService::GetAnnotationDouble(nsIURI* aURI, - const nsACString& aName, - double *_retval) -{ - nsresult rv = StartGetAnnotationFromURI(aURI, aName); - if (NS_FAILED(rv)) - return rv; - *_retval = mDBGetAnnotationFromURI->AsDouble(kAnnoIndex_Content); - mDBGetAnnotationFromURI->Reset(); - return NS_OK; -} - - -// nsAnnotationService::GetAnnotationBinary - -NS_IMETHODIMP -nsAnnotationService::GetAnnotationBinary(nsIURI* aURI, - const nsACString& aName, - PRUint8** aData, PRUint32* aDataLen, - nsACString& aMimeType) -{ - nsresult rv = StartGetAnnotationFromURI(aURI, aName); - if (NS_FAILED(rv)) - return rv; - rv = mDBGetAnnotationFromURI->GetBlob(kAnnoIndex_Content, aDataLen, aData); - if (NS_FAILED(rv)) { - mDBGetAnnotationFromURI->Reset(); - return rv; - } - rv = mDBGetAnnotationFromURI->GetUTF8String(kAnnoIndex_MimeType, aMimeType); - mDBGetAnnotationFromURI->Reset(); - return rv; -} - - -// nsAnnotationService::GetAnnotationInfo - -NS_IMETHODIMP -nsAnnotationService::GetAnnotationInfo(nsIURI* aURI, - const nsACString& aName, - PRInt32 *aFlags, PRInt32 *aExpiration, - nsACString& aMimeType, - PRInt32 *aStorageType) -{ - nsresult rv = StartGetAnnotationFromURI(aURI, aName); - if (NS_FAILED(rv)) - return rv; - mozStorageStatementScoper resetter(mDBGetAnnotationFromURI); - - *aFlags = mDBGetAnnotationFromURI->AsInt32(kAnnoIndex_Flags); - *aExpiration = mDBGetAnnotationFromURI->AsInt32(kAnnoIndex_Expiration); - rv = mDBGetAnnotationFromURI->GetUTF8String(kAnnoIndex_MimeType, aMimeType); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBGetAnnotationFromURI->GetTypeOfIndex(kAnnoIndex_Content, aStorageType); - return rv; -} - - -// nsAnnotationService::GetPagesWithAnnotation - -NS_IMETHODIMP -nsAnnotationService::GetPagesWithAnnotation(const nsACString& aName, - PRUint32* aResultCount, - nsIURI*** aResults) -{ - if (aName.IsEmpty() || ! aResultCount || ! aResults) - return NS_ERROR_INVALID_ARG; - *aResultCount = 0; - *aResults = nsnull; - nsCOMArray<nsIURI> results; - - nsresult rv = GetPagesWithAnnotationCOMArray(aName, &results); - NS_ENSURE_SUCCESS(rv, rv); - - // convert to raw array - if (results.Count() == 0) - return NS_OK; - *aResults = NS_STATIC_CAST(nsIURI**, - nsMemory::Alloc(results.Count() * sizeof(nsIURI*))); - if (! aResults) - return NS_ERROR_OUT_OF_MEMORY; - *aResultCount = results.Count(); - for (PRUint32 i = 0; i < *aResultCount; i ++) { - (*aResults)[i] = results[i]; - NS_ADDREF((*aResults)[i]); - } - return NS_OK; -} - -// nsAnnotationService::GetPagesWithAnnotationTArray - -NS_IMETHODIMP -nsAnnotationService::GetPagesWithAnnotationCOMArray( - const nsACString& aName, nsCOMArray<nsIURI>* aResults){ - // this probably isn't a common operation, so we don't have a precompiled - // statement. Perhaps this should change. - nsCOMPtr<mozIStorageStatement> statement; - nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT h.url FROM moz_anno_name n " - "LEFT JOIN moz_anno a ON n.name_id = a.name_id " - "LEFT JOIN moz_history h ON a.page = h.id " - "WHERE n.name = ?1"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->BindUTF8StringParameter(0, aName); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMore = PR_FALSE; - while (NS_SUCCEEDED(rv = statement->ExecuteStep(&hasMore)) && hasMore) { - nsCAutoString uristring; - rv = statement->GetUTF8String(0, uristring); - NS_ENSURE_SUCCESS(rv, rv); - - // convert to a URI, in case of some invalid URI, just ignore this row - // so we can mostly continue. - nsCOMPtr<nsIURI> uri; - rv = NS_NewURI(getter_AddRefs(uri), uristring); - if (NS_FAILED(rv)) - continue; - PRBool added = aResults->AppendObject(uri); - NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY); - } - return NS_OK; -} - -// nsAnnotationService::GetPageAnnotationNames - -NS_IMETHODIMP -nsAnnotationService::GetPageAnnotationNames(nsIURI* aURI, PRUint32* aCount, - nsIVariant*** _result) -{ - *aCount = 0; - *_result = nsnull; - - nsTArray<nsCString> names; - nsresult rv = GetPageAnnotationNamesTArray(aURI, &names); - NS_ENSURE_SUCCESS(rv, rv); - if (names.Length() == 0) - return NS_OK; - - *_result = NS_STATIC_CAST(nsIVariant**, - nsMemory::Alloc(sizeof(nsIVariant*) * names.Length())); - NS_ENSURE_TRUE(*_result, NS_ERROR_OUT_OF_MEMORY); - - for (PRUint32 i = 0; i < names.Length(); i ++) { - nsCOMPtr<nsIWritableVariant> var = new nsVariant; - if (! var) { - // need to release all the variants we've already created - for (PRUint32 j = 0; j < i; j ++) - NS_RELEASE((*_result)[j]); - nsMemory::Free(*_result); - *_result = nsnull; - return rv; - } - var->SetAsAUTF8String(names[i]); - NS_ADDREF((*_result)[i] = var); - } - *aCount = names.Length(); - - return NS_OK; -} - - -// nsAnnotationService::GetPageAnnotationNamesTArray - -NS_IMETHODIMP -nsAnnotationService::GetPageAnnotationNamesTArray(nsIURI* aURI, - nsTArray<nsCString>* aResult) -{ - aResult->Clear(); - - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_FAILURE); - - PRInt64 uriID; - nsresult rv = history->GetUrlIdFor(aURI, &uriID, PR_FALSE); - NS_ENSURE_SUCCESS(rv, rv); - if (uriID == 0) // Check if URI exists. - return NS_OK; - - mozStorageStatementScoper scoper(mDBGetAnnotationNames); - rv = mDBGetAnnotationNames->BindInt64Parameter(0, uriID); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasResult; - nsCAutoString name; - while (NS_SUCCEEDED(mDBGetAnnotationNames->ExecuteStep(&hasResult)) && - hasResult) { - rv = mDBGetAnnotationNames->GetUTF8String(0, name); - NS_ENSURE_SUCCESS(rv, rv); - if (! aResult->AppendElement(name)) - return NS_ERROR_OUT_OF_MEMORY; - } - - return NS_OK; -} - - -// nsAnnotationService::HasAnnotation - -NS_IMETHODIMP -nsAnnotationService::HasAnnotation(nsIURI* aURI, - const nsACString& aName, - PRBool *_retval) -{ - nsresult rv = StartGetAnnotationFromURI(aURI, aName); - if (rv == NS_ERROR_NOT_AVAILABLE) { - *_retval = PR_FALSE; - rv = NS_OK; - } else if (NS_SUCCEEDED(rv)) { - *_retval = PR_TRUE; - } - mDBGetAnnotationFromURI->Reset(); - return rv; -} - - -// nsAnnotationService::RemoveAnnotation -// -// We don't remove anything from the moz_anno_name table. If we delete the -// last item of a given name, that item really should go away. It will get -// cleaned up on the next shutdown. - -NS_IMETHODIMP -nsAnnotationService::RemoveAnnotation(nsIURI* aURI, - const nsACString& aName) -{ - nsresult rv; - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_FAILURE); - - PRInt64 uriID; - rv = history->GetUrlIdFor(aURI, &uriID, PR_FALSE); - NS_ENSURE_SUCCESS(rv, rv); - if (uriID == 0) // Check if URI exists. - return NS_OK; - - mozStorageStatementScoper resetter(mDBRemoveAnnotation); - - rv = mDBRemoveAnnotation->BindInt64Parameter(0, uriID); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBRemoveAnnotation->BindUTF8StringParameter(1, aName); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBRemoveAnnotation->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - resetter.Abandon(); - - // Update observers - for (PRInt32 i = 0; i < mObservers.Count(); i ++) - mObservers[i]->OnAnnotationRemoved(aURI, aName); - - return NS_OK; -} - - -// nsAnnotationSerivce::RemovePageAnnotations -// -// I don't believe this is a common operation, so am not using a precompiled -// statement. If this ends up being used a lot, the statement should be -// precompiled and added to the class. - -NS_IMETHODIMP -nsAnnotationService::RemovePageAnnotations(nsIURI* aURI) -{ - nsresult rv; - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_UNEXPECTED); - - PRInt64 uriID; - rv = history->GetUrlIdFor(aURI, &uriID, PR_FALSE); - NS_ENSURE_SUCCESS(rv, rv); - if (uriID == 0) - return NS_OK; // URI doesn't exist, nothing to delete - - nsCOMPtr<mozIStorageStatement> statement; - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "DELETE FROM moz_anno WHERE page = ?1"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindInt64Parameter(0, uriID); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - // Update observers - for (PRInt32 i = 0; i < mObservers.Count(); i ++) - mObservers[i]->OnAnnotationRemoved(aURI, EmptyCString()); - return NS_OK; -} - - -// nsAnnotationService::CopyAnnotations -// -// This function currently assumes there are very few annotations per -// URI and that brute-force is therefore a good strategy for intersecting -// the two sets. If this ends up not being the case, this function should -// be changed to do this more efficiently. -// -// XXX: If we use annotations for some standard items like GeckoFlags, it -// might be a good idea to blacklist these standard annotations from this -// copy function. -// -// We operate on strings from GetPageAnnotationNamesTArray. This is less -// efficient than operating on name IDs, which we should consider if this -// is too slow. - -NS_IMETHODIMP -nsAnnotationService::CopyAnnotations(nsIURI* aSourceURI, nsIURI* aDestURI, - PRBool aOverwriteDest) -{ - mozStorageTransaction transaction(mDBConn, PR_FALSE); - - // source - nsTArray<nsCString> sourceNames; - nsresult rv = GetPageAnnotationNamesTArray(aSourceURI, &sourceNames); - NS_ENSURE_SUCCESS(rv, rv); - if (sourceNames.Length() == 0) - return NS_OK; // nothing to copy - - // dest - nsTArray<nsCString> destNames; - rv = GetPageAnnotationNamesTArray(aDestURI, &destNames); - NS_ENSURE_SUCCESS(rv, rv); - - // we assume you will only have a couple annotations max per URI -#ifdef DEBUG - if (sourceNames.Length() > 10 || destNames.Length() > 10) { - NS_WARNING("There are a lot of annotations, copying them may be inefficient."); - } -#endif - - if (aOverwriteDest) { - // overwrite existing ones, remove dest dupes from DB and our list - for (PRUint32 i = 0; i < sourceNames.Length(); i ++) { - PRUint32 destIndex = destNames.IndexOf(sourceNames[i]); - if (destIndex != destNames.NoIndex) { - destNames.RemoveElementAt(destIndex); - RemoveAnnotation(aDestURI, sourceNames[i]); - } - } - } else { - // don't overwrite existing ones, remove dupes from the list of source names - for (PRUint32 i = 0; i < destNames.Length(); i ++) { - PRUint32 sourceIndex = sourceNames.IndexOf(destNames[i]); - if (sourceIndex != sourceNames.NoIndex) - sourceNames.RemoveElementAt(sourceIndex); - } - } - - // given (sourceID, destID, name) this will insert a new annotation on - // source with the same values of the annotation on dest. - nsCOMPtr<mozIStorageStatement> statement; - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "INSERT INTO moz_anno (page, name_id, mime_type, content, flags, expiration) " - "SELECT ?1, name_id, mime_type, content, flags, expiration " - "FROM moz_anno WHERE page = ?2 AND name_id = " - "(SELECT name_id FROM moz_anno_name WHERE name = ?3)"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - // Get the IDs of the pages in quesion. PERFORMANCE: This is the second time - // we do this for each page, since GetPageAnnotationNamesTArray does it when - // it gets the names. If this function requires optimization, we should only - // do this once and get the names ourselves using the IDs. - PRInt64 sourceID, destID; - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_FAILURE); - - rv = history->GetUrlIdFor(aSourceURI, &sourceID, PR_FALSE); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(sourceID, NS_ERROR_UNEXPECTED); // we should have caught this above - - rv = history->GetUrlIdFor(aSourceURI, &destID, PR_FALSE); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(destID, NS_ERROR_UNEXPECTED); // we should have caught this above - - // now we know to create annotations from all sources names and there won't - // be any collisions - for (PRUint32 i = 0; i < sourceNames.Length(); i ++) { - statement->BindInt64Parameter(0, sourceID); - statement->BindInt64Parameter(1, destID); - statement->BindUTF8StringParameter(2, sourceNames[i]); - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - } - - transaction.Commit(); - return NS_OK; -} - - -// nsAnnotationService::AddObserver - -NS_IMETHODIMP -nsAnnotationService::AddObserver(nsIAnnotationObserver* aObserver) -{ - if (mObservers.IndexOfObject(aObserver) >= 0) - return NS_ERROR_INVALID_ARG; // already registered - if (!mObservers.AppendObject(aObserver)) - return NS_ERROR_OUT_OF_MEMORY; - return NS_OK; -} - - -// nsAnnotationService::RemoveObserver - -NS_IMETHODIMP -nsAnnotationService::RemoveObserver(nsIAnnotationObserver* aObserver) -{ - if (!mObservers.RemoveObject(aObserver)) - return NS_ERROR_INVALID_ARG; - return NS_OK; -} - - -// nsAnnotationService::GetAnnotationURI - -NS_IMETHODIMP -nsAnnotationService::GetAnnotationURI(nsIURI* aURI, const nsACString& aName, - nsIURI** _result) -{ - if (aName.IsEmpty()) - return NS_ERROR_INVALID_ARG; - - nsCAutoString annoSpec; - nsresult rv = aURI->GetSpec(annoSpec); - NS_ENSURE_SUCCESS(rv, rv); - - nsCAutoString spec; - spec.AssignLiteral("moz-anno:"); - spec += aName; - spec += NS_LITERAL_CSTRING(":"); - spec += annoSpec; - - return NS_NewURI(_result, spec); -} - - -// nsAnnotationService::HasAnnotationInternal -// -// This is just like HasAnnotation but takes a URL ID. It will also give -// you the ID of the annotation, if it exists. This value can be NULL and -// it won't retrieve the annotation ID. If it doesn't exist, annotationID -// is not touched. - -nsresult -nsAnnotationService::HasAnnotationInternal(PRInt64 aURLID, - const nsACString& aName, - PRBool* hasAnnotation, - PRInt64* annotationID) -{ - mozStorageStatementScoper resetter(mDBGetAnnotation); - nsresult rv; - - rv = mDBGetAnnotation->BindInt64Parameter(0, aURLID); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBGetAnnotation->BindUTF8StringParameter(1, aName); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBGetAnnotation->ExecuteStep(hasAnnotation); - NS_ENSURE_SUCCESS(rv, rv); - if (! annotationID || ! *hasAnnotation) - return NS_OK; - - return mDBGetAnnotation->GetInt64(0, annotationID); -} - - -// nsAnnotationService::StartGetAnnotationFromURI -// -// This loads the statement GetAnnotationFromURI and steps it once so you -// can get data out of it. YOU NEED TO RESET THIS STATEMENT WHEN YOU ARE -// DONE! Returns error if the annotation is not found, in which case you -// don't need to reset anything. - -nsresult -nsAnnotationService::StartGetAnnotationFromURI(nsIURI* aURI, - const nsACString& aName) -{ - mozStorageStatementScoper statementResetter(mDBGetAnnotationFromURI); - nsresult rv; - - rv = BindStatementURI(mDBGetAnnotationFromURI, 0, aURI); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBGetAnnotationFromURI->BindUTF8StringParameter(1, aName); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasResult = PR_FALSE; - rv = mDBGetAnnotationFromURI->ExecuteStep(&hasResult); - if (NS_FAILED(rv) || ! hasResult) - return NS_ERROR_NOT_AVAILABLE; - - // on success, DON'T reset the statement, the caller needs to read from it, - // and it is the caller's job to do the reseting. - statementResetter.Abandon(); - return NS_OK; -} - - -// nsAnnotationService::StartSetAnnotation -// -// This does most of the work of setting an annotation, except for setting -// the actual value and MIME type and executing the statement. It will -// create a URL entry if necessary. It will either update an existing -// annotation or insert a new one, and aStatement will be set to either -// mDBAddAnnotation or mDBSetAnnotation. The aStatement RESULT IS NOT -// ADDREFED. This is just one of the class vars, which control its scope. -// DO NOT RELEASE. -// -// The caller must make sure the statement is reset. On error, the -// statement will not need reseting. - -nsresult -nsAnnotationService::StartSetAnnotation(nsIURI* aURI, - const nsACString& aName, - PRInt32 aFlags, PRInt32 aExpiration, - mozIStorageStatement** aStatement) -{ - nsresult rv; - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_FAILURE); - - PRInt64 uriID; - rv = history->GetUrlIdFor(aURI, &uriID, PR_TRUE); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasAnnotation; - PRInt64 annotationID; - rv = HasAnnotationInternal(uriID, aName, &hasAnnotation, &annotationID); - NS_ENSURE_SUCCESS(rv, rv); - - // either update the existing annotation (using the old annotation's ID) - // or insert a new one tied to the URI. - if (hasAnnotation) { - *aStatement = mDBSetAnnotation; - rv = (*aStatement)->BindInt64Parameter(kAnnoIndex_ID, annotationID); - } else { - *aStatement = mDBAddAnnotation; - - // make sure the name exists - { - mozStorageStatementScoper scoper(mDBGetAnnotationNameID); - rv = mDBGetAnnotationNameID->BindUTF8StringParameter(0, aName); - NS_ENSURE_SUCCESS(rv, rv); - PRBool hasName; - PRInt64 nameID; - if (NS_FAILED(mDBGetAnnotationNameID->ExecuteStep(&hasName)) || ! hasName) { - // add a new annotation name - mDBGetAnnotationNameID->Reset(); - mozStorageStatementScoper addNameScoper(mDBAddAnnotationName); - rv = mDBAddAnnotationName->BindUTF8StringParameter(0, aName); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBAddAnnotationName->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBConn->GetLastInsertRowID(&nameID); - NS_ENSURE_SUCCESS(rv, rv); - } else { - nameID = mDBGetAnnotationNameID->AsInt64(0); - } - rv = (*aStatement)->BindInt64Parameter(kAnnoIndex_Page, uriID); - NS_ENSURE_SUCCESS(rv, rv); - rv = (*aStatement)->BindInt64Parameter(kAnnoIndex_Name, nameID); - NS_ENSURE_SUCCESS(rv, rv); - } - } - mozStorageStatementScoper statementResetter(*aStatement); - NS_ENSURE_SUCCESS(rv, rv); - rv = (*aStatement)->BindInt32Parameter(kAnnoIndex_Flags, aFlags); - NS_ENSURE_SUCCESS(rv, rv); - rv = (*aStatement)->BindInt32Parameter(kAnnoIndex_Expiration, aExpiration); - NS_ENSURE_SUCCESS(rv, rv); - - // on success, leave the statement open, the caller will set the value - // and MIME type and execute the statement - statementResetter.Abandon(); - return NS_OK; -} - - -// nsAnnotationService::CallSetObservers - -void -nsAnnotationService::CallSetObservers(nsIURI* aURI, const nsACString& aName) -{ - for (PRInt32 i = 0; i < mObservers.Count(); i ++) - mObservers[i]->OnAnnotationSet(aURI, aName); -} - - -// nsAnnotationService::MigrateFromAlpha1 -// -// The alpha 1 release had a column called "name" with the names of the -// annotations. We need to delete that and move the strings to a -// moz_anno_name table. Since sqlite can not change columns, we rename the -// table, copy the data, and delete the renamed version. - -nsresult // static -nsAnnotationService::MigrateFromAlpha1(mozIStorageConnection* aDBConn) -{ - // rename current annotation table - nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "ALTER TABLE moz_anno RENAME TO moz_anno_old")); - NS_ENSURE_SUCCESS(rv, rv); - - // create the new annotation table: should be the same as InitTables - // (this migration code is temporary, so we don't worry about maintenance - // too much). Note we create the indices later because we need to delete - // the old ones first. - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_anno (" - "anno_id INTEGER PRIMARY KEY," - "page INTEGER NOT NULL," - "name_id INTEGER," - "mime_type VARCHAR(32) DEFAULT NULL," - "content LONGVARCHAR, flags INTEGER DEFAULT 0," - "expiration INTEGER DEFAULT 0)")); - NS_ENSURE_SUCCESS(rv, rv); - - // Add an ID column to the old table. We can then set this and copy all - // values to the new table at once. - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "ALTER TABLE moz_anno_old ADD COLUMN name_id")); - NS_ENSURE_SUCCESS(rv, rv); - - // get the unique column names - nsCOMPtr<mozIStorageStatement> statement; - rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT DISTINCT name FROM moz_anno_old"), getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - PRBool hasMore; - nsCStringArray uniqueNames; - while (NS_SUCCEEDED(statement->ExecuteStep(&hasMore)) && hasMore) { - nsCAutoString curName; - rv = statement->GetUTF8String(0, curName); - NS_ENSURE_SUCCESS(rv, rv); - uniqueNames.AppendCString(curName); - } - - rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING( - "INSERT INTO moz_anno_name (name) VALUES (?1)"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - // add the proper name_id values into the old table - nsCOMPtr<mozIStorageStatement> updateAnno; - rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING( - "UPDATE moz_anno_old SET name_id = ?1 WHERE name = ?2"), - getter_AddRefs(updateAnno)); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRInt32 i = 0; i < uniqueNames.Count(); i ++) { - // insert the name into the moz_anno_name table - rv = statement->BindUTF8StringParameter(0, *uniqueNames[i]); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - PRInt64 nameID; - rv = aDBConn->GetLastInsertRowID(&nameID); - NS_ENSURE_SUCCESS(rv, rv); - - // update the corresponding entries in the moz_anno table to reference it - rv = updateAnno->BindInt64Parameter(0, nameID); - NS_ENSURE_SUCCESS(rv, rv); - rv = updateAnno->BindUTF8StringParameter(1, *uniqueNames[i]); - NS_ENSURE_SUCCESS(rv, rv); - rv = updateAnno->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - } - - // copy values to the new table - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "INSERT INTO moz_anno " - "(anno_id, page, name_id, mime_type, content, flags, expiration) " - "SELECT anno_id, page, name_id, mime_type, content, flags, expiration " - "FROM moz_anno_old")); - NS_ENSURE_SUCCESS(rv, rv); - - // delete old table & indices (which will be automatically dropped when their - // table is deleted) - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DROP TABLE moz_anno_old")); - NS_ENSURE_SUCCESS(rv, rv); - - // create indices on the new table - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX moz_anno_pageindex ON moz_anno (page)")); - NS_ENSURE_SUCCESS(rv, rv); - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX moz_anno_nameindex ON moz_anno (name_id)")); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} diff --git a/mozilla/browser/components/places/src/nsAnnotationService.h b/mozilla/browser/components/places/src/nsAnnotationService.h deleted file mode 100644 index 4bd01fee4be..00000000000 --- a/mozilla/browser/components/places/src/nsAnnotationService.h +++ /dev/null @@ -1,124 +0,0 @@ -//* -*- 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 Mozilla Annotation Service - * - * 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): - * Brett Wilson <brettw@gmail.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 ***** */ - -#ifndef nsAnnotationService_h___ -#define nsAnnotationService_h___ - -#include "nsIAnnotationService.h" -#include "nsCOMArray.h" -#include "nsCOMPtr.h" -#include "mozIStorageService.h" -#include "mozIStorageConnection.h" -#include "nsServiceManagerUtils.h" - -class nsAnnotationService : public nsIAnnotationService -{ -public: - nsAnnotationService(); - - nsresult Init(); - - static nsresult InitTables(mozIStorageConnection* aDBConn); - - /** - * Returns a cached pointer to the annotation service for consumers in the - * places directory. - */ - static nsAnnotationService* GetAnnotationService() - { - if (! gAnnotationService) { - // note that we actually have to set the service to a variable here - // because the work in do_GetService actually happens during assignment >:( - nsresult rv; - nsCOMPtr<nsIAnnotationService> serv(do_GetService("@mozilla.org/browser/annotation-service;1", &rv)); - NS_ENSURE_SUCCESS(rv, nsnull); - - // our constructor should have set the static variable. If it didn't, - // something is wrong. - NS_ASSERTION(gAnnotationService, "Annotation service creation failed"); - } - // the service manager will keep the pointer to our service around, so - // this should always be valid even if nobody currently has a reference. - return gAnnotationService; - } - - NS_DECL_ISUPPORTS - NS_DECL_NSIANNOTATIONSERVICE - -private: - ~nsAnnotationService(); - -protected: - nsCOMPtr<mozIStorageService> mDBService; - nsCOMPtr<mozIStorageConnection> mDBConn; - - nsCOMPtr<mozIStorageStatement> mDBSetAnnotation; - nsCOMPtr<mozIStorageStatement> mDBGetAnnotation; - nsCOMPtr<mozIStorageStatement> mDBGetAnnotationNames; - nsCOMPtr<mozIStorageStatement> mDBGetAnnotationFromURI; - nsCOMPtr<mozIStorageStatement> mDBGetAnnotationNameID; - nsCOMPtr<mozIStorageStatement> mDBAddAnnotationName; - nsCOMPtr<mozIStorageStatement> mDBAddAnnotation; - nsCOMPtr<mozIStorageStatement> mDBRemoveAnnotation; - - nsCOMArray<nsIAnnotationObserver> mObservers; - - static nsAnnotationService* gAnnotationService; - - static const int kAnnoIndex_ID; - static const int kAnnoIndex_Page; - static const int kAnnoIndex_Name; - static const int kAnnoIndex_MimeType; - static const int kAnnoIndex_Content; - static const int kAnnoIndex_Flags; - static const int kAnnoIndex_Expiration; - - nsresult HasAnnotationInternal(PRInt64 aURLID, const nsACString& aName, - PRBool* hasAnnotation, PRInt64* annotationID); - nsresult StartGetAnnotationFromURI(nsIURI* aURI, - const nsACString& aName); - nsresult StartSetAnnotation(nsIURI* aURI, - const nsACString& aName, - PRInt32 aFlags, PRInt32 aExpiration, - mozIStorageStatement** aStatement); - void CallSetObservers(nsIURI* aURI, const nsACString& aName); - - static nsresult MigrateFromAlpha1(mozIStorageConnection* aDBConn); -}; - -#endif /* nsAnnotationService_h___ */ diff --git a/mozilla/browser/components/places/src/nsBookmarksFeedHandler.cpp b/mozilla/browser/components/places/src/nsBookmarksFeedHandler.cpp deleted file mode 100755 index a17e0b43272..00000000000 --- a/mozilla/browser/components/places/src/nsBookmarksFeedHandler.cpp +++ /dev/null @@ -1,940 +0,0 @@ -/* -*- 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 mozilla.org code. - * - * The Initial Developer of the Original Code is - * Vladimir Vukicevic <vladimir@pobox.com> - * Portions created by the Initial Developer are Copyright (C) 2004 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Annie Sullivan <annie.sullivan@gmail.com> (modified to work with places) - * Robert Sayre <sayrer@gmail.com> - * Masayuki Nakano <masayuki@d-toybox.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 ***** */ - -// THE ORIGINAL LOCATION OF THIS CODE IS -// /browser/components/bookmarks/src/nsBookmarksFeedHandler.cpp - -#include "nsLivemarkService.h" -#include "nsArrayEnumerator.h" -#include "nsIArray.h" -#include "nsIDOMWindow.h" -#include "nsIObserverService.h" -#include "nsIRDFContainer.h" -#include "nsIRDFContainerUtils.h" -#include "nsIRDFService.h" -#include "nsIRDFXMLSerializer.h" -#include "nsIRDFXMLSource.h" -#include "nsIRDFXMLParser.h" -#include "nsRDFCID.h" -#include "nsISupportsPrimitives.h" -#include "rdf.h" -#include "nsUnicharUtils.h" -#include "nsInt64.h" -#include "nsStringStream.h" - -#include "nsIScriptSecurityManager.h" - -#include "nsIURL.h" -#include "nsIInputStream.h" -#include "nsNetUtil.h" -#include "nsICachingChannel.h" -#include "nsICacheVisitor.h" - -#include "nsIDOMParser.h" -#include "nsIDOMDocument.h" -#include "nsIDOMElement.h" -#include "nsIDOMCharacterData.h" -#include "nsIDOMNodeList.h" -#include "nsIDOM3Node.h" -#include "nsIDOMDocumentTraversal.h" -#include "nsIDOMTreeWalker.h" -#include "nsIDOMNodeFilter.h" -#include "nsIDOMDocumentFragment.h" -#include "nsIParser.h" -#include "nsParserCIID.h" -#include "nsIFragmentContentSink.h" -#include "nsIContentSink.h" -#include "nsIDocument.h" -#include "nsNavBookmarks.h" - -static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID); -static NS_DEFINE_CID(kRDFContainerCID, NS_RDFCONTAINER_CID); -static NS_DEFINE_CID(kRDFInMemoryDataSourceCID, NS_RDFINMEMORYDATASOURCE_CID); - -//////////////////////////////////////////////////////////////////////// -// nsLivemarkLoadListener -// -// An nsIStreamListener implementation that UpdateLivemarkChildren uses -// to aysnchronously fetch and update a livemark's child entries. -// -// This could potentially become a nested class within the livemarks service. -// - -class nsLivemarkLoadListener : public nsIStreamListener -{ -public: - nsLivemarkLoadListener(nsLivemarkService *aLivemarkService, - nsCOMPtr<nsIAnnotationService> aAnnotationService, - nsLivemarkService::LivemarkInfo * pLivemark) - : mLivemarkService(aLivemarkService), mAnnotationService(aAnnotationService), - mLivemark(pLivemark), mAborted(PR_FALSE) - { - NS_IF_ADDREF(mLivemarkService); - } - - virtual ~nsLivemarkLoadListener() { - NS_IF_RELEASE(mLivemarkService); - } - - NS_DECL_ISUPPORTS - NS_DECL_NSISTREAMLISTENER - NS_DECL_NSIREQUESTOBSERVER - - void Abort () { mAborted = PR_TRUE; } - -protected: - static NS_METHOD StreamReaderCallback(nsIInputStream *in, - void *closure, - const char *fromRawSegment, - PRUint32 toOffset, - PRUint32 count, - PRUint32 *writeCount); - - NS_METHOD TryParseAsRDF(); - NS_METHOD TryParseAsSimpleRSS(); - NS_METHOD SetResourceTTL(PRInt32 ttl); - - // helpers - NS_METHOD HandleRDFItem (nsIRDFDataSource *aDS, nsIRDFResource *itemResource, - nsIRDFResource *aLinkResource, nsIRDFResource *aTitleResource); - NS_METHOD FindTextInChildNodes (nsIDOMNode *aNode, nsAString &aString); - NS_METHOD ParseHTMLFragment(nsAString &aFragString, nsIDocument* aTargetDocument, nsIDOMNode **outNode); - - PRBool IsLinkValid(const PRUnichar *aURI); - - nsLivemarkService *mLivemarkService; - nsCOMPtr<nsIAnnotationService> mAnnotationService; - nsRefPtr<nsLivemarkService::LivemarkInfo> mLivemark; - nsCOMPtr<nsIOutputStream> mCacheStream; - nsCOMPtr<nsIScriptSecurityManager> mSecMan; - PRBool mAborted; - nsCString mBody; -}; - -NS_IMPL_ISUPPORTS2(nsLivemarkLoadListener, nsIStreamListener, nsIRequestObserver) - -NS_IMETHODIMP -nsLivemarkLoadListener::OnStartRequest(nsIRequest *aResult, nsISupports *ctxt) -{ - if (mAborted) - return NS_ERROR_UNEXPECTED; - - mBody.Truncate(); - return NS_OK; -} - -NS_METHOD -nsLivemarkLoadListener::StreamReaderCallback(nsIInputStream *aInputStream, - void *aClosure, - const char *aRawSegment, - PRUint32 aToOffset, - PRUint32 aCount, - PRUint32 *aWriteCount) -{ - nsLivemarkLoadListener *rll = (nsLivemarkLoadListener *) aClosure; - - rll->mBody.Append(aRawSegment, aCount); - *aWriteCount = aCount; - - return NS_OK; -} - -NS_IMETHODIMP -nsLivemarkLoadListener::OnDataAvailable(nsIRequest *aRequest, - nsISupports *aContext, - nsIInputStream *aInputStream, - PRUint32 aSourceOffset, - PRUint32 aCount) -{ - PRUint32 totalRead; - return aInputStream->ReadSegments(StreamReaderCallback, (void *)this, aCount, &totalRead); -} - -NS_IMETHODIMP -nsLivemarkLoadListener::OnStopRequest(nsIRequest *aRequest, - nsISupports *aContext, - nsresult aStatus) -{ - nsresult rv; - - if (mAborted) { - mLivemark->locked = PR_FALSE; - return NS_OK; - } - - if (NS_FAILED(aStatus)) { - // Something went wrong; try to load again in 5 minutes. - SetResourceTTL (300); - mLivemark->locked = PR_FALSE; - return NS_OK; - } - - // enclose all the changes (deletes and a bunch of adds) in a batch to - // avoid UI and DB updates - nsBookmarksUpdateBatcher bookmarksBatch; - - // Clear out any child nodes of the livemark folder, since - // they're about to be replaced. - rv = mLivemarkService->DeleteLivemarkChildren(mLivemark->folderId); - - if (NS_FAILED(rv)) { - mLivemark->locked = PR_FALSE; - return rv; - } - - // Grab the security manager - mSecMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); - - // We need to parse the returned data here, stored in mBody. We - // try parsing as RDF first, then as Atom and the "simple" RSS - // (the userland 0.91/0.92/2.0 formats) - // - - // Try parsing as RDF - rv = TryParseAsRDF(); - - // Try parsing as Atom/Simple RSS - if (!NS_SUCCEEDED(rv)) { - rv = TryParseAsSimpleRSS(); - } - - // If we weren't able to load with anything, attach a dummy bookmark - if (!NS_SUCCEEDED(rv)) { - rv = mLivemarkService->InsertLivemarkFailedItem(mLivemark->folderId); - } - - // Set an expiration on the livemark, for reloading the data - PRInt32 ttl; - if (NS_FAILED(rv)) { - // if we failed, try again in 1 hour, to avoid trying - // to load a feed that doesn't parse over and over. - ttl = 3600; - } else { - // TODO -- read the pref for livemark reload time and set to max of - // the pref value and 1 minute. - ttl = 3600; // 1 hr default - - // ensure that the ttl is at least equal to the cache expiry time, since - // otherwise a reload won't have much effect - nsCOMPtr<nsICachingChannel> channel = do_QueryInterface(aRequest); - if (channel) { - nsCOMPtr<nsISupports> cacheToken; - channel->GetCacheToken(getter_AddRefs(cacheToken)); - if (cacheToken) { - nsCOMPtr<nsICacheEntryInfo> entryInfo = do_QueryInterface(cacheToken); - if (entryInfo) { - PRUint32 expiresTime; - - if (NS_SUCCEEDED(entryInfo->GetExpirationTime(&expiresTime))) { - PRInt64 temp64, nowtime = PR_Now(); - PRUint32 nowsec; - LL_I2L(temp64, PR_USEC_PER_SEC); - LL_DIV(temp64, nowtime, temp64); - LL_L2UI(nowsec, temp64); - - if (nowsec >= expiresTime) { - expiresTime -= nowsec; - if (ttl < (PRInt32) expiresTime) - ttl = (PRInt32) expiresTime; - } - } - } - } - } - } - - rv = SetResourceTTL(ttl); - if (NS_FAILED(rv)) { - NS_WARNING ("SetResourceTTL failed on Livemark"); - } - - mLivemark->locked = PR_FALSE; - - return NS_OK; -} - -/** - * SetResourceTTL: Set the next time we should attempt to reload this - * resource's feed - */ - -nsresult -nsLivemarkLoadListener::SetResourceTTL (PRInt32 aTTL) -{ - nsresult rv; - - PRTime million, temp64, exptime = PR_Now(); - LL_I2L (million, PR_USEC_PER_SEC); - LL_I2L (temp64, aTTL); - LL_MUL (temp64, temp64, million); - LL_ADD (exptime, exptime, temp64); - - - rv = mAnnotationService->SetAnnotationInt64(mLivemark->feedURI, - NS_LITERAL_CSTRING(LMANNO_EXPIRATION), - exptime, 0, - nsIAnnotationService::EXPIRE_NEVER); - if (NS_FAILED(rv)) return rv; - - return NS_OK; -} - -/** - * TryParseAsRDF: attempt to parse the read data in mBody as - * RSS 0.90/1.0 data. This is supposed to be RDF, so we use - * the RDF parser to do our work for us. - */ - -nsresult -nsLivemarkLoadListener::TryParseAsRDF () -{ - nsresult rv; - - nsCOMPtr<nsIRDFXMLParser> rdfparser(do_CreateInstance("@mozilla.org/rdf/xml-parser;1", &rv)); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr<nsIRDFDataSource> ds(do_CreateInstance(kRDFInMemoryDataSourceCID, &rv)); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr<nsIStreamListener> listener; - rv = rdfparser->ParseAsync(ds, mLivemark->feedURI, getter_AddRefs(listener)); - if (NS_FAILED(rv)) return rv; - if (!listener) return NS_ERROR_FAILURE; - - nsCOMPtr<nsIInputStream> stream; - rv = NS_NewCStringInputStream(getter_AddRefs(stream), mBody); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr<nsIChannel> channel; - rv = NS_NewInputStreamChannel(getter_AddRefs(channel), mLivemark->feedURI, - stream, NS_LITERAL_CSTRING("text/xml")); - if (NS_FAILED(rv)) return rv; - - listener->OnStartRequest(channel, nsnull); - listener->OnDataAvailable(channel, nsnull, stream, 0, mBody.Length()); - listener->OnStopRequest(channel, nsnull, NS_OK); - - // Grab the (only) thing that's a channel - // We try RSS 1.0 first, then RSS 0.9, and set up the remaining - // resources accordingly - - nsIRDFResource *RSS_items = nsnull; - nsIRDFResource *RSS_title = nsnull; - nsIRDFResource *RSS_link = nsnull; - - nsCOMPtr<nsIRDFResource> channelResource = nsnull; - - rv = ds->GetSource(mLivemarkService->mLMRDF_type, mLivemarkService->mLMRSS10_channel, PR_TRUE, getter_AddRefs(channelResource)); - if (NS_FAILED(rv)) return NS_ERROR_FAILURE; - if (rv == NS_OK) { - RSS_items = mLivemarkService->mLMRSS10_items; - RSS_title = mLivemarkService->mLMRSS10_title; - RSS_link = mLivemarkService->mLMRSS10_link; - } else { - // try RSS 0.9 - rv = ds->GetSource(mLivemarkService->mLMRDF_type, mLivemarkService->mLMRSS09_channel, PR_TRUE, getter_AddRefs(channelResource)); - if (NS_FAILED(rv)) return NS_ERROR_FAILURE; - if (rv == NS_OK) { - RSS_items = nsnull; - RSS_title = mLivemarkService->mLMRSS09_title; - RSS_link = mLivemarkService->mLMRSS09_link; - } - } - - if (!channelResource) { - // no channel, either 1.0 or 0.9 - return NS_ERROR_FAILURE; - } - - // this will get filled in differently. - nsCOMPtr<nsISimpleEnumerator> itemsEnumerator; - - if (RSS_items) { - // if there is something that should be rss:items, then it's RSS 1.0 - nsCOMPtr<nsIRDFNode> itemsNode; - rv = ds->GetTarget(channelResource, RSS_items, PR_TRUE, getter_AddRefs(itemsNode)); - if (NS_FAILED(rv) || rv == NS_RDF_NO_VALUE) return NS_ERROR_FAILURE; - - // items is a seq - nsCOMPtr<nsIRDFContainer> itemsContainer = do_CreateInstance (kRDFContainerCID, &rv); - if (NS_FAILED(rv)) return rv; - rv = itemsContainer->Init(ds, (nsIRDFResource *) itemsNode.get()); - if (NS_FAILED(rv)) return rv; - - rv = itemsContainer->GetElements (getter_AddRefs(itemsEnumerator)); - if (NS_FAILED(rv) || rv == NS_RDF_NO_VALUE) return NS_ERROR_FAILURE; - } else { - // - // if there is no rss:items, but we still were able to parse it as RDF - // and found a channel, then it's possibly RSS 0.9. For RSS 0.9, - // we know that each item will be an <item ...>, so we get everything - // that has a type of item. - rv = ds->GetSources(mLivemarkService->mLMRDF_type, mLivemarkService->mLMRSS09_item, PR_TRUE, getter_AddRefs(itemsEnumerator)); - if (NS_FAILED(rv)) return NS_ERROR_FAILURE; - } - - // Go through each resource and pull out its link/title, if present. - PRBool more; - while (NS_SUCCEEDED(rv = itemsEnumerator->HasMoreElements(&more)) && more) { - nsCOMPtr<nsISupports> iSupports; - rv = itemsEnumerator->GetNext(getter_AddRefs(iSupports)); - if (NS_FAILED(rv)) break; - - nsCOMPtr<nsIRDFResource> item(do_QueryInterface(iSupports)); - if (!item) { - rv = NS_ERROR_UNEXPECTED; - break; - } - - rv = HandleRDFItem (ds, item, RSS_link, RSS_title); - // ignore rv - } - if (NS_FAILED(rv)) return rv; - - return NS_OK; -} - - -nsresult -nsLivemarkLoadListener::ParseHTMLFragment(nsAString &aFragString, - nsIDocument* aTargetDocument, - nsIDOMNode **outNode) -{ - NS_ENSURE_ARG(aTargetDocument); - - nsresult rv; - - nsCOMPtr<nsIParser> parser; - parser = do_CreateInstance(kCParserCID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - // create the html fragment sink - nsCOMPtr<nsIContentSink> sink; - sink = do_CreateInstance(NS_HTMLFRAGMENTSINK2_CONTRACTID); - NS_ENSURE_TRUE(sink, NS_ERROR_FAILURE); - nsCOMPtr<nsIFragmentContentSink> fragSink(do_QueryInterface(sink)); - NS_ENSURE_TRUE(fragSink, NS_ERROR_FAILURE); - - fragSink->SetTargetDocument(aTargetDocument); - - // parse the fragment - parser->SetContentSink(sink); -#ifdef MOZILLA_1_8_BRANCH - parser->Parse(aFragString, (void*)0, NS_LITERAL_CSTRING("text/html"), PR_FALSE, PR_TRUE, eDTDMode_fragment); -#else - parser->Parse(aFragString, (void*)0, NS_LITERAL_CSTRING("text/html"), PR_TRUE, eDTDMode_fragment); -#endif - // get the fragment node - nsCOMPtr<nsIDOMDocumentFragment> contextfrag; - rv = fragSink->GetFragment(getter_AddRefs(contextfrag)); - NS_ENSURE_SUCCESS(rv, rv); - rv = CallQueryInterface (contextfrag, outNode); - - return rv; -} - - -// find all of the text and CDATA nodes below aNode and return them as a string -nsresult -nsLivemarkLoadListener::FindTextInChildNodes (nsIDOMNode *aNode, nsAString &aString) -{ - NS_ENSURE_ARG(aNode); - - nsresult rv; - - nsCOMPtr<nsIDOMDocument> aDoc; - aNode->GetOwnerDocument(getter_AddRefs(aDoc)); - nsCOMPtr<nsIDOMDocumentTraversal> trav = do_QueryInterface(aDoc, &rv); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr<nsIDOMTreeWalker> walker; - rv = trav->CreateTreeWalker(aNode, - nsIDOMNodeFilter::SHOW_TEXT | nsIDOMNodeFilter::SHOW_CDATA_SECTION, - nsnull, PR_TRUE, getter_AddRefs(walker)); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr<nsIDOMNode> currentNode; - walker->GetCurrentNode(getter_AddRefs(currentNode)); - nsCOMPtr<nsIDOMCharacterData> charTextNode; - nsAutoString tempString; - while (currentNode) { - charTextNode = do_QueryInterface(currentNode); - if (charTextNode) { - charTextNode->GetData(tempString); - aString.Append(tempString); - } - walker->NextNode(getter_AddRefs(currentNode)); - } - - if (aString.IsEmpty()) { - return NS_ERROR_FAILURE; - } else { - return NS_OK; - } -} - -nsresult -nsLivemarkLoadListener::HandleRDFItem (nsIRDFDataSource *aDS, nsIRDFResource *aItem, - nsIRDFResource *aLinkResource, - nsIRDFResource *aTitleResource) -{ - nsresult rv; - - // We care about this item's link and title - nsCOMPtr<nsIRDFNode> linkNode; - rv = aDS->GetTarget (aItem, aLinkResource, PR_TRUE, getter_AddRefs(linkNode)); - if (NS_FAILED(rv) || rv == NS_RDF_NO_VALUE) return NS_ERROR_FAILURE; - - nsCOMPtr<nsIRDFNode> titleNode; - rv = aDS->GetTarget (aItem, aTitleResource, PR_TRUE, getter_AddRefs(titleNode)); - if (rv == NS_RDF_NO_VALUE) { - rv = aDS->GetTarget (aItem, mLivemarkService->mLMDC_date, PR_TRUE, getter_AddRefs(titleNode)); - } - if (NS_FAILED(rv) || rv == NS_RDF_NO_VALUE) return NS_ERROR_FAILURE; - - nsCOMPtr<nsIRDFLiteral> linkLiteral(do_QueryInterface(linkNode)); - nsCOMPtr<nsIRDFLiteral> titleLiteral(do_QueryInterface(titleNode)); - - // if the link/title points to something other than a literal skip it. - if (!linkLiteral || !titleLiteral) - return NS_ERROR_FAILURE; - - const PRUnichar *linkStr, *titleStr; - rv = linkLiteral->GetValueConst(&linkStr); - rv |= titleLiteral->GetValueConst(&titleStr); - if (NS_FAILED(rv)) return rv; - - if (!IsLinkValid(linkStr)) - return NS_OK; - - // Add new bookmark with the link and title. - nsCOMPtr<nsIURI> linkURI; - rv = NS_NewURI(getter_AddRefs(linkURI), NS_ConvertUTF16toUTF8(linkStr)); - rv = mLivemarkService->InsertLivemarkChild(mLivemark->folderId, - linkURI, - nsDependentString(titleStr), - nsDependentString(linkStr)); - if (NS_FAILED(rv)) return rv; - - return NS_OK; -} - -/** - * TryParseAsSimpleRSS - * - * Tries to parse the content as RSS (Userland) 0.91/0.92/2.0, or Atom - * These are not RDF formats. - */ - -nsresult -nsLivemarkLoadListener::TryParseAsSimpleRSS () -{ - nsresult rv; - - nsCOMPtr<nsIDOMParser> parser(do_CreateInstance("@mozilla.org/xmlextras/domparser;1", &rv)); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr<nsIDOMDocument> xmldoc; - parser->SetBaseURI(mLivemark->feedURI); - rv = parser->ParseFromBuffer ((const PRUint8*) mBody.get(), mBody.Length(), "text/xml", getter_AddRefs(xmldoc)); - if (NS_FAILED(rv)) return rv; - - // becomes true if we figure out that this is an atom stream. - PRBool isAtom = PR_FALSE; - - nsCOMPtr<nsIDOMElement> docElement; - rv = xmldoc->GetDocumentElement(getter_AddRefs(docElement)); - if (!docElement) return NS_ERROR_UNEXPECTED; - if (NS_FAILED(rv)) return rv; - - nsCOMPtr<nsIDOMNode> node; - rv = xmldoc->GetFirstChild(getter_AddRefs(node)); - if (!node) return NS_ERROR_UNEXPECTED; - if (NS_FAILED(rv)) return rv; - - PRBool lookingForChannel = PR_FALSE; - - while (node) { - PRUint16 ntype; - rv = node->GetNodeType(&ntype); - if (NS_FAILED(rv)) return rv; - - if (ntype == nsIDOMNode::ELEMENT_NODE) { - nsAutoString nname; - rv = node->GetNodeName (nname); - - // slight hack to get node pointing to the thing that - // has items/entries as its children. We need to descend - // into <channel> for RSS, but not for Atom. - if (!lookingForChannel) { - if (nname.Equals(NS_LITERAL_STRING("rss"))) { - lookingForChannel = PR_TRUE; - nsCOMPtr<nsIDOMNode> temp; - rv = node->GetFirstChild(getter_AddRefs(temp)); - if (!temp) return NS_ERROR_UNEXPECTED; - if (NS_FAILED(rv)) return rv; - node = temp; - continue; - } - if (nname.Equals(NS_LITERAL_STRING("feed"))) { - // Atom has no <channel>; instead, <item>s are - // children of <feed> - isAtom = PR_TRUE; - break; - } - } else { - if (nname.Equals(NS_LITERAL_STRING("channel"))) { - break; - } - } - } - - nsCOMPtr<nsIDOMNode> temp; - rv = node->GetNextSibling(getter_AddRefs(temp)); - if (!temp) return NS_ERROR_UNEXPECTED; - if (NS_FAILED(rv)) return rv; - node = temp; - } - - // we didn't find a rss/feed/channel or whatever - if (!node) - return NS_ERROR_FAILURE; - - nsCOMPtr<nsIDOMElement> chElement = do_QueryInterface(node); - if (!chElement) return NS_ERROR_UNEXPECTED; - - // Go through the <channel>/<feed> and do what we need - // with <item> or <entry> nodes - - rv = chElement->GetFirstChild(getter_AddRefs(node)); - if (!node) return NS_ERROR_UNEXPECTED; - if (NS_FAILED(rv)) return rv; - - while (node) { - PRUint16 ntype; - rv = node->GetNodeType(&ntype); - if (NS_FAILED(rv)) return rv; - - if (ntype == nsIDOMNode::ELEMENT_NODE) { - nsAutoString nname; - rv = node->GetNodeName (nname); - - if ((!isAtom && nname.Equals(NS_LITERAL_STRING("item"))) || - ( isAtom && nname.Equals(NS_LITERAL_STRING("entry")))) - { - // We need to pull out the <title> and <link> children - nsAutoString titleStr; - nsAutoString linkStr; - nsAutoString dateStr; - - nsCOMPtr<nsIDOMNode> childNode; - rv = node->GetFirstChild(getter_AddRefs(childNode)); - if (NS_FAILED(rv)) return rv; - - while (childNode) { - PRUint16 childNtype; - rv = childNode->GetNodeType(&childNtype); - if (NS_FAILED(rv)) return rv; - - if (childNtype == nsIDOMNode::ELEMENT_NODE) { - nsAutoString childNname; - rv = childNode->GetNodeName (childNname); - - if (childNname.Equals(NS_LITERAL_STRING("title"))) { - if (isAtom) { - /* Atom titles can contain HTML, so we need to find and remove it */ - nsCOMPtr<nsIDOMElement> titleElem = do_QueryInterface(childNode); - if (!titleElem) break; // out of while(childNode) loop - - nsAutoString titleMode; // Atom 0.3 only - nsAutoString titleType; - titleElem->GetAttribute(NS_LITERAL_STRING("type"), titleType); - titleElem->GetAttribute(NS_LITERAL_STRING("mode"), titleMode); - - /* No one does this in <title> except standards pedants making test feeds, - * Atom 0.3 is deprecated, and RFC 4287 doesn't allow it - */ - if (titleMode.EqualsLiteral("base64")) { - break; // out of while(childNode) loop - } - - /* Cover Atom 0.3 and RFC 4287 together */ - if (titleType.EqualsLiteral("text") || - titleType.EqualsLiteral("text/plain") || - titleType.IsEmpty()) - { - rv = FindTextInChildNodes(childNode, titleStr); - } else if (titleType.EqualsLiteral("html") || // RFC 4287 - (titleType.EqualsLiteral("text/html") && // Atom 0.3 - !titleMode.EqualsLiteral("xml")) || - titleMode.EqualsLiteral("escaped")) // Atom 0.3 - { - nsAutoString escapedHTMLStr; - rv = FindTextInChildNodes(childNode, escapedHTMLStr); - if (NS_FAILED(rv)) break; // out of while(childNode) loop - - nsCOMPtr<nsIDOMNode> newNode; - nsCOMPtr<nsIDocument> doc = do_QueryInterface(xmldoc); - ParseHTMLFragment(escapedHTMLStr, doc, getter_AddRefs(newNode)); - rv = FindTextInChildNodes(newNode, titleStr); - - }else if (titleType.EqualsLiteral("xhtml") || // RFC 4287 - titleType.EqualsLiteral("application/xhtml") || // Atom 0.3 - titleMode.EqualsLiteral("xml")) // Atom 0.3 - { - rv = FindTextInChildNodes(childNode, titleStr); - } else { - break; // out of while(childNode) loop - } - } else { - rv = FindTextInChildNodes(childNode, titleStr); - } - if (NS_FAILED(rv)) break; - } else if (childNname.Equals(NS_LITERAL_STRING("pubDate")) || - childNname.Equals(NS_LITERAL_STRING("updated"))) - { - rv = FindTextInChildNodes (childNode, dateStr); - if (NS_FAILED(rv)) break; - } else if (!isAtom && childNname.Equals(NS_LITERAL_STRING("guid"))) { - nsCOMPtr<nsIDOMElement> linkElem = do_QueryInterface(childNode); - if (!linkElem) break; // out of while(childNode) loop - - nsAutoString isPermaLink; - linkElem->GetAttribute(NS_LITERAL_STRING("isPermaLink"), isPermaLink); - // Ignore failures. isPermaLink defaults to true. - if (!isPermaLink.Equals(NS_LITERAL_STRING("false"))) { - // in node's TEXT - rv = FindTextInChildNodes (childNode, linkStr); - if (NS_FAILED(rv)) break; - } - } else if (childNname.Equals(NS_LITERAL_STRING("link"))) { - if (isAtom) { - // in HREF attribute - nsCOMPtr<nsIDOMElement> linkElem = do_QueryInterface(childNode); - if (!linkElem) break; // out of while(childNode) loop - - nsAutoString rel; - linkElem->GetAttribute(NS_LITERAL_STRING("rel"), rel); - if (rel.Equals(NS_LITERAL_STRING("alternate")) || - rel.IsEmpty()) - { - rv = linkElem->GetAttribute(NS_LITERAL_STRING("href"), linkStr); - if (NS_FAILED(rv)) break; // out of while(childNode) loop - - nsCOMPtr<nsIDOM3Node> linkElem3 = do_QueryInterface(childNode); - if (linkElem3) { - // get the BaseURI (as string) - nsAutoString base; - rv = linkElem3->GetBaseURI(base); - if (NS_SUCCEEDED(rv) && !base.IsEmpty()) { - // convert a baseURI (string) to a nsIURI - nsCOMPtr<nsIURI> baseURI; - rv = NS_NewURI(getter_AddRefs(baseURI), base); - if (baseURI) { - nsString absLinkStr; - if (NS_SUCCEEDED(NS_MakeAbsoluteURI(absLinkStr, linkStr, baseURI))) - linkStr = absLinkStr; - } - } - } - } - } else if (linkStr.IsEmpty()) { - // in node's TEXT - rv = FindTextInChildNodes (childNode, linkStr); - if (NS_FAILED(rv)) break; - } - } - } - - if (!titleStr.IsEmpty() && !linkStr.IsEmpty()) - break; - - nsCOMPtr<nsIDOMNode> temp; - rv = childNode->GetNextSibling(getter_AddRefs(temp)); - childNode = temp; - if (!childNode || NS_FAILED(rv)) break; - } - - // Clean up whitespace - titleStr.CompressWhitespace(); - linkStr.Trim("\b\t\r\n "); - dateStr.CompressWhitespace(); - - if (titleStr.IsEmpty() && !dateStr.IsEmpty()) - titleStr.Assign(dateStr); - - if (!titleStr.IsEmpty() && !linkStr.IsEmpty() && IsLinkValid(linkStr.get())) { - nsCOMPtr<nsIURI> linkURI; - rv = NS_NewURI(getter_AddRefs(linkURI), - NS_ConvertUTF16toUTF8(linkStr)); - if (NS_FAILED(rv)) return rv; - rv = mLivemarkService->InsertLivemarkChild(mLivemark->folderId, - linkURI, - nsDependentString(titleStr), - nsDependentString(linkStr)); - if (NS_FAILED(rv)) return rv; - } - } - } - - nsCOMPtr<nsIDOMNode> temp; - rv = node->GetNextSibling(getter_AddRefs(temp)); - if (NS_FAILED(rv)) return rv; - node = temp; - } - - return NS_OK; -} - - -// return true if this link is valid and a livemark should be created; -// otherwise, false. -PRBool -nsLivemarkLoadListener::IsLinkValid(const PRUnichar *aURI) -{ - nsCOMPtr<nsIURI> linkuri; - nsresult rv = NS_NewURI(getter_AddRefs(linkuri), nsDependentString(aURI)); - if (NS_FAILED(rv)) - return PR_FALSE; - - // Er, where'd our security manager go? - if (!mSecMan) - return PR_FALSE; - - rv = mSecMan->CheckLoadURI(mLivemark->feedURI, linkuri, - nsIScriptSecurityManager::DISALLOW_FROM_MAIL | - nsIScriptSecurityManager::DISALLOW_SCRIPT_OR_DATA); - if (NS_FAILED(rv)) - return PR_FALSE; - - return PR_TRUE; -} - -/////////////////////////////////////////////////////////////////////////// -//// Main entry point for nsLivemarkService to deal with Livemark -//// load listener -//// - -/* - * Update the child elements of a livemark; take care of cache checking, - * channel setup and firing off the async load and parse. If aForceUpdate - * is true, the livemark will be updated whether or not it has expired; this - * is intended for user "Update Livemark" commands. - */ -nsresult -nsLivemarkService::UpdateLivemarkChildren(PRInt32 aLivemarkIndex, PRBool aForceUpdate) -{ - nsresult rv; - - if (mLivemarks[aLivemarkIndex]->locked) - { - // We're already loading the livemark - return NS_OK; - } - - // Lock the livemark - mLivemarks[aLivemarkIndex]->locked = PR_TRUE; - - // Check the TTL/expiration on this. If there isn't one, - // then we assume it's never been loaded. We perform this - // check even when the update is being forced, in case the - // livemark has somehow never been loaded. - PRTime exprTime; - rv = mAnnotationService->GetAnnotationInt64(mLivemarks[aLivemarkIndex]->feedURI, - NS_LITERAL_CSTRING(LMANNO_EXPIRATION), - &exprTime); - if (rv == NS_OK) { - if (! aForceUpdate) { - PRTime nowTime = PR_Now(); - if (LL_CMP(exprTime, >, nowTime)) { - // No need to refresh yet. - mLivemarks[aLivemarkIndex]->locked = PR_FALSE; - return rv; - } - } - } else { - // This livemark has never been loaded, since it has no expire time. - rv = InsertLivemarkLoadingItem(mLivemarks[aLivemarkIndex]->folderId); - } - - // Create a load group for the request. This will allow us to automatically - // keep track of redirects, so we can always cancel the channel. - - nsCOMPtr<nsILoadGroup> loadGroup; - rv = NS_NewLoadGroup(getter_AddRefs(loadGroup), nsnull); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<nsIChannel> uriChannel; - rv = NS_NewChannel(getter_AddRefs(uriChannel), - mLivemarks[aLivemarkIndex]->feedURI, - nsnull, loadGroup, nsnull, - nsIRequest::LOAD_BACKGROUND); - if (NS_FAILED(rv)) { - mLivemarks[aLivemarkIndex]->locked = PR_FALSE; - return rv; - } - - nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(uriChannel); - if (httpChannel) { - // XXXvladimir - handle POST livemarks - rv = httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("GET")); - } - - nsCOMPtr<nsLivemarkLoadListener> listener = new nsLivemarkLoadListener(this, - mAnnotationService, - mLivemarks[aLivemarkIndex]); - nsCOMPtr<nsIChannel> channel; - rv = NS_NewChannel(getter_AddRefs(channel), - mLivemarks[aLivemarkIndex]->feedURI, nsnull, nsnull, nsnull, - nsIRequest::LOAD_BACKGROUND | nsIRequest::VALIDATE_ALWAYS); - if (NS_FAILED(rv)) { - mLivemarks[aLivemarkIndex]->locked = PR_FALSE; - return rv; - } - - rv = channel->AsyncOpen(listener, nsnull); - if (NS_FAILED(rv)) { - mLivemarks[aLivemarkIndex]->locked = PR_FALSE; - return rv; - } - - mLivemarks[aLivemarkIndex]->loadGroup = loadGroup; - return NS_OK; -} diff --git a/mozilla/browser/components/places/src/nsBookmarksHTML.cpp b/mozilla/browser/components/places/src/nsBookmarksHTML.cpp deleted file mode 100644 index 2b698e8d9e5..00000000000 --- a/mozilla/browser/components/places/src/nsBookmarksHTML.cpp +++ /dev/null @@ -1,1709 +0,0 @@ -//* -*- 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 Mozilla History System - * - * 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): - * Brett Wilson <brettw@gmail.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 ***** */ - -/** - * Importer/exporter between the mozStorage-based bookmarks and the old-style - * "bookmarks.html" - * - * Format: - * - * Primary heading := h1 - * Old version used this to set attributes on the bookmarks RDF root, such - * as the last modified date. We only use H1 to check for the attribute - * PLACES_ROOT, which tells us that this hierarchy root is the places root. - * For backwards compatability, if we don't find this, we assume that the - * hierarchy is rooted at the bookmarks menu. - * Heading := any heading other than h1 - * Old version used this to set attributes on the current container. We only - * care about the content of the heading container, which contains the title - * of the bookmark container. - * Bookmark := a - * HREF is the destination of the bookmark - * FEEDURL is the URI of the RSS feed if this is a livemark. - * LAST_CHARSET should be stored as an annotation (FIXME bug 334408) so that the - * next time we go to that page we remember the user's preference. - * ICON will be stored in the favicon service - * ICON_URI is new for places bookmarks.html, it refers to the original - * URI of the favicon so we don't have to make up favicon URLs. - * Text of the <a> container is the name of the bookmark - * Ignored: ADD_DATE, LAST_VISIT, LAST_MODIFIED, ID - * Bookmark comment := dd - * This affects the previosly added bookmark - * Separator := hr - * Insert a separator into the current container - * The folder hierarchy is defined by <dl>/<ul>/<menu> (the old importing code - * handles all these cases, when we write, use <dl>). - * - * Overall design - * -------------- - * - * We need to emulate a recursive parser. A "Bookmark import frame" is created - * corresponding to each folder we encounter. These are arranged in a stack, - * and contain all the state we need to keep track of. - * - * A frame is created when we find a heading, which defines a new container. - * The frame also keeps track of the nesting of <DL>s, (in well-formed - * bookmarks files, these will have a 1-1 correspondence with frames, but we - * try to be a little more flexible here). When the nesting count decreases - * to 0, then we know a frame is complete and to pop back to the previous - * frame. - * - * Note that a lot of things happen when tags are CLOSED because we need to - * get the text from the content of the tag. For example, link and heading tags - * both require the content (= title) before actually creating it. - */ - -#include "nsToolkitCompsCID.h" -#include "nsCOMPtr.h" -#include "nsCRT.h" -#include "nsEscape.h" -#include "nsFaviconService.h" -#include "nsIAnnotationService.h" -#include "nsIFile.h" -#include "nsIHTMLContentSink.h" -#include "nsLivemarkService.h" -#include "nsIParser.h" -#include "nsIServiceManager.h" -#include "nsNavBookmarks.h" -#include "nsNavHistory.h" -#include "nsNavHistoryResult.h" -#include "nsNetUtil.h" -#include "nsParserCIID.h" -#include "nsString.h" -#include "nsTArray.h" -#include "nsUnicharUtils.h" -#include "mozStorageHelper.h" -#include "plbase64.h" -#include "prtime.h" - -static NS_DEFINE_CID(kParserCID, NS_PARSER_CID); - -#define KEY_TOOLBARFOLDER_LOWER "personal_toolbar_folder" -#define KEY_BOOKMARKSMENU_LOWER "bookmarks_menu" -#define KEY_PLACESROOT_LOWER "places_root" -#define KEY_HREF_LOWER "href" -#define KEY_FEEDURL_LOWER "feedurl" -#define KEY_LASTCHARSET_LOWER "last_charset" -#define KEY_ICON_LOWER "icon" -#define KEY_ICON_URI_LOWER "icon_uri" -#define KEY_SHORTCUTURL_LOWER "shortcuturl" - -#define BOOKMARKS_MENU_ICON_URI "chrome://browser/skin/places/bookmarksMenu.png" -#define BOOKMARKS_TOOLBAR_ICON_URI "chrome://browser/skin/places/bookmarksToolbar.png" - -// define to get debugging messages on console about import -//#define DEBUG_IMPORT - -static const char kWhitespace[] = " \r\n\t\b"; - -static nsresult WriteEscapedUrl(const nsCString& aString, - nsIOutputStream* aOutput); - -class BookmarkImportFrame -{ -public: - BookmarkImportFrame(PRInt64 aID) : - mContainerID(aID), - mContainerNesting(0), - mLastContainerType(Container_Normal), - mInDescription(PR_FALSE) - { - } - - enum ContainerType { Container_Normal, - Container_Places, - Container_Menu, - Container_Toolbar }; - - PRInt64 mContainerID; - - // How many <dl>s have been nested. Each frame/container should start - // with a heading, and is then followed by a <dl>, <ul>, or <menu>. When - // that list is complete, then it is the end of this container and we need - // to pop back up one level for new items. If we never get an open tag for - // one of these things, we should assume that the container is empty and - // that things we find should be siblings of it. Normally, these <dl>s won't - // be nested so this will be 0 or 1. - PRInt32 mContainerNesting; - - // when we find a heading tag, it actually affects the title of the NEXT - // container in the list. This stores that heading tag and whether it was - // special. 'ConsumeHeading' resets this. - ContainerType mLastContainerType; - - // this contains the text from the last begin tag until now. It is reset - // at every begin tag. We can check it when we see a </a>, or </h3> - // to see what the text content of that node should be. - nsString mPreviousText; - - // true when we hit a <dd>, which contains the description for the preceeding - // <a> tag. We can't just check for </dd> like we can for </a> or </h3> - // because if there is a sub-folder, it is actually a child of the <dd> - // because the tag is never explicitly closed. If this is true and we see a - // new open tag, that means to commit the description to the previous - // bookmark. - // - // Additional weirdness happens when the previous <dt> tag contains a <h3>: - // this means there is a new folder with the given description, and whose - // children are contained in the following <dl> list. - // - // This is handled in OpenContainer(), which commits previous text if - // necessary. - PRBool mInDescription; - - // contains the URL of the previous bookmark created. This is used so that - // when we encounter a <dd>, we know what bookmark to associate the text with. - // This is cleared whenever we hit a <h3>, so that we know NOT to save this - // with a bookmark, but to keep it until - nsCOMPtr<nsIURI> mPreviousLink; - - // contains the URL of the previous livemark, so that when the link ends, - // and the livemark title is known, we can create it. - nsCOMPtr<nsIURI> mPreviousFeed; - - void ConsumeHeading(nsAString* aHeading, ContainerType* aContainerType) - { - *aHeading = mPreviousText; - *aContainerType = mLastContainerType; - mPreviousText.Truncate(0); - } -}; - - -/** - * The content sink stuff is based loosely on - */ -class BookmarkContentSink : public nsIHTMLContentSink -{ -public: - nsresult Init(PRBool aAllowRootChanges, - nsINavBookmarksService* bookmarkService, - PRInt64 aFolder); - - NS_DECL_ISUPPORTS - - // nsIContentSink (superclass of nsIHTMLContentSink) - NS_IMETHOD WillBuildModel() { return NS_OK; } - NS_IMETHOD DidBuildModel() { return NS_OK; } - NS_IMETHOD WillInterrupt() { return NS_OK; } - NS_IMETHOD WillResume() { return NS_OK; } - NS_IMETHOD SetParser(nsIParser* aParser) { return NS_OK; } - virtual void FlushPendingNotifications(mozFlushType aType) { } - NS_IMETHOD SetDocumentCharset(nsACString& aCharset) { return NS_OK; } - virtual nsISupports *GetTarget() { return nsnull; } - - // nsIHTMLContentSink -#ifdef MOZILLA_1_8_BRANCH - NS_IMETHOD SetTitle(const nsString& aValue) { return NS_OK; } - NS_IMETHOD OpenHTML(const nsIParserNode& aNode) { return NS_OK; } - NS_IMETHOD CloseHTML() { return NS_OK; } - NS_IMETHOD OpenHead(const nsIParserNode& aNode) { return NS_OK; } - NS_IMETHOD CloseHead() { return NS_OK; } - NS_IMETHOD OpenBody(const nsIParserNode& aNode) { return NS_OK; } - NS_IMETHOD CloseBody() { return NS_OK; } - NS_IMETHOD OpenForm(const nsIParserNode& aNode) { return NS_OK; } - NS_IMETHOD CloseForm() { return NS_OK; } - NS_IMETHOD OpenMap(const nsIParserNode& aNode) { return NS_OK; } - NS_IMETHOD CloseMap() { return NS_OK; } - NS_IMETHOD OpenFrameset(const nsIParserNode& aNode) { return NS_OK; } - NS_IMETHOD CloseFrameset() { return NS_OK; } - NS_IMETHOD AddHeadContent(const nsIParserNode& aNode) { return NS_OK; } -#else - NS_IMETHOD OpenHead() { return NS_OK; } -#endif - NS_IMETHOD BeginContext(PRInt32 aPosition) { return NS_OK; } - NS_IMETHOD EndContext(PRInt32 aPosition) { return NS_OK; } - NS_IMETHOD IsEnabled(PRInt32 aTag, PRBool* aReturn) - { *aReturn = PR_TRUE; return NS_OK; } - NS_IMETHOD WillProcessTokens() { return NS_OK; } - NS_IMETHOD DidProcessTokens() { return NS_OK; } - NS_IMETHOD WillProcessAToken() { return NS_OK; } - NS_IMETHOD DidProcessAToken() { return NS_OK; } - NS_IMETHOD OpenContainer(const nsIParserNode& aNode); - NS_IMETHOD CloseContainer(const nsHTMLTag aTag); - NS_IMETHOD AddLeaf(const nsIParserNode& aNode); - NS_IMETHOD AddComment(const nsIParserNode& aNode) { return NS_OK; } - NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode) { return NS_OK; } - NS_IMETHOD AddDocTypeDecl(const nsIParserNode& aNode) { return NS_OK; } - NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode) { return NS_OK; } - NS_IMETHOD_(PRBool) IsFormOnStack() { return PR_FALSE; } - -protected: - nsCOMPtr<nsINavBookmarksService> mBookmarksService; - nsCOMPtr<nsINavHistoryService> mHistoryService; - nsCOMPtr<nsIAnnotationService> mAnnotationService; - nsCOMPtr<nsILivemarkService> mLivemarkService; - - // if set, we will move root items to where we find them. This should be - // set when we are loading the default places html file, and should be - // unset when doing normal imports so that, for example, the toolbar folder - // will be a child of the menu in old bookmarks.html, and we don't want - // to reparent it on import. - PRBool mAllowRootChanges; - - // If a folder was specified to import into, then ignore flags to put - // bookmarks in the bookmarks menu or toolbar and keep them inside - // the folder. - PRBool mFolderSpecified; - - void HandleContainerBegin(const nsIParserNode& node); - void HandleContainerEnd(); - void HandleHead1Begin(const nsIParserNode& node); - void HandleHeadBegin(const nsIParserNode& node); - void HandleHeadEnd(); - void HandleLinkBegin(const nsIParserNode& node); - void HandleLinkEnd(); - void HandleSeparator(); - - // This is a list of frames. We really want a recursive parser, but the HTML - // parser gives us tags as a stream. This implements all the state on a stack - // so we can get the recursive information we need. Use "CurFrame" to get the - // top "stack frame" with the current state in it. - nsTArray<BookmarkImportFrame> mFrames; - BookmarkImportFrame& CurFrame() - { - NS_ASSERTION(mFrames.Length() > 0, "Asking for frame when there are none!"); - return mFrames[mFrames.Length() - 1]; - } - nsresult NewFrame(); - nsresult PopFrame(); - - nsresult SetFaviconForURI(nsIURI* aPageURI, nsIURI* aFaviconURI, - const nsCString& aData); - nsresult SetFaviconForFolder(PRInt64 aFolder, const nsACString& aFavicon); - -#ifdef DEBUG_IMPORT - // prints spaces for indenting to the current frame depth - void PrintNesting() - { - for (PRUint32 i = 0; i < mFrames.Length(); i ++) - printf(" "); - } -#endif -}; - - -// BookmarkContentSink::Init -// -// Note that the bookmark service pointer is passed in. We can not create -// the bookmark service from here because this can be called from bookmark -// service creation, making a weird reentrant loop. - -nsresult -BookmarkContentSink::Init(PRBool aAllowRootChanges, - nsINavBookmarksService* bookmarkService, - PRInt64 aFolder) -{ - nsresult rv; - mBookmarksService = bookmarkService; - mHistoryService = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - mAnnotationService = do_GetService(NS_ANNOTATIONSERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - mLivemarkService = do_GetService(NS_LIVEMARKSERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - mAllowRootChanges = aAllowRootChanges; - - // initialize the root frame with the menu root - PRInt64 menuRoot; - if (aFolder == 0) { - rv = mBookmarksService->GetBookmarksRoot(&menuRoot); - NS_ENSURE_SUCCESS(rv, rv); - mFolderSpecified = false; - } - else { - menuRoot = aFolder; - mFolderSpecified = true; - } - if (! mFrames.AppendElement(BookmarkImportFrame(menuRoot))) - return NS_ERROR_OUT_OF_MEMORY; - - return NS_OK; -} - - -NS_IMPL_ISUPPORTS2(BookmarkContentSink, - nsIContentSink, - nsIHTMLContentSink) - -// nsIContentSink ************************************************************** - -NS_IMETHODIMP -BookmarkContentSink::OpenContainer(const nsIParserNode& aNode) -{ - // see the comment for the definition of mInDescription. Basically, we commit - // any text in mPreviousText to the description of the node/folder if there - // is any. - BookmarkImportFrame& frame = CurFrame(); - if (frame.mInDescription) { - frame.mPreviousText.Trim(kWhitespace); // important! - if (! frame.mPreviousText.IsEmpty()) { - // FIXME bug 334758: This description should be stored as an annotation - // on the URL or folder. We should probably not overwrite existing - // annotations. - frame.mPreviousText.Truncate(0); - } - frame.mInDescription = PR_FALSE; - } - - switch(aNode.GetNodeType()) { - case eHTMLTag_h1: - HandleHead1Begin(aNode); - break; - case eHTMLTag_h2: - case eHTMLTag_h3: - case eHTMLTag_h4: - case eHTMLTag_h5: - case eHTMLTag_h6: - HandleHeadBegin(aNode); - break; - case eHTMLTag_a: - HandleLinkBegin(aNode); - break; - case eHTMLTag_dl: - case eHTMLTag_ul: - case eHTMLTag_menu: - HandleContainerBegin(aNode); - break; - } - return NS_OK; -} - -NS_IMETHODIMP -BookmarkContentSink::CloseContainer(const nsHTMLTag aTag) -{ - switch (aTag) { - case eHTMLTag_dl: - case eHTMLTag_ul: - case eHTMLTag_menu: - HandleContainerEnd(); - break; - case eHTMLTag_dt: - break; - case eHTMLTag_h1: - // ignore - break; - case eHTMLTag_h2: - case eHTMLTag_h3: - case eHTMLTag_h4: - case eHTMLTag_h5: - case eHTMLTag_h6: - HandleHeadEnd(); - break; - case eHTMLTag_a: - HandleLinkEnd(); - break; - default: - break; - } - return NS_OK; -} - - -// BookmarkContentSink::AddLeaf -// -// XXX on the branch, we should be calling CollectSkippedContent as in -// nsHTMLFragmentContentSink.cpp:AddLeaf when we encounter title, script, -// style, or server tags. Apparently if we don't, we'll leak the next DOM -// node. However, this requires that we keep a reference to the parser we'll -// introduce a circular reference because it has a reference to us. -// -// This is annoying to fix and these elements are not allowed in bookmarks -// files anyway. So if somebody tries to import a crazy bookmarks file, it -// will leak a little bit. - -NS_IMETHODIMP -BookmarkContentSink::AddLeaf(const nsIParserNode& aNode) -{ - switch (aNode.GetNodeType()) { - case eHTMLTag_text: - // save any text we find - CurFrame().mPreviousText += aNode.GetText(); - break; - case eHTMLTag_entity: { - nsAutoString tmp; - PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp); - if (unicode < 0) { - // invalid entity - just use the text of it - CurFrame().mPreviousText += aNode.GetText(); - } else { - CurFrame().mPreviousText.Append(unicode); - } - break; - } - case eHTMLTag_whitespace: - CurFrame().mPreviousText.Append(PRUnichar(' ')); - break; - case eHTMLTag_hr: - HandleSeparator(); - break; - } - - return NS_OK; -} - -// BookmarkContentSink::HandleContainerBegin - -void -BookmarkContentSink::HandleContainerBegin(const nsIParserNode& node) -{ - CurFrame().mContainerNesting ++; -} - - -// BookmarkContentSink::HandleContainerEnd -// -// Our "indent" count has decreased, and when we hit 0 that means that this -// container is complete and we need to pop back to the outer frame. Never -// pop the toplevel frame - -void -BookmarkContentSink::HandleContainerEnd() -{ - BookmarkImportFrame& frame = CurFrame(); - if (frame.mContainerNesting > 0) - frame.mContainerNesting --; - if (mFrames.Length() > 1 && frame.mContainerNesting == 0) - PopFrame(); -} - - -// BookmarkContentSink::HandleHead1Begin -// -// Handles <H1>. We check for the attribute PLACES_ROOT and reset the -// container id if it's found. Otherwise, the default bookmark menu -// root is assumed and imported things will go into the bookmarks menu. - -void -BookmarkContentSink::HandleHead1Begin(const nsIParserNode& node) -{ - PRInt32 attrCount = node.GetAttributeCount(); - for (PRInt32 i = 0; i < attrCount; i ++) { - if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_PLACESROOT_LOWER)) { - if (mFrames.Length() > 1) { - NS_WARNING("Trying to set the places root from the middle of the hierarchy. " - "This can only be set at the beginning."); - return; - } - PRInt64 mPlacesRoot; - mBookmarksService->GetPlacesRoot(&mPlacesRoot); - CurFrame().mContainerID = mPlacesRoot; - break; - } - } -} - - -// BookmarkContentSink::HandleHeadBegin -// -// Called for h2,h3,h4,h5,h6. This just stores the correct information in -// the current frame; the actual new frame corresponding to the container -// associated with the heading will be created when the tag has been closed -// and we know the title (we don't know to create a new folder or to merge -// with an existing one until we have the title). - -void -BookmarkContentSink::HandleHeadBegin(const nsIParserNode& node) -{ - BookmarkImportFrame& frame = CurFrame(); - - // after a heading, a previous bookmark is not applicable (for example, for - // the descriptions contained in a <dd>). Neither is any previous head type - frame.mPreviousLink = nsnull; - frame.mLastContainerType = BookmarkImportFrame::Container_Normal; - - // It is syntactically possible for a heading to appear after another heading - // but before the <dl> that encloses that folder's contents. This should not - // happen in practice, as the file will contain "<dl></dl>" sequence for - // empty containers. - // - // Just to be on the safe side, if we encounter - // <h3>FOO</h3> - // <h3>BAR</h3> - // <dl>...content 1...</dl> - // <dl>...content 2...</dl> - // we'll pop the stack when we find the h3 for BAR, treating that as an - // implicit ending of the FOO container. The output will be FOO and BAR as - // siblings. If there's another <dl> following (as in "content 2"), those - // items will be treated as further siblings of FOO and BAR - if (frame.mContainerNesting == 0) - PopFrame(); - - // We have to check for some attributes to see if this is a "special" - // folder, which will have different creation rules when the end tag is - // processed. - PRInt32 attrCount = node.GetAttributeCount(); - frame.mLastContainerType = BookmarkImportFrame::Container_Normal; - if (!mFolderSpecified) { - for (PRInt32 i = 0; i < attrCount; i ++) { - if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_TOOLBARFOLDER_LOWER)) { - frame.mLastContainerType = BookmarkImportFrame::Container_Toolbar; - break; - } else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_BOOKMARKSMENU_LOWER)) { - frame.mLastContainerType = BookmarkImportFrame::Container_Menu; - break; - } else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_PLACESROOT_LOWER)) { - frame.mLastContainerType = BookmarkImportFrame::Container_Places; - break; - } - } - } - CurFrame().mPreviousText.Truncate(0); -} - - -// BookmarkContentSink::HandleHeadEnd -// -// Creates the new frame for this heading now that we know the name of the -// container (tokens since the heading open tag will have been placed in -// mPreviousText). - -void -BookmarkContentSink::HandleHeadEnd() -{ - NewFrame(); -} - - -// BookmarkContentSink::HandleLinkBegin -// -// Handles "<a" tags by creating a new bookmark. The title of the bookmark -// will be the text content, which will be stuffed in mPreviousText for us -// and which will be saved by HandleLinkEnd - -void -BookmarkContentSink::HandleLinkBegin(const nsIParserNode& node) -{ - BookmarkImportFrame& frame = CurFrame(); - - // We need to make sure that the feed URIs from previous frames are emptied. - frame.mPreviousFeed = nsnull; - - // mPreviousText will hold our link text, clear it so that can be appended to - frame.mPreviousText.Truncate(); - - // get the attributes we care about - nsAutoString href; - nsAutoString feedUrl; - nsAutoString icon; - nsAutoString iconUri; - nsAutoString lastCharset; - nsAutoString keyword; - PRInt32 attrCount = node.GetAttributeCount(); - for (PRInt32 i = 0; i < attrCount; i ++) { - const nsAString& key = node.GetKeyAt(i); - if (key.LowerCaseEqualsLiteral(KEY_HREF_LOWER)) { - href = node.GetValueAt(i); - } else if (key.LowerCaseEqualsLiteral(KEY_FEEDURL_LOWER)) { - feedUrl = node.GetValueAt(i); - } else if (key.LowerCaseEqualsLiteral(KEY_ICON_LOWER)) { - icon = node.GetValueAt(i); - } else if (key.LowerCaseEqualsLiteral(KEY_ICON_URI_LOWER)) { - iconUri = node.GetValueAt(i); - } else if (key.LowerCaseEqualsLiteral(KEY_LASTCHARSET_LOWER)) { - lastCharset = node.GetValueAt(i); - } else if (key.LowerCaseEqualsLiteral(KEY_SHORTCUTURL_LOWER)) { - keyword = node.GetValueAt(i); - } - } - href.Trim(kWhitespace); - feedUrl.Trim(kWhitespace); - icon.Trim(kWhitespace); - iconUri.Trim(kWhitespace); - lastCharset.Trim(kWhitespace); - keyword.Trim(kWhitespace); - - // For feeds, get the feed URL. If it is invalid, it will leave mPreviousFeed - // NULL and we'll continue trying to create it as a normal bookmark. - if (! feedUrl.IsEmpty()) { - NS_NewURI(getter_AddRefs(frame.mPreviousFeed), - NS_ConvertUTF16toUTF8(feedUrl), nsnull); - } - - // Ignore <a> tags that have no href: we don't know what to do with them. - if (href.IsEmpty()) { - frame.mPreviousLink = nsnull; - - // The exception is for feeds, where the href is an optional component - // indicating the source web site. - if (! frame.mPreviousFeed) - return; - } else { - // Save this so the link text and descriptions can be associated with it. - // Note that we ignore errors if this is a feed: URLs aren't strictly - // necessary in these cases. - nsresult rv = NS_NewURI(getter_AddRefs(frame.mPreviousLink), - href, nsnull); - if (NS_FAILED(rv) && ! frame.mPreviousFeed) { - frame.mPreviousLink = nsnull; - return; // invalid link - } - } - - // if there is a feedURL, this is a livemark, which is a special case - // that we handle in HandleLinkBegin(): don't create normal bookmarks - if (frame.mPreviousFeed) - return; - - // create the bookmark, but only if it is not in the folder already. If it - // is already in the folder, we'll override the titles and favicons, but we - // won't want to put it at the end again - nsTArray<PRInt64> currentFolders; - mBookmarksService->GetBookmarkFoldersTArray(frame.mPreviousLink, - ¤tFolders); - PRBool hasBookmark = PR_FALSE; - for (PRUint32 curFolder = 0; curFolder < currentFolders.Length(); curFolder ++) { - if (currentFolders[curFolder] == frame.mContainerID) { - hasBookmark = PR_TRUE; - break; - } - } - if (! hasBookmark) - mBookmarksService->InsertItem(frame.mContainerID, frame.mPreviousLink, -1); - - // save the favicon, ignore errors - if (! icon.IsEmpty() || ! iconUri.IsEmpty()) { - nsCOMPtr<nsIURI> iconUriObject; - NS_NewURI(getter_AddRefs(iconUriObject), iconUri); - if (! icon.IsEmpty() || iconUriObject) - SetFaviconForURI(frame.mPreviousLink, iconUriObject, - NS_ConvertUTF16toUTF8(icon)); - } - - // save the keyword, ignore errors - if (! keyword.IsEmpty()) - mBookmarksService->SetKeywordForURI(frame.mPreviousLink, keyword); - - // FIXME bug 334408: save the last charset -} - - -// BookmarkContentSink::HandleLinkEnd -// -// Saves the title for the given bookmark. This only writes the user title. -// Any previous title will be untouched. If this is a new entry, it will have -// an empty "official" title until you visit it. - -void -BookmarkContentSink::HandleLinkEnd() -{ - BookmarkImportFrame& frame = CurFrame(); - frame.mPreviousText.Trim(kWhitespace); - if (frame.mPreviousFeed) { - // The bookmark is actually a livemark. Create it here. - // (It gets created here instead of in HandleLinkBegin() - // because we need to know the title before creating it.) - PRInt64 folderId; - mLivemarkService->CreateLivemark(frame.mContainerID, - frame.mPreviousText, - frame.mPreviousLink, - frame.mPreviousFeed, - -1, - &folderId); -#ifdef DEBUG_IMPORT - PrintNesting(); - printf("Creating livemark '%s'\n", - NS_ConvertUTF16toUTF8(frame.mPreviousText).get()); -#endif - } - else if (frame.mPreviousLink) { -#ifdef DEBUG_IMPORT - PrintNesting(); - printf("Creating bookmark '%s'\n", - NS_ConvertUTF16toUTF8(frame.mPreviousText).get()); -#endif - mHistoryService->SetPageUserTitle(frame.mPreviousLink, - frame.mPreviousText); - } - frame.mPreviousText.Truncate(0); -} - - -// BookmarkContentSink::HandleSeparator -// -// Inserts a separator into the current container -void -BookmarkContentSink::HandleSeparator() -{ - BookmarkImportFrame& frame = CurFrame(); - - // bookmarks.html contains a separator between the toolbar menu and the - // rest of the items. Since we pull the toolbar menu out into the top level, - // we want to skip over this separator since it looks out of place. - if (frame.mLastContainerType != BookmarkImportFrame::Container_Toolbar) { - // create the separator -#ifdef DEBUG_IMPORT - PrintNesting(); - printf("--------\n"); -#endif - mBookmarksService->InsertSeparator(frame.mContainerID, -1); - } -} - - -// BookmarkContentSink::NewFrame -// -// This is called when there is a new folder found. The folder takes the -// name from the previous frame's heading. - -nsresult -BookmarkContentSink::NewFrame() -{ - nsresult rv; - - nsString containerName; - BookmarkImportFrame::ContainerType containerType; - CurFrame().ConsumeHeading(&containerName, &containerType); - - PRBool updateFolder = PR_FALSE; - PRInt64 ourID = 0; - switch (containerType) { - case BookmarkImportFrame::Container_Normal: - // regular folder: use an existing folder if that name already exists - rv = mBookmarksService->GetChildFolder(CurFrame().mContainerID, - containerName, &ourID); - NS_ENSURE_SUCCESS(rv, rv); - if (! ourID) { - // need to append a new folder - rv = mBookmarksService->CreateFolder(CurFrame().mContainerID, - containerName, -1, &ourID); - NS_ENSURE_SUCCESS(rv, rv); - } - break; - case BookmarkImportFrame::Container_Places: - // places root, never reparent here, when we're building the initial - // hierarchy, it will only be defined at the top level - rv = mBookmarksService->GetPlacesRoot(&ourID); - NS_ENSURE_SUCCESS(rv, rv); - break; - case BookmarkImportFrame::Container_Menu: - // menu root - rv = mBookmarksService->GetBookmarksRoot(&ourID); - NS_ENSURE_SUCCESS(rv, rv); - if (mAllowRootChanges) { - updateFolder = PR_TRUE; - SetFaviconForFolder(ourID, NS_LITERAL_CSTRING(BOOKMARKS_MENU_ICON_URI)); - } - break; - case BookmarkImportFrame::Container_Toolbar: - // toolbar root - rv = mBookmarksService->GetToolbarRoot(&ourID); - NS_ENSURE_SUCCESS(rv, rv); - if (mAllowRootChanges) { - updateFolder = PR_TRUE; - SetFaviconForFolder(ourID, NS_LITERAL_CSTRING(BOOKMARKS_TOOLBAR_ICON_URI)); - } - break; - default: - NS_NOTREACHED("Unknown container type"); - } -#ifdef DEBUG_IMPORT - PrintNesting(); - printf("Folder %lld \'%s\'", ourID, NS_ConvertUTF16toUTF8(containerName).get()); -#endif - - if (updateFolder) { - // move the menu/toolbar folder to the current position - mBookmarksService->MoveFolder(ourID, CurFrame().mContainerID, -1); - mBookmarksService->SetFolderTitle(ourID, containerName); -#ifdef DEBUG_IMPORT - printf(" [reparenting]"); -#endif - } - -#ifdef DEBUG_IMPORT - printf("\n"); -#endif - - if (! mFrames.AppendElement(BookmarkImportFrame(ourID))) - return NS_ERROR_OUT_OF_MEMORY; - return NS_OK; -} - - -// BookmarkContentSink::PopFrame -// - -nsresult -BookmarkContentSink::PopFrame() -{ - // we must always have one frame - if (mFrames.Length() <= 1) { - NS_NOTREACHED("Trying to complete more bookmark folders than you started"); - return NS_ERROR_FAILURE; - } - mFrames.RemoveElementAt(mFrames.Length() - 1); - return NS_OK; -} - - -// BookmarkContentSink::SetFaviconForURI -// -// aData is a string that is a data URI for the favicon. Our job is to -// decode it and store it in the favicon service. -// -// When aIconURI is non-null, we will use that as the URI of the favicon -// when storing in the favicon service. -// -// When aIconURI is null, we have to make up a URI for this favicon so that -// it can be stored in the service. The real one will be set the next time -// the user visits the page. Our made up one should get expired when the -// page no longer references it. - -nsresult -BookmarkContentSink::SetFaviconForURI(nsIURI* aPageURI, nsIURI* aIconURI, - const nsCString& aData) -{ - nsresult rv; - static PRUint32 serialNumber = 0; // for made-up favicon URIs - - nsFaviconService* faviconService = nsFaviconService::GetFaviconService(); - if (! faviconService) - return NS_ERROR_NO_INTERFACE; - - // if the input favicon URI is a chrome: URI, then we just save it and don't - // worry about data - if (aIconURI) { - nsCString faviconScheme; - aIconURI->GetScheme(faviconScheme); - if (faviconScheme.EqualsLiteral("chrome")) { - return faviconService->SetFaviconUrlForPage(aPageURI, aIconURI); - } - } - - // some bookmarks have placeholder URIs that contain just "data:" - // ignore these - if (aData.Length() <= 5) - return NS_OK; - - nsCOMPtr<nsIURI> faviconURI; - if (aIconURI) { - faviconURI = aIconURI; - } else { - // make up favicon URL - nsCAutoString faviconSpec; - faviconSpec.AssignLiteral("http://www.mozilla.org/2005/made-up-favicon/"); - faviconSpec.AppendInt(serialNumber); - faviconSpec.AppendLiteral("-"); - faviconSpec.AppendInt(PR_Now()); - rv = NS_NewURI(getter_AddRefs(faviconURI), faviconSpec); - NS_ENSURE_SUCCESS(rv, rv); - serialNumber ++; - } - - nsCOMPtr<nsIURI> dataURI; - rv = NS_NewURI(getter_AddRefs(dataURI), aData); - NS_ENSURE_SUCCESS(rv, rv); - - // use the data: protocol handler to convert the data - nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr<nsIProtocolHandler> protocolHandler; - rv = ioService->GetProtocolHandler("data", getter_AddRefs(protocolHandler)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<nsIChannel> channel; - rv = protocolHandler->NewChannel(dataURI, getter_AddRefs(channel)); - NS_ENSURE_SUCCESS(rv, rv); - - // blocking stream is OK for data URIs - nsCOMPtr<nsIInputStream> stream; - rv = channel->Open(getter_AddRefs(stream)); - NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 available; - rv = stream->Available(&available); - NS_ENSURE_SUCCESS(rv, rv); - if (available == 0) - return NS_ERROR_FAILURE; - - // read all the decoded data - PRUint8* buffer = NS_STATIC_CAST(PRUint8*, - nsMemory::Alloc(sizeof(PRUint8) * available)); - if (! buffer) - return NS_ERROR_OUT_OF_MEMORY; - PRUint32 numRead; - rv = stream->Read(NS_REINTERPRET_CAST(char*, buffer), available, &numRead); - if (NS_FAILED(rv) || numRead != available) { - nsMemory::Free(buffer); - return rv; - } - - nsCAutoString mimeType; - rv = channel->GetContentType(mimeType); - NS_ENSURE_SUCCESS(rv, rv); - - // save in service - rv = faviconService->SetFaviconData(faviconURI, buffer, available, mimeType, 0); - nsMemory::Free(buffer); - NS_ENSURE_SUCCESS(rv, rv); - return faviconService->SetFaviconUrlForPage(aPageURI, faviconURI); -} - - -// BookmarkContentSink::SetFaviconForFolder -// -// This sets the given favicon URI for the given folder. It is used to -// initialize the favicons for the bookmarks menu and toolbar. We don't -// actually set any data here because we assume the URI is a chrome: URI. -// These do not have to contain any data for them to work. - -nsresult -BookmarkContentSink::SetFaviconForFolder(PRInt64 aFolder, - const nsACString& aFavicon) -{ - nsFaviconService* faviconService = nsFaviconService::GetFaviconService(); - NS_ENSURE_TRUE(faviconService, NS_ERROR_OUT_OF_MEMORY); - - nsCOMPtr<nsIURI> folderURI; - nsresult rv = mBookmarksService->GetFolderURI(aFolder, - getter_AddRefs(folderURI)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<nsIURI> faviconURI; - rv = NS_NewURI(getter_AddRefs(faviconURI), aFavicon); - NS_ENSURE_SUCCESS(rv, rv); - - return faviconService->SetFaviconUrlForPage(folderURI, faviconURI); -} - - -// SyncChannelStatus -// -// If a function returns an error, we need to set the channel status to be -// the same, but only if the channel doesn't have its own error. This returns -// the error code that should be sent to OnStopRequest. - -static nsresult -SyncChannelStatus(nsIChannel* channel, nsresult status) -{ - nsresult channelStatus; - channel->GetStatus(&channelStatus); - if (NS_FAILED(channelStatus)) - return channelStatus; - - if (NS_SUCCEEDED(status)) - return NS_OK; // caller and the channel are happy - - // channel was OK, but caller wasn't: set the channel state - channel->Cancel(status); - return status; -} - - -// nsNavBookmarks::ImportBookmarksHTML - -NS_IMETHODIMP -nsNavBookmarks::ImportBookmarksHTML(nsIURI* aURL) -{ - // this version is exposed on the interface and disallows changing of roots - return ImportBookmarksHTMLInternal(aURL, PR_FALSE, 0); -} - -NS_IMETHODIMP -nsNavBookmarks::ImportBookmarksHTMLToFolder(nsIURI* aURL, PRInt64 aFolder) -{ - // this version is exposed on the interface and disallows changing of roots - return ImportBookmarksHTMLInternal(aURL, PR_FALSE, aFolder); -} - -nsresult -nsNavBookmarks::ImportBookmarksHTMLInternal(nsIURI* aURL, - PRBool aAllowRootChanges, - PRInt64 aFolder) -{ - // wrap the import in a transaction to make it faster - mozStorageTransaction transaction(DBConn(), PR_FALSE); - -#ifdef DEBUG_IMPORT - nsCAutoString spec; - aURL->GetSpec(spec); - printf("\nImporting %s\n", spec.get()); -#endif - - nsresult rv; - nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<BookmarkContentSink> sink = new BookmarkContentSink; - NS_ENSURE_TRUE(sink, NS_ERROR_OUT_OF_MEMORY); - rv = sink->Init(aAllowRootChanges, this, aFolder); - NS_ENSURE_SUCCESS(rv, rv); - parser->SetContentSink(sink); - - // channel: note we have to set the content type or the default "unknown" type - // will confuse the parser - nsCOMPtr<nsIIOService> ioservice = do_GetIOService(&rv); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr<nsIChannel> channel; - rv = ioservice->NewChannelFromURI(aURL, getter_AddRefs(channel)); - NS_ENSURE_SUCCESS(rv, rv); - rv = channel->SetContentType(NS_LITERAL_CSTRING("text/html")); - NS_ENSURE_SUCCESS(rv, rv); - - // streams - nsCOMPtr<nsIInputStream> stream; - rv = channel->Open(getter_AddRefs(stream)); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr<nsIInputStream> bufferedstream; - rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedstream), stream, 4096); - NS_ENSURE_SUCCESS(rv, rv); - - // init parser -#ifdef MOZILLA_1_8_BRANCH - rv = parser->Parse(aURL, nsnull, PR_FALSE); -#else - rv = parser->Parse(aURL, nsnull); -#endif - NS_ENSURE_SUCCESS(rv, rv); - - // feed the parser the data - // Note: on error, we always need to set the channel's status to be the - // same, and to always call OnStopRequest with the channel error. - nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser, &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = listener->OnStartRequest(channel, nsnull); - rv = SyncChannelStatus(channel, rv); - while(NS_SUCCEEDED(rv)) - { - PRUint32 available; - rv = bufferedstream->Available(&available); - if (rv == NS_BASE_STREAM_CLOSED) { - rv = NS_OK; - available = 0; - } - if (NS_FAILED(rv)) { - channel->Cancel(rv); - break; - } - if (! available) - break; // blocking input stream has none available when done - - rv = listener->OnDataAvailable(channel, nsnull, bufferedstream, 0, available); - rv = SyncChannelStatus(channel, rv); - if (NS_FAILED(rv)) - break; - } - listener->OnStopRequest(channel, nsnull, rv); - transaction.Commit(); - return NS_OK; -} - -static char kFileIntro[] = - "<!DOCTYPE NETSCAPE-Bookmark-file-1>" NS_LINEBREAK - // Note: we write bookmarks in UTF-8 - "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">" NS_LINEBREAK - "<TITLE>Bookmarks" NS_LINEBREAK; -static const char kRootIntro[] = "

-// -// Goes after the container header (Write(aIndent.get(), aIndent.Length(), &dummy); - if (NS_FAILED(rv)) return rv; - rv = aOutput->Write(kBookmarkIntro, sizeof(kBookmarkIntro)-1, &dummy); - if (NS_FAILED(rv)) return rv; - return NS_OK; -} - - -// WriteContainerEpilogue -// -//

-// -// Goes after the container contents to close the container - -static nsresult -WriteContainerEpilogue(const nsCString& aIndent, nsIOutputStream* aOutput) -{ - PRUint32 dummy; - nsresult rv = aOutput->Write(aIndent.get(), aIndent.Length(), &dummy); - if (NS_FAILED(rv)) return rv; - rv = aOutput->Write(kBookmarkClose, sizeof(kBookmarkClose)-1, &dummy); - if (NS_FAILED(rv)) return rv; - return NS_OK; -} - - -// DataToDataURI - -static nsresult -DataToDataURI(PRUint8* aData, PRUint32 aDataLen, const nsACString& aMimeType, - nsACString& aDataURI) -{ - char* encoded = PL_Base64Encode(NS_REINTERPRET_CAST(const char*, aData), - aDataLen, nsnull); - if (! encoded) - return NS_ERROR_OUT_OF_MEMORY; - - aDataURI.AssignLiteral("data:"); - aDataURI.Append(aMimeType); - aDataURI.AppendLiteral(";base64,"); - aDataURI.Append(encoded); - - nsMemory::Free(encoded); - return NS_OK; -} - - -// WriteFaviconAttribute -// -// This writes the 'ICON="data:asdlfkjas;ldkfja;skdljfasdf"' attribute for -// an item. We special-case chrome favicon URIs by just writing the chrome: -// URI. - -static nsresult -WriteFaviconAttribute(const nsACString& aURI, nsIOutputStream* aOutput) -{ - nsresult rv; - PRUint32 dummy; - - nsFaviconService* faviconService = nsFaviconService::GetFaviconService(); - NS_ENSURE_TRUE(faviconService, NS_ERROR_UNEXPECTED); - - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), aURI); - NS_ENSURE_SUCCESS(rv, rv); - - // get favicon - nsCOMPtr faviconURI; - rv = faviconService->GetFaviconForPage(uri, getter_AddRefs(faviconURI)); - if (rv == NS_ERROR_NOT_AVAILABLE) - return NS_OK; // no favicon - NS_ENSURE_SUCCESS(rv, rv); // anything else is error - - nsCAutoString faviconScheme; - nsCAutoString faviconSpec; - rv = faviconURI->GetSpec(faviconSpec); - NS_ENSURE_SUCCESS(rv, rv); - rv = faviconURI->GetScheme(faviconScheme); - NS_ENSURE_SUCCESS(rv, rv); - - // write favicon URI: 'ICON_URI="..."' - rv = aOutput->Write(kIconURIAttribute, sizeof(kIconURIAttribute)-1, &dummy); - if (NS_FAILED(rv)) return rv; - rv = WriteEscapedUrl(faviconSpec, aOutput); - if (NS_FAILED(rv)) return rv; - rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy); - if (NS_FAILED(rv)) return rv; - - if (! faviconScheme.EqualsLiteral("chrome")) { - // only store data for non-chrome URIs - - // get the data - BE SURE TO FREE - nsCAutoString mimeType; - PRUint32 dataLen; - PRUint8* data; - rv = faviconService->GetFaviconData(faviconURI, mimeType, &dataLen, &data); - NS_ENSURE_SUCCESS(rv, rv); - if (dataLen > 0) { - // convert to URI - nsCString faviconContents; - rv = DataToDataURI(data, dataLen, mimeType, faviconContents); - nsMemory::Free(data); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aOutput->Write(kIconAttribute, sizeof(kIconAttribute)-1, &dummy); - if (NS_FAILED(rv)) return rv; - rv = aOutput->Write(faviconContents.get(), faviconContents.Length(), &dummy); - if (NS_FAILED(rv)) return rv; - rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy); - if (NS_FAILED(rv)) return rv; - } - } - return NS_OK; -} - - -// nsNavBookmarks::WriteContainer -// -// Writes out all the necessary parts of a bookmarks folder. - -nsresult -nsNavBookmarks::WriteContainer(PRInt64 aFolder, const nsCString& aIndent, - nsIOutputStream* aOutput) -{ - nsresult rv = WriteContainerHeader(aFolder, aIndent, aOutput); - if (NS_FAILED(rv)) return rv; - - // FIXME bug 334758: write container description here as a

- - rv = WriteContainerPrologue(aIndent, aOutput); - if (NS_FAILED(rv)) return rv; - rv = WriteContainerContents(aFolder, aIndent, aOutput); - if (NS_FAILED(rv)) return rv; - rv = WriteContainerEpilogue(aIndent, aOutput); - if (NS_FAILED(rv)) return rv; - return NS_OK; -} - - -// nsNavBookmarks::WriteContainerHeader -// -// This writes '

Title

' -// Remember folders can also have favicons, which we put in the H3 tag - -nsresult -nsNavBookmarks::WriteContainerHeader(PRInt64 aFolder, const nsCString& aIndent, - nsIOutputStream* aOutput) -{ - PRUint32 dummy; - nsresult rv; - - // indent - if (! aIndent.IsEmpty()) { - rv = aOutput->Write(aIndent.get(), aIndent.Length(), &dummy); - if (NS_FAILED(rv)) return rv; - } - - // "
Write(kContainerIntro, sizeof(kContainerIntro)-1, &dummy); - if (NS_FAILED(rv)) return rv; - - // " PERSONAL_TOOLBAR_FOLDER="true"", etc. - if (aFolder == mRoot) { - rv = aOutput->Write(kPlacesRootAttribute, sizeof(kPlacesRootAttribute)-1, &dummy); - if (NS_FAILED(rv)) return rv; - } else if (aFolder == mBookmarksRoot) { - rv = aOutput->Write(kBookmarksRootAttribute, sizeof(kBookmarksRootAttribute)-1, &dummy); - if (NS_FAILED(rv)) return rv; - } else if (aFolder == mToolbarRoot) { - rv = aOutput->Write(kToolbarRootAttribute, sizeof(kToolbarRootAttribute)-1, &dummy); - if (NS_FAILED(rv)) return rv; - } - - // favicon (most folders won't have one) - nsCOMPtr folderURI; - rv = GetFolderURI(aFolder, getter_AddRefs(folderURI)); - NS_ENSURE_SUCCESS(rv, rv); - nsCAutoString folderSpec; - rv = folderURI->GetSpec(folderSpec); - NS_ENSURE_SUCCESS(rv, rv); - rv = WriteFaviconAttribute(folderSpec, aOutput); - if (NS_FAILED(rv)) return rv; - - // ">" - rv = aOutput->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy); - if (NS_FAILED(rv)) return rv; - - // title - rv = WriteContainerTitle(aFolder, aOutput); - if (NS_FAILED(rv)) return rv; - - // "\n" - rv = aOutput->Write(kContainerClose, sizeof(kContainerClose)-1, &dummy); - return rv; -} - - -// nsNavBookmarks::WriteContainerTitle -// -// Retrieves, escaped and writes the container title to the stream. - -nsresult -nsNavBookmarks::WriteContainerTitle(PRInt64 aFolder, nsIOutputStream* aOutput) -{ - nsAutoString title; - nsresult rv = GetFolderTitle(aFolder, title); - NS_ENSURE_SUCCESS(rv, rv); - - char* escapedTitle = nsEscapeHTML(NS_ConvertUTF16toUTF8(title).get()); - if (escapedTitle) { - PRUint32 dummy; - rv = aOutput->Write(escapedTitle, strlen(escapedTitle), &dummy); - nsMemory::Free(escapedTitle); - if (NS_FAILED(rv)) return rv; - } - return NS_OK; -} - - -// WriteItem -// -// "
Name" - -static nsresult -WriteItem(nsNavHistoryResultNode* aItem, const nsCString& aIndent, - nsIOutputStream* aOutput) -{ - PRUint32 dummy; - nsresult rv; - - // indent - if (! aIndent.IsEmpty()) { - rv = aOutput->Write(aIndent.get(), aIndent.Length(), &dummy); - if (NS_FAILED(rv)) return rv; - } - - // '
Write(kItemOpen, sizeof(kItemOpen)-1, &dummy); - if (NS_FAILED(rv)) return rv; - - // ' HREF="http://..."' - note that we need to call GetURI on the result - // node because some nodes (eg queries) generate this lazily. - rv = aOutput->Write(kHrefAttribute, sizeof(kHrefAttribute)-1, &dummy); - if (NS_FAILED(rv)) return rv; - nsCAutoString uri; - rv = aItem->GetUri(uri); - NS_ENSURE_SUCCESS(rv, rv); - rv = WriteEscapedUrl(uri, aOutput); - if (NS_FAILED(rv)) return rv; - rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy); - if (NS_FAILED(rv)) return rv; - - // ' ICON="..."' - rv = WriteFaviconAttribute(uri, aOutput); - if (NS_FAILED(rv)) return rv; - - // FIXME bug 334408: write last character set here - - // '>' - rv = aOutput->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy); - if (NS_FAILED(rv)) return rv; - - // title - char* escapedTitle = nsEscapeHTML(aItem->mTitle.get()); - if (escapedTitle) { - rv = aOutput->Write(escapedTitle, strlen(escapedTitle), &dummy); - nsMemory::Free(escapedTitle); - if (NS_FAILED(rv)) return rv; - } - - // '\n' - rv = aOutput->Write(kItemClose, sizeof(kItemClose)-1, &dummy); - if (NS_FAILED(rv)) return rv; - - // FIXME bug 334758: write item description here as a
- - return NS_OK; -} - - -// WriteLivemark -// -// Similar to WriteItem, this has an additional FEEDURL attribute and -// the HREF is optional and points to the source page. - -nsresult -nsNavBookmarks::WriteLivemark(PRInt64 aFolderId, const nsCString& aIndent, - nsIOutputStream* aOutput) -{ - PRUint32 dummy; - nsresult rv; - - nsLivemarkService* livemarkService = nsLivemarkService::GetLivemarkService(); - NS_ENSURE_TRUE(livemarkService, NS_ERROR_UNEXPECTED); - - // indent - if (! aIndent.IsEmpty()) { - rv = aOutput->Write(aIndent.get(), aIndent.Length(), &dummy); - if (NS_FAILED(rv)) return rv; - } - - // '
Write(kItemOpen, sizeof(kItemOpen)-1, &dummy); - if (NS_FAILED(rv)) return rv; - - // get feed URI - nsCOMPtr feedURI; - rv = livemarkService->GetFeedURI(aFolderId, getter_AddRefs(feedURI)); - NS_ENSURE_SUCCESS(rv, rv); - nsCString feedSpec; - rv = feedURI->GetSpec(feedSpec); - NS_ENSURE_SUCCESS(rv, rv); - - // write feed URI - rv = aOutput->Write(kFeedURIAttribute, sizeof(kFeedURIAttribute)-1, &dummy); - if (NS_FAILED(rv)) return rv; - rv = WriteEscapedUrl(feedSpec, aOutput); - if (NS_FAILED(rv)) return rv; - rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy); - if (NS_FAILED(rv)) return rv; - - // get the optional site URI - nsCOMPtr siteURI; - rv = livemarkService->GetSiteURI(aFolderId, getter_AddRefs(siteURI)); - NS_ENSURE_SUCCESS(rv, rv); - if (siteURI) { - nsCString siteSpec; - rv = siteURI->GetSpec(siteSpec); - NS_ENSURE_SUCCESS(rv, rv); - - // write site URI - rv = aOutput->Write(kHrefAttribute, sizeof(kHrefAttribute)-1, &dummy); - if (NS_FAILED(rv)) return rv; - rv = WriteEscapedUrl(siteSpec, aOutput); - if (NS_FAILED(rv)) return rv; - rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy); - if (NS_FAILED(rv)) return rv; - } - - // '>' - rv = aOutput->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy); - if (NS_FAILED(rv)) return rv; - - // title - rv = WriteContainerTitle(aFolderId, aOutput); - if (NS_FAILED(rv)) return rv; - - // '\n' - rv = aOutput->Write(kItemClose, sizeof(kItemClose)-1, &dummy); - if (NS_FAILED(rv)) return rv; - - return NS_OK; -} - - -// WriteSeparator -// -// "
" - -nsresult -WriteSeparator(const nsCString& aIndent, nsIOutputStream* aOutput) -{ - PRUint32 dummy; - nsresult rv; - - // indent - if (! aIndent.IsEmpty()) { - rv = aOutput->Write(aIndent.get(), aIndent.Length(), &dummy); - if (NS_FAILED(rv)) return rv; - } - - rv = aOutput->Write(kSeparator, sizeof(kSeparator)-1, &dummy); - return rv; -} - - -// WriteEscapedUrl -// -// Writes the given string to the stream escaped as necessary for URLs. -// -// Unfortunately, the old bookmarks system uses a custom hardcoded and -// braindead escaping scheme that we need to emulate. It just replaces -// quotes with %22 and that's it. - -nsresult -WriteEscapedUrl(const nsCString& aString, nsIOutputStream* aOutput) -{ - nsCAutoString escaped(aString); - PRInt32 offset; - while ((offset = escaped.FindChar('\"')) >= 0) { - escaped.Cut(offset, 1); - escaped.Insert(NS_LITERAL_CSTRING("%22"), offset); - } - PRUint32 dummy; - return aOutput->Write(escaped.get(), escaped.Length(), &dummy); -} - - -// nsNavBookmarks::WriteContainerContents -// -// The indent here is the indent of the parent. We will add an additional -// indent before writing data. - -nsresult -nsNavBookmarks::WriteContainerContents(PRInt64 aFolder, const nsCString& aIndent, - nsIOutputStream* aOutput) -{ - nsCString myIndent = aIndent; - myIndent.Append(kIndent); - - nsNavHistory* historyService = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(historyService, NS_ERROR_UNEXPECTED); - - // get empty options - nsCOMPtr optionsInterface; - nsresult rv = historyService->GetNewQueryOptions(getter_AddRefs(optionsInterface)); - NS_ENSURE_SUCCESS(rv, rv); - - // QueryFolderChildren requires a concrete options - nsCOMPtr options = do_QueryInterface(optionsInterface); - NS_ENSURE_TRUE(options, NS_ERROR_UNEXPECTED); - - // get contents of folder - nsCOMArray items; - rv = QueryFolderChildren(aFolder, options, &items); - NS_ENSURE_SUCCESS(rv, rv); - - // write each item - for (PRInt32 i = 0; i < items.Count(); i ++) { - if (items[i]->IsFolder()) { - // bookmarks folder - PRInt64 folderId = items[i]->GetAsFolder()->mFolderId; - if (aFolder == mRoot && (folderId == mToolbarRoot || - folderId == mBookmarksRoot)) { - // don't write out the bookmarks menu or the toolbar folder from the - // places root. When writing to bookmarks.html, these are reparented - // to the menu, which is the root of the namespace. This provides - // better backwards compatability. - continue; - } - - // it could be a regular folder or it could be a livemark - nsLivemarkService* livemarkService = nsLivemarkService::GetLivemarkService(); - NS_ENSURE_TRUE(livemarkService, NS_ERROR_UNEXPECTED); - PRBool isLivemark; - rv = livemarkService->IsLivemark(folderId, &isLivemark); - NS_ENSURE_SUCCESS(rv, rv); - - if (isLivemark) - rv = WriteLivemark(folderId, myIndent, aOutput); - else - rv = WriteContainer(folderId, myIndent, aOutput); - } else if (items[i]->IsSeparator()) { - rv = WriteSeparator(myIndent, aOutput); - } else { - rv = WriteItem(items[i], myIndent, aOutput); - } - if (NS_FAILED(rv)) return rv; - } - return NS_OK; -} - - -// nsNavBookmarks::ExportBookmarksHTML -// - -NS_IMETHODIMP -nsNavBookmarks::ExportBookmarksHTML(nsIFile* aBookmarksFile) -{ - if (! aBookmarksFile) - return NS_ERROR_NULL_POINTER; - - // get a safe output stream, so we don't clobber the bookmarks file unless - // all the writes succeeded. - nsCOMPtr out; - nsresult rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out), - aBookmarksFile, - PR_WRONLY | PR_CREATE_FILE, - /*octal*/ 0600, - 0); - if (NS_FAILED(rv)) return rv; - - // We need a buffered output stream for performance. - // See bug 202477. - nsCOMPtr strm; - rv = NS_NewBufferedOutputStream(getter_AddRefs(strm), out, 4096); - if (NS_FAILED(rv)) return rv; - - // file header - PRUint32 dummy; - rv = strm->Write(kFileIntro, sizeof(kFileIntro)-1, &dummy); - if (NS_FAILED(rv)) return rv; - - // 'Write(kRootIntro, sizeof(kRootIntro)-1, &dummy); //

folderURI; - rv = GetFolderURI(mBookmarksRoot, getter_AddRefs(folderURI)); - NS_ENSURE_SUCCESS(rv, rv); - nsCAutoString folderSpec; - rv = folderURI->GetSpec(folderSpec); - NS_ENSURE_SUCCESS(rv, rv); - rv = WriteFaviconAttribute(folderSpec, strm); - if (NS_FAILED(rv)) return rv; - - // '>Bookmarks

- rv = strm->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy); // > - if (NS_FAILED(rv)) return rv; - rv = WriteContainerTitle(mBookmarksRoot, strm); - if (NS_FAILED(rv)) return rv; - rv = strm->Write(kCloseRootH1, sizeof(kCloseRootH1)-1, &dummy); // - if (NS_FAILED(rv)) return rv; - - // prologue - rv = WriteContainerPrologue(EmptyCString(), strm); - if (NS_FAILED(rv)) return rv; - - // indents - nsCAutoString indent; - indent.Assign(kIndent); - - // places root - rv = WriteContainer(mRoot, indent, strm); - if (NS_FAILED(rv)) return rv; - - // toolbar - rv = WriteContainer(mToolbarRoot, indent, strm); - if (NS_FAILED(rv)) return rv; - - // bookmarks menu contents - rv = WriteContainerContents(mBookmarksRoot, EmptyCString(), strm); - if (NS_FAILED(rv)) return rv; - - // epilogue - rv = WriteContainerEpilogue(EmptyCString(), strm); - if (NS_FAILED(rv)) return rv; - - // commit the write - nsCOMPtr safeStream = do_QueryInterface(strm, &rv); - NS_ENSURE_SUCCESS(rv, rv); - return safeStream->Finish(); -} diff --git a/mozilla/browser/components/places/src/nsFaviconService.cpp b/mozilla/browser/components/places/src/nsFaviconService.cpp deleted file mode 100644 index c15ef7b6027..00000000000 --- a/mozilla/browser/components/places/src/nsFaviconService.cpp +++ /dev/null @@ -1,951 +0,0 @@ -/* -*- 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): - * Brett Wilson (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 ***** */ - -/** - * This is the favicon service, which stores favicons for web pages with your - * history as you browse. It is also used to save the favicons for bookmarks. - * - * DANGER: The history query system makes assumptions about the favicon storage - * so that icons can be quickly generated for history/bookmark result sets. If - * you change the database layout at all, you will have to update both services. - */ - -#include "nsFaviconService.h" -#include "nsICacheVisitor.h" -#include "nsICachingChannel.h" -#include "nsICategoryManager.h" -#include "nsIChannelEventSink.h" -#include "nsIContentSniffer.h" -#include "nsIInterfaceRequestor.h" -#include "nsIStreamListener.h" -#include "nsISupportsPrimitives.h" -#include "nsNavBookmarks.h" -#include "nsNavHistory.h" -#include "nsNetUtil.h" -#include "nsReadableUtils.h" -#include "nsStreamUtils.h" -#include "mozStorageHelper.h" - -// This is the maximum favicon size that we will bother storing. Most icons -// are about 4K. Some people add 32x32 versions at different bit depths, -// making them much bigger. It would be nice if could extract just the 16x16 -// version that we need. Instead, we'll just store everything below this -// sanity threshold. -#define MAX_FAVICON_SIZE 32768 - -#define FAVICON_BUFFER_INCREMENT 8192 - -#define MAX_FAVICON_CACHE_SIZE 512 -#define FAVICON_CACHE_REDUCE_COUNT 64 - -#define CONTENT_SNIFFING_SERVICES "content-sniffing-services" - - -class FaviconLoadListener : public nsIStreamListener, - public nsIInterfaceRequestor, - public nsIChannelEventSink -{ -public: - FaviconLoadListener(nsFaviconService* aFaviconService, - nsIURI* aPageURI, nsIURI* aFaviconURI, - nsIChannel* aChannel); - - NS_DECL_ISUPPORTS - NS_DECL_NSIREQUESTOBSERVER - NS_DECL_NSISTREAMLISTENER - NS_DECL_NSIINTERFACEREQUESTOR - NS_DECL_NSICHANNELEVENTSINK - -private: - ~FaviconLoadListener(); - - nsCOMPtr mFaviconService; - nsCOMPtr mChannel; - nsCOMPtr mPageURI; - nsCOMPtr mFaviconURI; - - nsCString mData; -}; - - -nsFaviconService* nsFaviconService::gFaviconService; - -NS_IMPL_ISUPPORTS1(nsFaviconService, nsIFaviconService) - -// nsFaviconService::nsFaviconService - -nsFaviconService::nsFaviconService() : mFailedFaviconSerial(0) -{ - NS_ASSERTION(! gFaviconService, "ATTEMPTING TO CREATE TWO FAVICON SERVICES!"); - gFaviconService = this; -} - - -// nsFaviconService::~nsFaviconService - -nsFaviconService::~nsFaviconService() -{ - NS_ASSERTION(gFaviconService == this, "Deleting a non-singleton favicon service"); - if (gFaviconService == this) - gFaviconService = nsnull; -} - - -// nsFaviconService::Init -// -// Called when the service is created. - -nsresult -nsFaviconService::Init() -{ - nsresult rv; - - nsNavHistory* historyService = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(historyService, NS_ERROR_OUT_OF_MEMORY); - mDBConn = historyService->GetStorageConnection(); - NS_ENSURE_TRUE(mDBConn, NS_ERROR_FAILURE); - - // creation of history service will have called InitTables before now - - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("SELECT id, length(data), expiration FROM moz_favicon WHERE url = ?1"), - getter_AddRefs(mDBGetIconInfo)); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("SELECT f.id, f.url, length(f.data), f.expiration FROM moz_history h JOIN moz_favicon f ON h.favicon = f.id WHERE h.url = ?1"), - getter_AddRefs(mDBGetURL)); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("SELECT f.data, f.mime_type FROM moz_favicon f WHERE url = ?1"), - getter_AddRefs(mDBGetData)); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_favicon (url, data, mime_type, expiration) VALUES (?1, ?2, ?3, ?4)"), - getter_AddRefs(mDBInsertIcon)); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("UPDATE moz_favicon SET data = ?2, mime_type = ?3, expiration = ?4 where id = ?1"), - getter_AddRefs(mDBUpdateIcon)); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("UPDATE moz_history SET favicon = ?2 WHERE id = ?1"), - getter_AddRefs(mDBSetPageFavicon)); - NS_ENSURE_SUCCESS(rv, rv); - - // failed favicon cache - if (! mFailedFavicons.Init(256)) - return NS_ERROR_OUT_OF_MEMORY; - - return NS_OK; -} - - -// nsFaviconService::InitTables -// -// Called by the history service to create the favicon table. The history -// service uses this table in its queries, so it must exist even if -// nobody has called the favicon service. - -nsresult // static -nsFaviconService::InitTables(mozIStorageConnection* aDBConn) -{ - nsresult rv; - PRBool exists = PR_FALSE; - aDBConn->TableExists(NS_LITERAL_CSTRING("moz_favicon"), &exists); - if (! exists) { - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_favicon (id INTEGER PRIMARY KEY, url LONGVARCHAR UNIQUE, data BLOB, mime_type VARCHAR(32), expiration LONG)")); - NS_ENSURE_SUCCESS(rv, rv); - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE INDEX moz_favicon_url ON moz_favicon (url)")); - NS_ENSURE_SUCCESS(rv, rv); - } - return NS_OK; -} - - -// nsFaviconService::SetFaviconUrlForPage - -NS_IMETHODIMP -nsFaviconService::SetFaviconUrlForPage(nsIURI* aPage, nsIURI* aFavicon) -{ - // we don't care whether there was data or what the expiration was - PRBool hasData; - PRTime expiration; - nsresult rv = SetFaviconUrlForPageInternal(aPage, aFavicon, - &hasData, &expiration); - NS_ENSURE_SUCCESS(rv, rv); - - // send favicon change notifications if the URL has any data - if (hasData) - SendFaviconNotifications(aPage, aFavicon); - return NS_OK; -} - - -// nsFaviconService::SetFaviconUrlForPageInternal -// -// This creates a new entry in the favicon table if necessary and tells the -// history service to associate the given favicon ID with the given URL. We -// don't want to update the history table directly since that may involve -// creating a new row in the history table, which should only be done by -// history. -// -// This sets aHasData if there was already icon data for this favicon. Used -// to know if we should try reloading. -// -// Expiration will be 0 if the icon has not been set yet. -// -// Does NOT send out notifications. Caller should send out notifications -// if the favicon has data. - -nsresult -nsFaviconService::SetFaviconUrlForPageInternal(nsIURI* aPage, nsIURI* aFavicon, - PRBool* aHasData, - PRTime* aExpiration) -{ - mozStorageStatementScoper scoper(mDBGetIconInfo); - mozStorageTransaction transaction(mDBConn, PR_FALSE); - nsresult rv = BindStatementURI(mDBGetIconInfo, 0, aFavicon); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasResult; - rv = mDBGetIconInfo->ExecuteStep(&hasResult); - NS_ENSURE_SUCCESS(rv, rv); - - PRInt64 iconId; - if (hasResult) { - // have an entry for this icon already, just get the stats - rv = mDBGetIconInfo->GetInt64(0, &iconId); - NS_ENSURE_SUCCESS(rv, rv); - - // see if there's data already - PRInt32 dataSize; - rv = mDBGetIconInfo->GetInt32(1, &dataSize); - NS_ENSURE_SUCCESS(rv, rv); - if (dataSize > 0) - *aHasData = PR_TRUE; - - // expiration - rv = mDBGetIconInfo->GetInt64(2, aExpiration); - NS_ENSURE_SUCCESS(rv, rv); - - mDBGetIconInfo->Reset(); - scoper.Abandon(); - } else { - // create a new icon entry - mDBGetIconInfo->Reset(); - scoper.Abandon(); - - *aHasData = PR_FALSE; - *aExpiration = 0; - - // no entry for this icon yet, create it - mozStorageStatementScoper scoper2(mDBInsertIcon); - rv = BindStatementURI(mDBInsertIcon, 0, aFavicon); - NS_ENSURE_SUCCESS(rv, rv); - mDBInsertIcon->BindNullParameter(1); - mDBInsertIcon->BindNullParameter(2); - mDBInsertIcon->BindNullParameter(3); - rv = mDBInsertIcon->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBConn->GetLastInsertRowID(&iconId); - NS_ENSURE_SUCCESS(rv, rv); - } - - // now link our entry with the history service - nsNavHistory* historyService = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(historyService, NS_ERROR_NO_INTERFACE); - - PRInt64 pageId; - rv = historyService->GetUrlIdFor(aPage, &pageId, PR_TRUE); - NS_ENSURE_SUCCESS(rv, rv); - - mozStorageStatementScoper scoper2(mDBSetPageFavicon); - rv = mDBSetPageFavicon->BindInt64Parameter(0, pageId); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBSetPageFavicon->BindInt64Parameter(1, iconId); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBSetPageFavicon->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - transaction.Commit(); - return NS_OK; -} - - -// nsFaviconService::UpdateBookmarkRedirectFavicon -// -// It is not uncommon to have a bookmark (usually manually entered or -// modified) that redirects to some other page. For example, "mozilla.org" -// redirects to "www.mozilla.org". We want that bookmark's favicon to get -// updated. So, we see if this URI has a bookmark redirect and set the -// favicon there as well. -// -// This should be called only when we know there is data for the favicon -// already loaded. We will always send out notifications for the bookmarked -// page. - -nsresult -nsFaviconService::UpdateBookmarkRedirectFavicon(nsIURI* aPage, nsIURI* aFavicon) -{ - NS_ENSURE_TRUE(aPage, NS_ERROR_INVALID_ARG); - NS_ENSURE_TRUE(aFavicon, NS_ERROR_INVALID_ARG); - - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_UNEXPECTED); - - nsCOMPtr bookmarkURI; - nsresult rv = bookmarks->GetBookmarkedURIFor(aPage, getter_AddRefs(bookmarkURI)); - NS_ENSURE_SUCCESS(rv, rv); - if (! bookmarkURI) - return NS_OK; // no bookmark redirect - - PRBool sameAsBookmark; - if (NS_SUCCEEDED(bookmarkURI->Equals(aPage, &sameAsBookmark)) && sameAsBookmark) - return NS_OK; // bookmarked directly, not through a redirect - - PRBool hasData = PR_FALSE; - PRTime expiration = 0; - rv = SetFaviconUrlForPageInternal(bookmarkURI, aFavicon, &hasData, &expiration); - NS_ENSURE_SUCCESS(rv, rv); - - if (hasData) { - // send notifications - SendFaviconNotifications(bookmarkURI, aFavicon); - } else { - NS_WARNING("Calling UpdateBookmarkRedirectFavicon when you don't have data for the favicon yet."); - } - return NS_OK; -} - - -// nsFaviconService::SendFaviconNotifications -// -// Call to send out favicon changed notifications. Shuold only be called -// when you know there is data loaded for the favicon. - -void -nsFaviconService::SendFaviconNotifications(nsIURI* aPage, nsIURI* aFaviconURI) -{ - nsCAutoString faviconSpec; - nsNavHistory* historyService = nsNavHistory::GetHistoryService(); - if (historyService && NS_SUCCEEDED(aFaviconURI->GetSpec(faviconSpec))) { - historyService->SendPageChangedNotification(aPage, - nsINavHistoryObserver::ATTRIBUTE_FAVICON, - NS_ConvertUTF8toUTF16(faviconSpec)); - } -} - - -// nsFaviconService::SetAndLoadFaviconForPage - -NS_IMETHODIMP -nsFaviconService::SetAndLoadFaviconForPage(nsIURI* aPage, nsIURI* aFavicon, - PRBool aForceReload) -{ -#ifdef LAZY_ADD - nsNavHistory* historyService = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(historyService, NS_ERROR_OUT_OF_MEMORY); - return historyService->AddLazyLoadFaviconMessage(aPage, aFavicon, - aForceReload); -#else - return DoSetAndLoadFaviconForPage(aPage, aFavicon, aForceReload); -#endif -} - - -// nsFaviconService::DoSetAndLoadFaviconForPage - -nsresult -nsFaviconService::DoSetAndLoadFaviconForPage(nsIURI* aPage, nsIURI* aFavicon, - PRBool aForceReload) -{ - nsCOMPtr page(aPage); - - // don't load favicons when history is disabled - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_FAILURE); - if (history->IsHistoryDisabled()) { - // history is disabled - check to see if this favicon could be for a - // bookmark - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_UNEXPECTED); - - nsCOMPtr bookmarkURI; - nsresult rv = bookmarks->GetBookmarkedURIFor(aPage, - getter_AddRefs(bookmarkURI)); - NS_ENSURE_SUCCESS(rv, rv); - if (! bookmarkURI) { - // page is not bookmarked, don't save favicon - return NS_OK; - } - - // page is bookmarked, set the URI to be the bookmark, the bookmark URI - // might be different than our input URI if there was a redirect, so - // always use the bookmark URI here to avoid setting the favicon for - // non-bookmarked pages. - page = bookmarkURI; - } - - // check the failed favicon cache - PRBool previouslyFailed; - nsresult rv = IsFailedFavicon(aFavicon, &previouslyFailed); - NS_ENSURE_SUCCESS(rv, rv); - if (previouslyFailed) { - if (aForceReload) - RemoveFailedFavicon(aFavicon); // force reload clears from failed cache - else - return NS_OK; // ignore previously failed favicons - } - - // filter out bad URLs - PRBool canAdd; - rv = history->CanAddURI(page, &canAdd); - NS_ENSURE_SUCCESS(rv, rv); - if (! canAdd) - return NS_OK; // ignore favicons for this url - - // If have an image loaded in the main frame, that image will get set as its - // own favicon. It would be nice to store a resampled version of the image, - // but that's prohibitive for now. This workaround just refuses to save the - // favicon in this case. - PRBool pageEqualsFavicon; - rv = page->Equals(aFavicon, &pageEqualsFavicon); - NS_ENSURE_SUCCESS(rv, rv); - if (pageEqualsFavicon) - return NS_OK; - - // ignore data URL favicons. The main case here is the error page, which uses - // a data URL as the favicon. The result is that we get the data URL in the - // favicon table associated with the decoded version of the data URL. - PRBool isDataURL; - rv = aFavicon->SchemeIs("data", &isDataURL); - NS_ENSURE_SUCCESS(rv, rv); - if (isDataURL) - return NS_OK; - - // See if we have data and get the expiration time for this favicon. It might - // be nice to say SetFaviconUrlForPageInternal here but we DON'T want to set - // the favicon for the page unless we know we have it. For example, if I go - // to random site x.com, the browser will still tell us that the favicon is - // x.com/favicon.ico even if there is no such file. We don't want to pollute - // our tables with this useless data. - PRBool hasData = PR_FALSE; - PRTime expiration = 0; - { // scope for statement - mozStorageStatementScoper scoper(mDBGetIconInfo); - rv = BindStatementURI(mDBGetIconInfo, 0, aFavicon); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMatch; - rv = mDBGetIconInfo->ExecuteStep(&hasMatch); - NS_ENSURE_SUCCESS(rv, rv); - if (hasMatch) { - PRInt32 dataSize; - mDBGetIconInfo->GetInt32(1, &dataSize); - hasData = dataSize > 0; - mDBGetIconInfo->GetInt64(2, &expiration); - } - } - - // See if this favicon has expired yet. We don't want to waste time reloading - // from the web or cache if we have a recent version. - PRTime now = PR_Now(); - if (hasData && now < expiration && ! aForceReload) { - // data still valid, no need to reload - - // For page revisits (pretty common) we DON'T want to send out any - // notifications if we've already set the favicon. These notifications will - // cause parts of the UI to update and will be slow. This also saves us a - // database write in these cases. - nsCOMPtr oldFavicon; - PRBool faviconsEqual; - if (NS_SUCCEEDED(GetFaviconForPage(page, getter_AddRefs(oldFavicon))) && - NS_SUCCEEDED(aFavicon->Equals(oldFavicon, &faviconsEqual)) && - faviconsEqual) - return NS_OK; // already set - - // This will associate the favicon URL with the page. - rv = SetFaviconUrlForPageInternal(page, aFavicon, &hasData, &expiration); - NS_ENSURE_SUCCESS(rv, rv); - - SendFaviconNotifications(page, aFavicon); - UpdateBookmarkRedirectFavicon(page, aFavicon); - return NS_OK; - } - - nsCOMPtr ioservice = do_GetIOService(&rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr channel; - rv = ioservice->NewChannelFromURI(aFavicon, getter_AddRefs(channel)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr listener = - new FaviconLoadListener(this, page, aFavicon, channel); - NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY); - nsCOMPtr listenerRequestor = - do_QueryInterface(listener, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = channel->SetNotificationCallbacks(listenerRequestor); - NS_ENSURE_SUCCESS(rv, rv); - rv = channel->AsyncOpen(listener, nsnull); - NS_ENSURE_SUCCESS(rv, rv); - - // DB will be update and observers will be notified when the data has - // finished loading - return NS_OK; -} - - -// nsFaviconService::SetFaviconData -// -// See the IDL for this function for lots of info. Note from there: we don't -// send out notifications. - -NS_IMETHODIMP -nsFaviconService::SetFaviconData(nsIURI* aFavicon, const PRUint8* aData, - PRUint32 aDataLen, const nsACString& aMimeType, - PRTime aExpiration) -{ - nsresult rv; - mozIStorageStatement* statement; - { - // this block forces the scoper to reset our statement: necessary for the - // next statement - mozStorageStatementScoper scoper(mDBGetIconInfo); - rv = BindStatementURI(mDBGetIconInfo, 0, aFavicon); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasResult; - rv = mDBGetIconInfo->ExecuteStep(&hasResult); - NS_ENSURE_SUCCESS(rv, rv); - - if (hasResult) { - // update old one (statement parameter 0 = ID) - PRInt64 id; - rv = mDBGetIconInfo->GetInt64(0, &id); - NS_ENSURE_SUCCESS(rv, rv); - statement = mDBUpdateIcon; - rv = statement->BindInt64Parameter(0, id); - } else { - // insert new one (statement parameter 0 = favicon URL) - statement = mDBInsertIcon; - rv = BindStatementURI(statement, 0, aFavicon); - } - NS_ENSURE_SUCCESS(rv, rv); - } - - mozStorageStatementScoper scoper(statement); - - // the insert and update statements share all but the 0th parameter - rv = statement->BindBlobParameter(1, aData, aDataLen); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindUTF8StringParameter(2, aMimeType); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindInt64Parameter(3, aExpiration); - NS_ENSURE_SUCCESS(rv, rv); - return statement->Execute(); -} - - -// nsFaviconService::GetFaviconData - -NS_IMETHODIMP -nsFaviconService::GetFaviconData(nsIURI* aFavicon, nsACString& aMimeType, - PRUint32* aDataLen, PRUint8** aData) -{ - mozStorageStatementScoper scoper(mDBGetData); - nsresult rv = BindStatementURI(mDBGetData, 0, aFavicon); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasResult; - rv = mDBGetData->ExecuteStep(&hasResult); - NS_ENSURE_SUCCESS(rv, rv); - if (! hasResult) - return NS_ERROR_NOT_AVAILABLE; - - rv = mDBGetData->GetUTF8String(1, aMimeType); - NS_ENSURE_SUCCESS(rv, rv); - return mDBGetData->GetBlob(0, aDataLen, aData); -} - - -// nsFaviconService::GetFaviconForPage - -NS_IMETHODIMP -nsFaviconService::GetFaviconForPage(nsIURI* aPage, nsIURI** _retval) -{ - mozStorageStatementScoper scoper(mDBGetURL); - nsresult rv = BindStatementURI(mDBGetURL, 0, aPage); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasResult; - rv = mDBGetURL->ExecuteStep(&hasResult); - NS_ENSURE_SUCCESS(rv, rv); - if (! hasResult) - return NS_ERROR_NOT_AVAILABLE; - - nsCAutoString url; - rv = mDBGetURL->GetUTF8String(1, url); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_NewURI(_retval, url); -} - - -// nsFaviconService::GetFaviconImageForPage - -NS_IMETHODIMP -nsFaviconService::GetFaviconImageForPage(nsIURI* aPage, nsIURI** _retval) -{ - mozStorageStatementScoper scoper(mDBGetURL); - nsresult rv = BindStatementURI(mDBGetURL, 0, aPage); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasResult; - rv = mDBGetURL->ExecuteStep(&hasResult); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr faviconURI; - if (hasResult) - { - PRInt32 dataLen; - rv = mDBGetURL->GetInt32(2, &dataLen); - NS_ENSURE_SUCCESS(rv, rv); - if (dataLen > 0) { - // this page has a favicon entry with data - nsCAutoString favIconUri; - rv = mDBGetURL->GetUTF8String(1, favIconUri); - NS_ENSURE_SUCCESS(rv, rv); - - return GetFaviconLinkForIconString(favIconUri, _retval); - } - } - - // not found, use default - if (! mDefaultIcon) { - nsresult rv = NS_NewURI(getter_AddRefs(mDefaultIcon), - NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL)); - NS_ENSURE_SUCCESS(rv, rv); - } - return mDefaultIcon->Clone(_retval); -} - - -// nsFaviconService::GetFaviconLinkForIcon - -nsresult -nsFaviconService::GetFaviconLinkForIcon(nsIURI* aIcon, nsIURI** aOutput) -{ - nsCAutoString spec; - if (aIcon) { - nsresult rv = aIcon->GetSpec(spec); - NS_ENSURE_SUCCESS(rv, rv); - } - return GetFaviconLinkForIconString(spec, aOutput); -} - - -// nsFaviconService::AddFailedFavicon - -PR_STATIC_CALLBACK(PLDHashOperator) -ExpireFailedFaviconsCallback(nsCStringHashKey::KeyType aKey, - PRUint32& aData, - void* userArg) -{ - PRUint32* threshold = NS_REINTERPRET_CAST(PRUint32*, userArg); - if (aData < *threshold) - return PL_DHASH_REMOVE; - return PL_DHASH_NEXT; -} - -NS_IMETHODIMP -nsFaviconService::AddFailedFavicon(nsIURI* aIcon) -{ - nsCAutoString spec; - nsresult rv = aIcon->GetSpec(spec); - NS_ENSURE_SUCCESS(rv, rv); - - if (! mFailedFavicons.Put(spec, mFailedFaviconSerial)) - return NS_ERROR_OUT_OF_MEMORY; - mFailedFaviconSerial ++; - - if (mFailedFavicons.Count() > MAX_FAVICON_CACHE_SIZE) { - // need to expire some entries, delete the FAVICON_CACHE_REDUCE_COUNT number - // of items that are the oldest - PRUint32 threshold = mFailedFaviconSerial - - MAX_FAVICON_CACHE_SIZE + FAVICON_CACHE_REDUCE_COUNT; - mFailedFavicons.Enumerate(ExpireFailedFaviconsCallback, &threshold); - } - return NS_OK; -} - - -// nsFaviconService::RemoveFailedFavicon - -NS_IMETHODIMP -nsFaviconService::RemoveFailedFavicon(nsIURI* aIcon) -{ - nsCAutoString spec; - nsresult rv = aIcon->GetSpec(spec); - NS_ENSURE_SUCCESS(rv, rv); - - // we silently do nothing and succeed if the icon is not in the cache - mFailedFavicons.Remove(spec); - return NS_OK; -} - - -// nsFaviconService::IsFailedFavicon - -NS_IMETHODIMP -nsFaviconService::IsFailedFavicon(nsIURI* aIcon, PRBool* _retval) -{ - nsCAutoString spec; - nsresult rv = aIcon->GetSpec(spec); - NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 serial; - *_retval = mFailedFavicons.Get(spec, &serial); - return NS_OK; -} - - -// nsFaviconService::GetFaviconLinkForIconString -// -// This computes a favicon URL with string input and using the cached -// default one to minimize parsing. - -nsresult -nsFaviconService::GetFaviconLinkForIconString(const nsCString& aSpec, nsIURI** aOutput) -{ - if (aSpec.IsEmpty()) { - // default icon for empty strings - if (! mDefaultIcon) { - nsresult rv = NS_NewURI(getter_AddRefs(mDefaultIcon), - NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL)); - NS_ENSURE_SUCCESS(rv, rv); - } - return mDefaultIcon->Clone(aOutput); - } - - if (StringBeginsWith(aSpec, NS_LITERAL_CSTRING("chrome:"))) { - // pass through for chrome URLs, since they can be referenced without - // this service - return NS_NewURI(aOutput, aSpec); - } - - nsCAutoString annoUri; - annoUri.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME ":"); - annoUri += aSpec; - return NS_NewURI(aOutput, annoUri); -} - - -// nsFaviconService::GetFaviconSpecForIconString -// -// This computes a favicon spec for when you don't want a URI object (as in -// the tree view implementation), sparing all parsing and normalization. - -void -nsFaviconService::GetFaviconSpecForIconString(const nsCString& aSpec, nsACString& aOutput) -{ - if (aSpec.IsEmpty()) { - aOutput.AssignLiteral(FAVICON_DEFAULT_URL); - } else if (StringBeginsWith(aSpec, NS_LITERAL_CSTRING("chrome:"))) { - aOutput = aSpec; - } else { - aOutput.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME ":"); - aOutput += aSpec; - } -} - - -NS_IMPL_ISUPPORTS3(FaviconLoadListener, - nsIStreamListener, // is a nsIRequestObserver - nsIInterfaceRequestor, - nsIChannelEventSink) - -// FaviconLoadListener::FaviconLoadListener - -FaviconLoadListener::FaviconLoadListener(nsFaviconService* aFaviconService, - nsIURI* aPageURI, nsIURI* aFaviconURI, - nsIChannel* aChannel) : - mFaviconService(aFaviconService), - mChannel(aChannel), - mPageURI(aPageURI), - mFaviconURI(aFaviconURI) -{ -} - - -// FaviconLoadListener::~FaviconLoadListener - -FaviconLoadListener::~FaviconLoadListener() -{ -} - - -// FaviconLoadListener::OnStartRequest (nsIRequestObserver) - -NS_IMETHODIMP -FaviconLoadListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) -{ - return NS_OK; -} - - -// FaviconLoadListener::OnStopRequest (nsIRequestObserver) - -NS_IMETHODIMP -FaviconLoadListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext, - nsresult aStatusCode) -{ - if (NS_FAILED(aStatusCode) || mData.Length() == 0) { - // load failed, add to failed cache - mFaviconService->AddFailedFavicon(mFaviconURI); - return NS_OK; - } - - // sniff the MIME type - nsresult rv; - nsCOMPtr categoryManager = - do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr sniffers; - rv = categoryManager->EnumerateCategory(CONTENT_SNIFFING_SERVICES, - getter_AddRefs(sniffers)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCString mimeType; - PRBool hasMore = PR_FALSE; - while (mimeType.IsEmpty() && NS_SUCCEEDED(sniffers->HasMoreElements(&hasMore)) - && hasMore) { - nsCOMPtr snifferCIDSupports; - rv = sniffers->GetNext(getter_AddRefs(snifferCIDSupports)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr snifferCIDSupportsCString = - do_QueryInterface(snifferCIDSupports, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsCAutoString snifferCID; - rv = snifferCIDSupportsCString->GetData(snifferCID); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr sniffer = do_GetService(snifferCID.get(), &rv); - NS_ENSURE_SUCCESS(rv, rv); - - sniffer->GetMIMETypeFromContent( -#ifndef MOZILLA_1_8_BRANCH - aRequest, -#endif - NS_REINTERPRET_CAST(PRUint8*, NS_CONST_CAST(char*, mData.get())), - mData.Length(), mimeType); - // ignore errors: mime type will be left empty and we'll try the next sniffer - } - - if (mimeType.IsEmpty()) { - // we can not handle favicons that do not have a recognisable MIME type - mFaviconService->AddFailedFavicon(mFaviconURI); - return NS_OK; - } - - // Expire this favicon in one day. An old version of this actually extracted - // the expiration time from the cache. But what if people (especially web - // developers) get a bad favicon or change it? The problem is that we're not - // aware when the icon has been reloaded in the cache or cleared. This way - // we'll always pick up any changes in the cache after a day. In most cases - // re-set favicons will come from the cache anyway and reloading them is not - // very expensive. - PRTime expiration = PR_Now() + - (PRInt64)(24 * 60 * 60) * (PRInt64)PR_USEC_PER_SEC; - - // save the favicon data - rv = mFaviconService->SetFaviconData(mFaviconURI, - NS_REINTERPRET_CAST(PRUint8*, NS_CONST_CAST(char*, mData.get())), - mData.Length(), mimeType, expiration); - NS_ENSURE_SUCCESS(rv, rv); - - // set the favicon for the page - PRBool hasData; - rv = mFaviconService->SetFaviconUrlForPageInternal(mPageURI, mFaviconURI, - &hasData, &expiration); - NS_ENSURE_SUCCESS(rv, rv); - - mFaviconService->SendFaviconNotifications(mPageURI, mFaviconURI); - mFaviconService->UpdateBookmarkRedirectFavicon(mPageURI, mFaviconURI); - return NS_OK; -} - - -// FaviconLoadListener::OnDataAvailable (nsIStreamListener) - -NS_IMETHODIMP -FaviconLoadListener::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext, - nsIInputStream *aInputStream, - PRUint32 aOffset, PRUint32 aCount) -{ - if (aOffset + aCount > MAX_FAVICON_SIZE) - return NS_ERROR_FAILURE; // too big - - nsCString buffer; - nsresult rv = NS_ConsumeStream(aInputStream, aCount, buffer); - if (rv != NS_BASE_STREAM_WOULD_BLOCK && NS_FAILED(rv)) - return rv; - - mData.Append(buffer); - return NS_OK; -} - - -// FaviconLoadListener::GetInterface (nsIInterfaceRequestor) - -NS_IMETHODIMP -FaviconLoadListener::GetInterface(const nsIID& uuid, void** aResult) -{ - return QueryInterface(uuid, aResult); -} - - -// FaviconLoadListener::OnChannelRedirect (nsIChannelEventSink) - -NS_IMETHODIMP -FaviconLoadListener::OnChannelRedirect(nsIChannel* oldChannel, - nsIChannel* newChannel, PRUint32 flags) -{ - mChannel = newChannel; - return NS_OK; -} - - diff --git a/mozilla/browser/components/places/src/nsFaviconService.h b/mozilla/browser/components/places/src/nsFaviconService.h deleted file mode 100644 index 4004a874772..00000000000 --- a/mozilla/browser/components/places/src/nsFaviconService.h +++ /dev/null @@ -1,129 +0,0 @@ -/* -*- 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): - * Brett Wilson (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 "nsCOMPtr.h" -#include "nsDataHashtable.h" -#include "nsIFaviconService.h" -#include "nsServiceManagerUtils.h" -#include "nsString.h" -#include "mozIStorageConnection.h" -#include "mozIStorageValueArray.h" -#include "mozIStorageStatement.h" - -// forward definition for friend class -class FaviconLoadListener; - -class nsFaviconService : public nsIFaviconService -{ -public: - nsFaviconService(); - nsresult Init(); - - // called by nsNavHistory::Init - static nsresult InitTables(mozIStorageConnection* aDBConn); - - /** - * Returns a cached pointer to the favicon service for consumers in the - * places directory. - */ - static nsFaviconService* GetFaviconService() - { - if (! gFaviconService) { - // note that we actually have to set the service to a variable here - // because the work in do_GetService actually happens during assignment >:( - nsresult rv; - nsCOMPtr serv(do_GetService("@mozilla.org/browser/favicon-service;1", &rv)); - NS_ENSURE_SUCCESS(rv, nsnull); - - // our constructor should have set the static variable. If it didn't, - // something is wrong. - NS_ASSERTION(gFaviconService, "Favicon service creation failed"); - } - // the service manager will keep the pointer to our service around, so - // this should always be valid even if nobody currently has a reference. - return gFaviconService; - } - - // internal version called by history when done lazily - nsresult DoSetAndLoadFaviconForPage(nsIURI* aPage, nsIURI* aFavicon, - PRBool aForceReload); - - // addition to API for strings to prevent excessive parsing of URIs - nsresult GetFaviconLinkForIconString(const nsCString& aIcon, nsIURI** aOutput); - void GetFaviconSpecForIconString(const nsCString& aIcon, nsACString& aOutput); - - NS_DECL_ISUPPORTS - NS_DECL_NSIFAVICONSERVICE - - -private: - ~nsFaviconService(); - - nsCOMPtr mDBConn; // from history service - - nsCOMPtr mDBGetURL; // returns URL, data len given page - nsCOMPtr mDBGetData; // returns actual data given URL - nsCOMPtr mDBGetIconInfo; - nsCOMPtr mDBInsertIcon; - nsCOMPtr mDBUpdateIcon; - nsCOMPtr mDBSetPageFavicon; - - static nsFaviconService* gFaviconService; - - /** - * A cached URI for the default icon. We return this a lot, and don't want to - * re-parse and normalize our unchanging string many times. Important: do - * not return this directly; use Clone() since callers may change the object - * they get back. May be null, in which case it needs initialization. - */ - nsCOMPtr mDefaultIcon; - - PRUint32 mFailedFaviconSerial; - nsDataHashtable mFailedFavicons; - - nsresult SetFaviconUrlForPageInternal(nsIURI* aURI, nsIURI* aFavicon, - PRBool* aHasData, PRTime* aExpiration); - - nsresult UpdateBookmarkRedirectFavicon(nsIURI* aPage, nsIURI* aFavicon); - void SendFaviconNotifications(nsIURI* aPage, nsIURI* aFaviconURI); - - friend class FaviconLoadListener; -}; - -#define FAVICON_DEFAULT_URL "chrome://browser/skin/places/defaultFavicon.png" -#define FAVICON_ANNOTATION_NAME "favicon" diff --git a/mozilla/browser/components/places/src/nsLivemarkService.cpp b/mozilla/browser/components/places/src/nsLivemarkService.cpp deleted file mode 100755 index e04e31f574a..00000000000 --- a/mozilla/browser/components/places/src/nsLivemarkService.cpp +++ /dev/null @@ -1,778 +0,0 @@ -/* -*- 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): - * Annie Sullivan (original author) - * Joe Hughes - * - * 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 "nsIRDFService.h" -#include "nsLivemarkService.h" -#include "nsIServiceManager.h" -#include "nsNavBookmarks.h" -#include "nsNavHistoryResult.h" -#include "nsIAnnotationService.h" -#include "nsFaviconService.h" -#include "nsNetUtil.h" -#include "rdf.h" -#include "nsRDFCID.h" -#include "nsIObserverService.h" -#include "nsCRT.h" -#include "nsXPCOM.h" - -#define LIVEMARK_TIMEOUT 15000 // fire every 15 seconds -#define PLACES_STRING_BUNDLE_URI "chrome://browser/locale/places/places.properties" -#define LIVEMARK_ICON_URI "chrome://browser/skin/places/livemarkItem.png" - -// Constants for parsing RDF Livemarks -// These are used in nsBookmarksFeedHandler.cpp, but because there is -// no initialization function in the nsLivemarkLoadListener class, they -// are initialized by the nsLivemarkService::Init() function in this file. -static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); -#ifndef RSS09_NAMESPACE_URI -#define RSS09_NAMESPACE_URI "http://my.netscape.com/rdf/simple/0.9/" -#endif -#ifndef RSS10_NAMESPACE_URI -#define RSS10_NAMESPACE_URI "http://purl.org/rss/1.0/" -#endif -#ifndef DC_NAMESPACE_URI -#define DC_NAMESPACE_URI "http://purl.org/dc/elements/1.1/" -#endif - - -nsLivemarkService* nsLivemarkService::sInstance = nsnull; - - -nsLivemarkService::nsLivemarkService() - : mTimer(nsnull) -{ - NS_ASSERTION(!sInstance, "Multiple nsLivemarkService instances!"); - sInstance = this; -} - -nsLivemarkService::~nsLivemarkService() -{ - NS_ASSERTION(sInstance == this, "Expected sInstance == this"); - sInstance = nsnull; -} - -NS_IMPL_ISUPPORTS3(nsLivemarkService, - nsILivemarkService, - nsIRemoteContainer, - nsIObserver) - -nsresult -nsLivemarkService::Init() -{ - nsresult rv; - - // Get string bundle for livemark messages - nsCOMPtr bundleService = - do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = bundleService->CreateBundle( - PLACES_STRING_BUNDLE_URI, - getter_AddRefs(mBundle)); - NS_ENSURE_SUCCESS(rv, rv); - - // Initialize the folder icon uri. - rv = NS_NewURI(getter_AddRefs(mIconURI), NS_LITERAL_STRING(LIVEMARK_ICON_URI)); - NS_ENSURE_SUCCESS(rv, rv); - - // Initialize the localized strings for the names of the dummy - // "livemark loading..." and "livemark failed to load" bookmarks - rv = mBundle->GetStringFromName(NS_LITERAL_STRING("bookmarksLivemarkLoading").get(), - getter_Copies(mLivemarkLoading)); - if (NS_FAILED(rv)) { - mLivemarkLoading.Assign(NS_LITERAL_STRING("Live Bookmark loading...")); - } - - nsXPIDLString lmfailedName; - rv = mBundle->GetStringFromName(NS_LITERAL_STRING("bookmarksLivemarkFailed").get(), - getter_Copies(mLivemarkFailed)); - if (NS_FAILED(rv)) { - mLivemarkFailed.Assign(NS_LITERAL_STRING("Live Bookmark feed failed to load.")); - } - - nsCOMPtr observerService = - do_GetService("@mozilla.org/observer-service;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE); - - // Create timer to check whether to update livemarks - if (!mTimer) { - mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); - NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create a timer"); - if (NS_FAILED(rv)) return rv; - mTimer->InitWithFuncCallback(nsLivemarkService::FireTimer, this, LIVEMARK_TIMEOUT, - nsITimer::TYPE_REPEATING_SLACK); - // Note: don't addref "this" as we'll cancel the timer in the nsLivemarkService destructor - } - - // Use the annotations service to store data about livemarks - mAnnotationService = do_GetService("@mozilla.org/browser/annotation-service;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - // Initialize the constants used to parse RDF livemark feeds - nsCOMPtr pRDF; - pRDF = do_GetService(kRDFServiceCID, &rv); - if (NS_FAILED(rv)) return rv; - pRDF->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"), - getter_AddRefs(mLMRDF_type)); - pRDF->GetResource(NS_LITERAL_CSTRING(RSS09_NAMESPACE_URI "channel"), - getter_AddRefs(mLMRSS09_channel)); - pRDF->GetResource(NS_LITERAL_CSTRING(RSS09_NAMESPACE_URI "item"), - getter_AddRefs(mLMRSS09_item)); - pRDF->GetResource(NS_LITERAL_CSTRING(RSS09_NAMESPACE_URI "title"), - getter_AddRefs(mLMRSS09_title)); - pRDF->GetResource(NS_LITERAL_CSTRING(RSS09_NAMESPACE_URI "link"), - getter_AddRefs(mLMRSS09_link)); - - pRDF->GetResource(NS_LITERAL_CSTRING(RSS10_NAMESPACE_URI "channel"), - getter_AddRefs(mLMRSS10_channel)); - pRDF->GetResource(NS_LITERAL_CSTRING(RSS10_NAMESPACE_URI "items"), - getter_AddRefs(mLMRSS10_items)); - pRDF->GetResource(NS_LITERAL_CSTRING(RSS10_NAMESPACE_URI "title"), - getter_AddRefs(mLMRSS10_title)); - pRDF->GetResource(NS_LITERAL_CSTRING(RSS10_NAMESPACE_URI "link"), - getter_AddRefs(mLMRSS10_link)); - - pRDF->GetResource(NS_LITERAL_CSTRING(DC_NAMESPACE_URI "date"), - getter_AddRefs(mLMDC_date)); - - // Initialize the list of livemarks from the list of URIs - // that have a feed uri annotation. - nsCOMArray pLivemarks; - rv = mAnnotationService->GetPagesWithAnnotationCOMArray( - NS_LITERAL_CSTRING(LMANNO_FEEDURI), &pLivemarks); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRInt32 i = 0; i < pLivemarks.Count(); ++i) { - - // Get the feed URI from the annotation. - nsAutoString feedURIString; - rv = mAnnotationService->GetAnnotationString(pLivemarks[i], - NS_LITERAL_CSTRING(LMANNO_FEEDURI), - feedURIString); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr feedURI; - rv = NS_NewURI(getter_AddRefs(feedURI), NS_ConvertUTF16toUTF8(feedURIString)); - NS_ENSURE_SUCCESS(rv, rv); - - // Use QueryStringToQueryArray() to get the folderId from the place:uri. - // (It ends up in folders[0] since we know that this place: uri was - // generated by the bookmark service to uniquely identify the folder) - nsCAutoString folderQueryString; - rv = pLivemarks[i]->GetSpec(folderQueryString); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMArray queries; - nsCOMPtr options; - rv = History()->QueryStringToQueryArray(folderQueryString, &queries, - getter_AddRefs(options)); - NS_ENSURE_SUCCESS(rv, rv); - if (queries.Count() != 1 || queries[0]->Folders().Length() != 1) - continue; // invalid folder URI (should identify exactly one folder) - - // Create the livemark and add it to the list. - LivemarkInfo *li = new LivemarkInfo(queries[0]->Folders()[0], - pLivemarks[i], feedURI); - NS_ENSURE_TRUE(li, NS_ERROR_OUT_OF_MEMORY); - NS_ENSURE_TRUE(mLivemarks.AppendElement(li), NS_ERROR_OUT_OF_MEMORY); - } - - return rv; -} - -NS_IMETHODIMP -nsLivemarkService::Observe(nsISupports *aSubject, const char *aTopic, - const PRUnichar *aData) -{ - if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { - nsresult rv; - - // Clear timer and pending loads to prevent leaks of the livemark service - // during shutdown. - if (mTimer) { - // be sure to cancel the timer, as it holds a - // weak reference back to nsLivemarkService - mTimer->Cancel(); - mTimer = nsnull; - } - // Cancel any pending loads - for (PRUint32 i = 0; i < mLivemarks.Length(); ++i) { - LivemarkInfo *li = mLivemarks[i]; - if (li->loadGroup) { - li->loadGroup->Cancel(NS_BINDING_ABORTED); - } - } - - // Remove our xpcom-shutdown observer so we don't leak the observer - // service. - nsCOMPtr observerService = - do_GetService("@mozilla.org/observer-service;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); - } - - return NS_OK; -} - - -NS_IMETHODIMP -nsLivemarkService::CreateLivemark(PRInt64 aFolder, - const nsAString &aName, - nsIURI *aSiteURI, - nsIURI *aFeedURI, - PRInt32 aIndex, - PRInt64 *aNewLivemark) -{ - // Create the livemark as a bookmark container - nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService(); - nsresult rv = bookmarks->CreateContainer(aFolder, aName, - NS_LITERAL_STRING(NS_LIVEMARKSERVICE_CONTRACTID), - aIndex, aNewLivemark); - NS_ENSURE_SUCCESS(rv, rv); - - // Get the livemark URI - nsCOMPtr livemarkURI; - rv = bookmarks->GetFolderURI(*aNewLivemark, getter_AddRefs(livemarkURI)); - NS_ENSURE_SUCCESS(rv, rv); - - // Add an annotation to map the folder URI to the livemark feed URI - nsCAutoString feedURISpec; - rv = aFeedURI->GetSpec(feedURISpec); - NS_ENSURE_SUCCESS(rv, rv); - mAnnotationService->SetAnnotationString(livemarkURI, - NS_LITERAL_CSTRING(LMANNO_FEEDURI), - NS_ConvertUTF8toUTF16(feedURISpec), - 0, - nsIAnnotationService::EXPIRE_NEVER); - - // Set the icon for the livemark - nsFaviconService* faviconService = nsFaviconService::GetFaviconService(); - NS_ENSURE_TRUE(faviconService, NS_ERROR_OUT_OF_MEMORY); - faviconService->SetFaviconUrlForPage(livemarkURI, mIconURI); - - if (aSiteURI) { - // Add an annotation to map the folder URI to the livemark site URI - nsCAutoString siteURISpec; - rv = aSiteURI->GetSpec(siteURISpec); - NS_ENSURE_SUCCESS(rv, rv); - rv = mAnnotationService->SetAnnotationString(livemarkURI, - NS_LITERAL_CSTRING(LMANNO_SITEURI), - NS_ConvertUTF8toUTF16(siteURISpec), - 0, - nsIAnnotationService::EXPIRE_NEVER); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Store the livemark info in our array. - LivemarkInfo *info = new LivemarkInfo(*aNewLivemark, livemarkURI, aFeedURI); - NS_ENSURE_TRUE(info, NS_ERROR_OUT_OF_MEMORY); - NS_ENSURE_TRUE(mLivemarks.AppendElement(info), NS_ERROR_OUT_OF_MEMORY); - - UpdateLivemarkChildren(mLivemarks.Length() - 1, PR_FALSE); - - return NS_OK; -} - -NS_IMETHODIMP -nsLivemarkService::IsLivemark(PRInt64 aFolder, PRBool *aResult) -{ - nsresult rv; - *aResult = PR_FALSE; - - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_FAILURE); - - nsCOMPtr folderURI; - rv = bookmarks->GetFolderURI(aFolder, getter_AddRefs(folderURI)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mAnnotationService->HasAnnotation( - folderURI, NS_LITERAL_CSTRING(LMANNO_FEEDURI), aResult); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -NS_IMETHODIMP -nsLivemarkService::GetSiteURI(PRInt64 aContainer, nsIURI **aURI) -{ - nsresult rv; - - // First off, make sure we're dealing with a livemark ID. - PRBool isLivemark = PR_FALSE; - rv = IsLivemark(aContainer, &isLivemark); - NS_ENSURE_SUCCESS(rv, rv); - if (!isLivemark) - return NS_ERROR_INVALID_ARG; - - // Now convert the container ID to a container URI for annotation operations - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_FAILURE); - - nsCOMPtr containerURI; - rv = bookmarks->GetFolderURI(aContainer, getter_AddRefs(containerURI)); - NS_ENSURE_SUCCESS(rv, rv); - - // If there's no site URI annotation, return null - nsAutoString siteURIString; - rv = mAnnotationService->GetAnnotationString( - containerURI, NS_LITERAL_CSTRING(LMANNO_SITEURI), siteURIString); - if (NS_FAILED(rv)) { - *aURI = nsnull; - return NS_OK; - } - - nsCOMPtr siteURI; - rv = NS_NewURI(getter_AddRefs(siteURI), NS_ConvertUTF16toUTF8(siteURIString)); - NS_ENSURE_SUCCESS(rv, rv); - *aURI = siteURI.get(); - NS_IF_ADDREF(*aURI); - - return NS_OK; -} - -NS_IMETHODIMP -nsLivemarkService::SetSiteURI(PRInt64 aContainer, nsIURI *aSiteURI) -{ - nsresult rv; - - // First off, make sure we're dealing with a livemark ID. - PRBool isLivemark = PR_FALSE; - rv = IsLivemark(aContainer, &isLivemark); - NS_ENSURE_SUCCESS(rv, rv); - if (!isLivemark) - return NS_ERROR_INVALID_ARG; - - // Now convert the container ID to a container URI for annotation operations - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_FAILURE); - - nsCOMPtr containerURI; - rv = bookmarks->GetFolderURI(aContainer, getter_AddRefs(containerURI)); - NS_ENSURE_SUCCESS(rv, rv); - - if (!aSiteURI) { // clear any existing site URI - rv = mAnnotationService->RemoveAnnotation( - containerURI, NS_LITERAL_CSTRING(LMANNO_SITEURI)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; - } - - nsCAutoString siteURISpec; - rv = aSiteURI->GetSpec(siteURISpec); - NS_ENSURE_SUCCESS(rv, rv); - rv = mAnnotationService->SetAnnotationString( - containerURI, - NS_LITERAL_CSTRING(LMANNO_SITEURI), - NS_ConvertUTF8toUTF16(siteURISpec), - 0, - nsIAnnotationService::EXPIRE_NEVER); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -NS_IMETHODIMP -nsLivemarkService::GetFeedURI(PRInt64 aContainer, nsIURI **aURI) -{ - nsresult rv; - - // Convert the container ID to a container URI for annotation operations - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_FAILURE); - - nsCOMPtr containerURI; - rv = bookmarks->GetFolderURI(aContainer, getter_AddRefs(containerURI)); - NS_ENSURE_SUCCESS(rv, rv); - - // If there's no feed URI annotation, that means this isn't a livemark - nsAutoString feedURIString; - rv = mAnnotationService->GetAnnotationString( - containerURI, NS_LITERAL_CSTRING(LMANNO_FEEDURI), feedURIString); - if (NS_FAILED(rv)) - return NS_ERROR_INVALID_ARG; - - nsCOMPtr feedURI; - rv = NS_NewURI(getter_AddRefs(feedURI), NS_ConvertUTF16toUTF8(feedURIString)); - NS_ENSURE_SUCCESS(rv, rv); - *aURI = feedURI.get(); - NS_IF_ADDREF(*aURI); - - return NS_OK; -} - -NS_IMETHODIMP -nsLivemarkService::SetFeedURI(PRInt64 aContainer, nsIURI *aFeedURI) -{ - nsresult rv; - - if (!aFeedURI) // a livemark folder with no feed URI is an illegal state - return NS_ERROR_NULL_POINTER; - - // Convert the container ID to a container URI for annotation operations - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_FAILURE); - - nsCOMPtr containerURI; - rv = bookmarks->GetFolderURI(aContainer, getter_AddRefs(containerURI)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCAutoString feedURISpec; - rv = aFeedURI->GetSpec(feedURISpec); - NS_ENSURE_SUCCESS(rv, rv); - rv = mAnnotationService->SetAnnotationString( - containerURI, - NS_LITERAL_CSTRING(LMANNO_FEEDURI), - NS_ConvertUTF8toUTF16(feedURISpec), - 0, - nsIAnnotationService::EXPIRE_NEVER); - NS_ENSURE_SUCCESS(rv, rv); - - // Now update our internal table - PRInt32 livemarkIndex = GetLivemarkIndex(aContainer); - NS_ENSURE_TRUE(livemarkIndex > -1, NS_ERROR_FAILURE); - - mLivemarks[livemarkIndex]->feedURI = aFeedURI; - - return NS_OK; -} - -#if 0 -// These two container API functions are not currently in the remote container -// service because they are untested. We do not need them here. -NS_IMETHODIMP -nsLivemarkService::OnContainerOpening( - nsINavHistoryContainerResultNode* container, - nsINavHistoryQueryOptions* options) -{ - // Nothing to do here since the containers are filled in - // as the livemarks are loaded through FireTimer(). - return NS_OK; -} - - -NS_IMETHODIMP -nsLivemarkService::OnContainerClosed(nsINavHistoryContainerResultNode* container) -{ - // Nothing to clean up - return NS_OK; -} -#endif - - -NS_IMETHODIMP -nsLivemarkService::OnContainerRemoving(PRInt64 aContainer) -{ - nsresult rv; - - // Find the livemark id in the list of livemarks. - PRInt32 lmIndex = -1; - PRInt32 i; - for (i = 0; i < PRInt32(mLivemarks.Length()); i++) { - if (mLivemarks[i]->folderId == aContainer) { - lmIndex = i; - break; - } - } - // If there livemark isn't in the list, it's not valid. - if (lmIndex == -1) - return NS_ERROR_INVALID_ARG; - - // Remove the annotations that link the folder URI to the - // Feed URI and Site URI - LivemarkInfo *removedItem = mLivemarks[lmIndex]; - rv = mAnnotationService->RemoveAnnotation(removedItem->folderURI, - NS_LITERAL_CSTRING(LMANNO_FEEDURI)); - NS_ENSURE_SUCCESS(rv, rv); - rv = mAnnotationService->RemoveAnnotation(removedItem->folderURI, - NS_LITERAL_CSTRING(LMANNO_SITEURI)); - NS_ENSURE_SUCCESS(rv, rv); - - // Check if there are any other livemarks pointing to this feed. - // If not, remove the annotations for the feed. - PRBool stillInUse = PR_FALSE; - PRBool urisEqual = PR_FALSE; - for (i = 0; i < PRInt32(mLivemarks.Length()); i++) { - if (i != lmIndex && - (NS_OK == mLivemarks[i]->feedURI->Equals(removedItem->feedURI, &urisEqual)) && - urisEqual) { - stillInUse = PR_TRUE; - break; - } - } - if (!stillInUse) { - // No other livemarks use this feed. Clear all the annotations for it. - rv = mAnnotationService->RemoveAnnotation(removedItem->feedURI, - NS_LITERAL_CSTRING("livemark_expiration")); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Cancel the load before we remove the element; this will ensure that - // a LivemarkLoadListener won't try to add items for this load. - if (removedItem->loadGroup) { - removedItem->loadGroup->Cancel(NS_BINDING_ABORTED); - } - - // Take the annotation out of the list of annotations. - mLivemarks.RemoveElementAt(lmIndex); - - // Get rid of the children for this feed, clearing their annotations. - DeleteLivemarkChildren(aContainer); - - return NS_OK; -} - - -NS_IMETHODIMP -nsLivemarkService::OnContainerMoved(PRInt64 aContainer, - PRInt64 aNewFolder, - PRInt32 aNewIndex) -{ - nsresult rv; - - // Find the livemark in the list. - PRInt32 index = -1; - for (PRInt32 i = 0; i < PRInt32(mLivemarks.Length()); i++) { - if (mLivemarks[i]->folderId == aContainer) { - index = i; - break; - } - } - if (index == -1) - return NS_ERROR_INVALID_ARG; - - // Get the new uri - nsCOMPtr newURI; - rv = nsNavBookmarks::GetBookmarksService()->GetFolderURI(aContainer, - getter_AddRefs(newURI)); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr oldURI = mLivemarks[index]->folderURI; - mLivemarks[index]->folderURI = newURI; - - // Update the annotation that maps the folder URI to the livemark feed URI - nsAutoString feedURIString; - rv = mAnnotationService->GetAnnotationString(oldURI, - NS_LITERAL_CSTRING(LMANNO_FEEDURI), - feedURIString); - NS_ENSURE_SUCCESS(rv, rv); - rv = mAnnotationService->RemoveAnnotation(oldURI, - NS_LITERAL_CSTRING(LMANNO_FEEDURI)); - NS_ENSURE_SUCCESS(rv, rv); - rv = mAnnotationService->SetAnnotationString(newURI, - NS_LITERAL_CSTRING(LMANNO_FEEDURI), - feedURIString, - 0, - nsIAnnotationService::EXPIRE_NEVER); - NS_ENSURE_SUCCESS(rv, rv); - - // Update the annotation that maps the folder URI to the livemark site URI - nsAutoString siteURIString; - rv = mAnnotationService->GetAnnotationString(oldURI, - NS_LITERAL_CSTRING(LMANNO_SITEURI), - siteURIString); - // rv will be failure if no site URI annotation is present - if (NS_SUCCEEDED(rv)) { - rv = mAnnotationService->RemoveAnnotation( - oldURI, - NS_LITERAL_CSTRING(LMANNO_SITEURI)); - NS_ENSURE_SUCCESS(rv, rv); - rv = mAnnotationService->SetAnnotationString( - newURI, - NS_LITERAL_CSTRING(LMANNO_SITEURI), - siteURIString, - 0, - nsIAnnotationService::EXPIRE_NEVER); - NS_ENSURE_SUCCESS(rv, rv); - } - return NS_OK; -} - - -NS_IMETHODIMP -nsLivemarkService::GetChildrenReadOnly(PRBool *aResult) -{ - *aResult = PR_TRUE; - return NS_OK; -} - - -void -nsLivemarkService::FireTimer(nsITimer* aTimer, void* aClosure) -{ - nsLivemarkService *lmks = NS_STATIC_CAST(nsLivemarkService *, aClosure); - if (!lmks) return; - - // Go through all of the livemarks, and check if each needs to be updated. - for (PRUint32 i = 0; i < lmks->mLivemarks.Length(); i++) { - lmks->UpdateLivemarkChildren(i, PR_FALSE); - } -} - -nsresult -nsLivemarkService::DeleteLivemarkChildren(PRInt64 aLivemarkFolderId) -{ - nsresult rv; - nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService(); - nsNavHistory* history = History(); - - nsCOMPtr query; - rv = history->GetNewQuery(getter_AddRefs(query)); - NS_ENSURE_TRUE(query, NS_ERROR_OUT_OF_MEMORY); - - rv = query->SetFolders(&aLivemarkFolderId, 1); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr options; - rv = history->GetNewQueryOptions(getter_AddRefs(options)); - NS_ENSURE_TRUE(options, NS_ERROR_OUT_OF_MEMORY); - PRUint32 mode = nsINavHistoryQueryOptions::GROUP_BY_FOLDER; - rv = options->SetGroupingMode(&mode, 1); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr result; - rv = history->ExecuteQuery(query, options, getter_AddRefs(result)); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr root; - rv = result->GetRoot(getter_AddRefs(root)); - NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 cc; - root->SetContainerOpen(PR_TRUE); - rv = root->GetChildCount(&cc); - NS_ENSURE_SUCCESS(rv, rv); - for (PRUint32 i = 0; i < cc; i++) { - nsCOMPtr node; - rv = root->GetChild(i, getter_AddRefs(node)); - if (NS_FAILED(rv)) continue; - nsCAutoString spec; - rv = node->GetUri(spec); - if (NS_FAILED(rv)) continue; - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), spec, nsnull); - if (NS_FAILED(rv)) continue; - rv = mAnnotationService->RemoveAnnotation(uri, - NS_LITERAL_CSTRING(LMANNO_BMANNO)); - if (NS_FAILED(rv)) continue; - } - - // Get the folder children. - rv = bookmarks->RemoveFolderChildren(aLivemarkFolderId); - return rv; -} - -nsresult -nsLivemarkService::InsertLivemarkChild(PRInt64 aLivemarkFolderId, - nsIURI *aURI, - const nsAString &aTitle, - const nsAString &aFeedURI) -{ - nsresult rv; - nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService(); - rv = bookmarks->InsertItem(aLivemarkFolderId, aURI, -1); - NS_ENSURE_SUCCESS(rv, rv); - rv = bookmarks->SetItemTitle(aURI, aTitle); - NS_ENSURE_SUCCESS(rv, rv); - - mAnnotationService->SetAnnotationString(aURI, - NS_LITERAL_CSTRING(LMANNO_BMANNO), - aFeedURI, - 0, - nsIAnnotationService::EXPIRE_NEVER); - return NS_OK; -} - -nsresult -nsLivemarkService::InsertLivemarkLoadingItem(PRInt64 aFolder) -{ - nsresult rv; - nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService(); - nsCOMPtr loadingURI; - rv = NS_NewURI(getter_AddRefs(loadingURI), NS_LITERAL_CSTRING("about:livemark-loading")); - NS_ENSURE_SUCCESS(rv, rv); - rv = bookmarks->InsertItem(aFolder, loadingURI, -1); - NS_ENSURE_SUCCESS(rv, rv); - rv = bookmarks->SetItemTitle(loadingURI, mLivemarkLoading); - return rv; -} - -nsresult -nsLivemarkService::InsertLivemarkFailedItem(PRInt64 aFolder) -{ - nsresult rv; - nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService(); - nsCOMPtr failedURI; - rv = NS_NewURI(getter_AddRefs(failedURI), NS_LITERAL_CSTRING("about:livemark-failed")); - NS_ENSURE_SUCCESS(rv, rv); - rv = bookmarks->InsertItem(aFolder, failedURI, -1); - NS_ENSURE_SUCCESS(rv, rv); - rv = bookmarks->SetItemTitle(failedURI, mLivemarkFailed); - return rv; -} - -NS_IMETHODIMP -nsLivemarkService::ReloadAllLivemarks() -{ - for (PRUint32 i = 0; i < mLivemarks.Length(); i++) { - UpdateLivemarkChildren(i, PR_TRUE); - } - return NS_OK; -} - -NS_IMETHODIMP -nsLivemarkService::ReloadLivemarkFolder(PRInt64 aFolderId) -{ - PRInt32 folderIndex = GetLivemarkIndex(aFolderId); - if (folderIndex == -1) - return NS_ERROR_INVALID_ARG; - - return UpdateLivemarkChildren(folderIndex, PR_TRUE); -} - -// Returns the index into mLivemarks that corresponds to the given -// folder ID, or -1 if not found. -PRInt32 -nsLivemarkService::GetLivemarkIndex(PRInt64 aFolderId) -{ - for (PRUint32 i = 0; i < mLivemarks.Length(); i++) { - if (mLivemarks[i]->folderId == aFolderId) { - return i; - } - } - - return -1; -} diff --git a/mozilla/browser/components/places/src/nsLivemarkService.h b/mozilla/browser/components/places/src/nsLivemarkService.h deleted file mode 100755 index 08aa7cc693d..00000000000 --- a/mozilla/browser/components/places/src/nsLivemarkService.h +++ /dev/null @@ -1,170 +0,0 @@ -/* -*- 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): - * Annie Sullivan (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 "nsILivemarkService.h" -#include "nsIStringBundle.h" -#include "nsIAnnotationService.h" -#include "nsNavHistory.h" -#include "nsToolkitCompsCID.h" -#include "nsILoadGroup.h" -#include "nsIObserver.h" - -/** - * This annotation's value is a string containing the URI of the - * source feed for the given livemark. It should be attached to the URI - * of the livemark container. - */ -#define LMANNO_FEEDURI "livemark/feedURI" - -/** - * This annotation's value is a string containing the URI of the - * website associated with the source feed for the given livemark. - * It should be attached to the URI of the livemark container. - */ -#define LMANNO_SITEURI "livemark/siteURI" - -/** - * This annotation's value is an Int64 representing the time - * (in milliseconds since January 1, 1970 GMT) when a - * livemark feed should expire and thus have its associated livemark - * refreshed. It should be attached to the URI of the livemark's - * source feed. - */ -#define LMANNO_EXPIRATION "livemark/expiration" - -/** - * This annotation's value is a string containing the URI of the - * syndication feed which was this source for a livemark item. - * It should be attached to the URI of a livemark item (that is, a - * child of a livemark container). - */ -#define LMANNO_BMANNO "livemark/bookmarkFeedURI" - -class nsIRDFResource; - -class nsLivemarkService : public nsILivemarkService, public nsIObserver -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIREMOTECONTAINER - NS_DECL_NSILIVEMARKSERVICE - NS_DECL_NSIOBSERVER - - nsLivemarkService(); - nsresult Init(); - - static nsLivemarkService* GetLivemarkService() { - if (!sInstance) { - nsresult rv; - nsCOMPtr serv(do_GetService(NS_LIVEMARKSERVICE_CONTRACTID, &rv)); - NS_ENSURE_SUCCESS(rv, nsnull); - NS_ASSERTION(sInstance, "Should have static instance pointer now"); - } - return sInstance; - } - - // These functions are called by the livemarks feed loader - // to set the livemark children. - nsresult DeleteLivemarkChildren(PRInt64 aLivemarkFolderId); - nsresult InsertLivemarkChild(PRInt64 aLivemarkFolderId, - nsIURI *aURI, - const nsAString &aTitle, - const nsAString &aFeedURI); - nsresult InsertLivemarkLoadingItem(PRInt64 aFolder); - nsresult InsertLivemarkFailedItem(PRInt64 aFolder); - - struct LivemarkInfo { - PRInt64 folderId; - nsCOMPtr folderURI; - nsCOMPtr feedURI; - PRBool locked; - // Keep track of the load group that contains the channel we're using - // to load this livemark. This allows the load to be cancelled if - // necessary. The load group automatically adds redirect channels, so - // cancelling the load group cancels everything. - nsCOMPtr loadGroup; - - LivemarkInfo(PRInt64 aFolderId, nsIURI *aFolderURI, nsIURI *aFeedURI) - : folderId(aFolderId), folderURI(aFolderURI), feedURI(aFeedURI), - locked(PR_FALSE) { } - - void AddRef() { ++mRefCnt; } - void Release() { if (--mRefCnt == 0) delete this; } - - private: - nsAutoRefCnt mRefCnt; - }; - - nsCOMPtr mLMRDF_type; - nsCOMPtr mLMRSS09_channel; - nsCOMPtr mLMRSS09_item; - nsCOMPtr mLMRSS09_title; - nsCOMPtr mLMRSS09_link; - nsCOMPtr mLMRSS10_channel; - nsCOMPtr mLMRSS10_items; - nsCOMPtr mLMRSS10_title; - nsCOMPtr mLMRSS10_link; - nsCOMPtr mLMDC_date; -private: - static nsLivemarkService *sInstance; - - ~nsLivemarkService(); - - // remove me when there is better query initialization - nsNavHistory* History() { return nsNavHistory::GetHistoryService(); } - - // For localized "livemark loading...", "livemark failed to load", - // etc. messages - nsCOMPtr mBundle; - nsXPIDLString mLivemarkLoading; - nsXPIDLString mLivemarkFailed; - - nsCOMPtr mIconURI; - - nsCOMPtr mAnnotationService; - - // The list of livemarks is stored in this array - nsTArray< nsRefPtr > mLivemarks; - - // Livemarks are updated on a timer. - nsCOMPtr mTimer; - static void FireTimer(nsITimer* aTimer, void* aClosure); - nsresult UpdateLivemarkChildren(PRInt32 aLivemarkIndex, PRBool aForceUpdate); - PRInt32 GetLivemarkIndex(PRInt64 folderID); - -}; diff --git a/mozilla/browser/components/places/src/nsMaybeWeakPtr.cpp b/mozilla/browser/components/places/src/nsMaybeWeakPtr.cpp deleted file mode 100644 index ec43c79346d..00000000000 --- a/mozilla/browser/components/places/src/nsMaybeWeakPtr.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* -*- 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 code. - * - * 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 "nsMaybeWeakPtr.h" - -void* -nsMaybeWeakPtr_base::GetValueAs(const nsIID &iid) const -{ - nsresult rv; - void *ref; - if (mPtr) { - rv = mPtr->QueryInterface(iid, &ref); - if (NS_SUCCEEDED(rv)) { - return ref; - } - } - - nsCOMPtr weakRef = do_QueryInterface(mPtr); - if (weakRef) { - rv = weakRef->QueryReferent(iid, &ref); - if (NS_FAILED(rv)) { - ref = nsnull; - } - } - return ref; -} - -/* static */ nsresult -nsMaybeWeakPtrArray_base::AppendWeakElementBase(nsTArray_base *aArray, - nsISupports *aElement, - PRBool aOwnsWeak) -{ - nsCOMPtr ref; - if (aOwnsWeak) { - nsCOMPtr weakRef; - weakRef = do_GetWeakReference(aElement); - NS_REINTERPRET_CAST(nsCOMPtr*, &weakRef)->swap(ref); - } else { - ref = aElement; - } - - isupports_type *array = NS_STATIC_CAST(isupports_type*, aArray); - if (array->IndexOf(ref) != isupports_type::NoIndex) { - return NS_ERROR_INVALID_ARG; // already present - } - if (!array->AppendElement(ref)) { - return NS_ERROR_OUT_OF_MEMORY; - } - return NS_OK; -} - -/* static */ nsresult -nsMaybeWeakPtrArray_base::RemoveWeakElementBase(nsTArray_base *aArray, - nsISupports *aElement) -{ - isupports_type *array = NS_STATIC_CAST(isupports_type*, aArray); - PRUint32 index = array->IndexOf(aElement); - if (index != isupports_type::NoIndex) { - array->RemoveElementAt(index); - return NS_OK; - } - - // Don't use do_GetWeakReference; it should only be called if we know - // the object supports weak references. - nsCOMPtr supWeakRef = do_QueryInterface(aElement); - NS_ENSURE_TRUE(supWeakRef, NS_ERROR_INVALID_ARG); - - nsCOMPtr weakRef; - nsresult rv = supWeakRef->GetWeakReference(getter_AddRefs(weakRef)); - NS_ENSURE_SUCCESS(rv, rv); - - index = array->IndexOf(weakRef); - if (index == isupports_type::NoIndex) { - return NS_ERROR_INVALID_ARG; - } - - array->RemoveElementAt(index); - return NS_OK; -} diff --git a/mozilla/browser/components/places/src/nsMaybeWeakPtr.h b/mozilla/browser/components/places/src/nsMaybeWeakPtr.h deleted file mode 100644 index 54d829093f4..00000000000 --- a/mozilla/browser/components/places/src/nsMaybeWeakPtr.h +++ /dev/null @@ -1,124 +0,0 @@ -/* -*- 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 code. - * - * 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 ***** */ - -#ifndef nsMaybeWeakPtr_h_ -#define nsMaybeWeakPtr_h_ - -#include "nsCOMPtr.h" -#include "nsWeakReference.h" -#include "nsTArray.h" - -#ifdef MOZILLA_1_8_BRANCH -#define NS_GET_TEMPLATE_IID NS_GET_IID -#endif - -// nsMaybeWeakPtr is a helper object to hold a strong-or-weak reference -// to the template class. It's pretty minimal, but sufficient. - -class nsMaybeWeakPtr_base -{ -protected: - // Returns an addref'd pointer to the requested interface - void* GetValueAs(const nsIID& iid) const; - - nsCOMPtr mPtr; -}; - -template -class nsMaybeWeakPtr : private nsMaybeWeakPtr_base -{ -public: - nsMaybeWeakPtr(nsISupports *ref) { mPtr = ref; } - nsMaybeWeakPtr(const nsCOMPtr &ref) { mPtr = ref; } - nsMaybeWeakPtr(const nsCOMPtr &ref) { mPtr = ref; } - - PRBool operator==(const nsMaybeWeakPtr &other) const { - return mPtr == other.mPtr; - } - - operator const nsCOMPtr() const { return GetValue(); } - -protected: - const nsCOMPtr GetValue() const { - return nsCOMPtr(dont_AddRef(NS_STATIC_CAST(T*, - GetValueAs(NS_GET_TEMPLATE_IID(T))))); - } -}; - -// nsMaybeWeakPtrArray is an array of MaybeWeakPtr objects, that knows how to -// grab a weak reference to a given object if requested. It only allows a -// given object to appear in the array once. - -class nsMaybeWeakPtrArray_base -{ -protected: - static nsresult AppendWeakElementBase(nsTArray_base *aArray, - nsISupports *aElement, PRBool aWeak); - static nsresult RemoveWeakElementBase(nsTArray_base *aArray, - nsISupports *aElement); - - typedef nsTArray< nsMaybeWeakPtr > isupports_type; -}; - -template -class nsMaybeWeakPtrArray : public nsTArray< nsMaybeWeakPtr >, - private nsMaybeWeakPtrArray_base -{ -public: - nsresult AppendWeakElement(T *aElement, PRBool aOwnsWeak) - { - return AppendWeakElementBase(this, aElement, aOwnsWeak); - } - - nsresult RemoveWeakElement(T *aElement) - { - return RemoveWeakElementBase(this, aElement); - } -}; - -// Call a method on each element in the array, but only if the element is -// non-null. - -#define ENUMERATE_WEAKARRAY(array, type, method) \ - for (PRUint32 array_idx = 0; array_idx < array.Length(); ++array_idx) { \ - const nsCOMPtr &e = array.ElementAt(array_idx); \ - if (e) \ - e->method; \ - } - -#endif diff --git a/mozilla/browser/components/places/src/nsMorkHistoryImporter.cpp b/mozilla/browser/components/places/src/nsMorkHistoryImporter.cpp deleted file mode 100644 index 7de94c7c1cb..00000000000 --- a/mozilla/browser/components/places/src/nsMorkHistoryImporter.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/* -*- 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) 2006 - * 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 "nsMorkHistoryImporter.h" -#include "nsNavHistory.h" -#include "mozStorageHelper.h" -#include "prprf.h" -#include "nsNetUtil.h" -#include "nsTArray.h" - -NS_IMPL_ISUPPORTS1(nsMorkHistoryImporter, nsIMorkHistoryImporter) - -// Columns for entry (non-meta) history rows -enum { - kURLColumn, - kNameColumn, - kVisitCountColumn, - kHiddenColumn, - kTypedColumn, - kLastVisitColumn, - kColumnCount // keep me last -}; - -static const char * const gColumnNames[] = { - "URL", "Name", "VisitCount", "Hidden", "Typed", "LastVisitDate" -}; - -struct TableReadClosure -{ - TableReadClosure(nsMorkReader *aReader, nsNavHistory *aHistory) - : reader(aReader), history(aHistory), swapBytes(PR_FALSE), - byteOrderColumn(-1) - { - NS_CONST_CAST(nsString*, &voidString)->SetIsVoid(PR_TRUE); - for (PRUint32 i = 0; i < kColumnCount; ++i) { - columnIndexes[i] = -1; - } - } - - // Backpointers to the reader and history we're operating on - const nsMorkReader *reader; - nsNavHistory *history; - - // A voided string to use for the user title - const nsString voidString; - - // Whether we need to swap bytes (file format is other-endian) - PRBool swapBytes; - - // Indexes of the columns that we care about - PRInt32 columnIndexes[kColumnCount]; - PRInt32 byteOrderColumn; -}; - -// Reverses the high and low bytes in a PRUnichar buffer. -// This is used if the file format has a different endianness from the -// current architecture. -static void -SwapBytes(PRUnichar *buffer) -{ - for (PRUnichar *b = buffer; *b; ++b) { - PRUnichar c = *b; - *b = (c << 8) | ((c >> 8) & 0xff); - } -} - -// Enumerator callback to add a table row to the NavHistoryService -/* static */ PLDHashOperator PR_CALLBACK -nsMorkHistoryImporter::AddToHistoryCB(const nsCSubstring &aRowID, - const nsTArray *aValues, - void *aData) -{ - TableReadClosure *data = NS_STATIC_CAST(TableReadClosure*, aData); - const nsMorkReader *reader = data->reader; - nsCString values[kColumnCount]; - const PRInt32 *columnIndexes = data->columnIndexes; - - for (PRInt32 i = 0; i < kColumnCount; ++i) { - if (columnIndexes[i] != -1) { - values[i] = (*aValues)[columnIndexes[i]]; - reader->NormalizeValue(values[i]); - } - } - - // title is really a UTF-16 string at this point - nsCString &titleC = values[kNameColumn]; - - PRUint32 titleLength; - const char *titleBytes; - if (titleC.IsEmpty()) { - titleBytes = "\0"; - titleLength = 0; - } else { - titleLength = titleC.Length() / 2; - - // add an extra null byte onto the end, so that the buffer ends - // with a complete unicode null character. - titleC.Append('\0'); - - // Swap the bytes in the unicode characters if necessary. - if (data->swapBytes) { - SwapBytes(NS_REINTERPRET_CAST(PRUnichar*, titleC.BeginWriting())); - } - titleBytes = titleC.get(); - } - - const PRUnichar *title = NS_REINTERPRET_CAST(const PRUnichar*, titleBytes); - - PRInt32 err; - PRInt32 count = values[kVisitCountColumn].ToInteger(&err); - if (err != 0 || count == 0) { - count = 1; - } - - PRTime date; - if (PR_sscanf(values[kLastVisitColumn].get(), "%lld", &date) != 1) { - date = -1; - } - - nsCOMPtr uri; - NS_NewURI(getter_AddRefs(uri), values[kURLColumn]); - - if (uri) { - PRBool isTyped = values[kTypedColumn].EqualsLiteral("1"); - PRInt32 transition = isTyped ? - (PRInt32) nsINavHistoryService::TRANSITION_TYPED - : (PRInt32) nsINavHistoryService::TRANSITION_LINK; - nsNavHistory *history = data->history; - - history->AddPageWithVisit(uri, - nsDependentString(title, titleLength), - data->voidString, - values[kHiddenColumn].EqualsLiteral("1"), - isTyped, count, transition, date); - } - return PL_DHASH_NEXT; -} - -// ImportHistory is the main entry point to the importer. -// It sets up the file stream and loops over the lines in the file to -// parse them, then adds the resulting row set to history. - -NS_IMETHODIMP -nsMorkHistoryImporter::ImportHistory(nsIFile *aFile, - nsINavHistoryService *aHistory) -{ - NS_ENSURE_TRUE(aFile && aHistory, NS_ERROR_NULL_POINTER); - - // Check that the file exists before we try to open it - PRBool exists; - aFile->Exists(&exists); - if (!exists) { - return NS_OK; - } - - // Read in the mork file - nsMorkReader reader; - nsresult rv = reader.Init(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = reader.Read(aFile); - NS_ENSURE_SUCCESS(rv, rv); - - // Gather up the column ids so we don't need to find them on each row - nsNavHistory *history = NS_STATIC_CAST(nsNavHistory*, aHistory); - TableReadClosure data(&reader, history); - const nsTArray &columns = reader.GetColumns(); - for (PRUint32 i = 0; i < columns.Length(); ++i) { - const nsCSubstring &name = columns[i].name; - for (PRUint32 j = 0; j < kColumnCount; ++j) { - if (name.Equals(gColumnNames[j])) { - data.columnIndexes[j] = i; - break; - } - } - if (name.EqualsLiteral("ByteOrder")) { - data.byteOrderColumn = i; - } - } - - // Determine the byte order from the table's meta-row. - const nsTArray *metaRow = reader.GetMetaRow(); - if (metaRow && data.byteOrderColumn != -1) { - const nsCString &byteOrder = (*metaRow)[data.byteOrderColumn]; - if (!byteOrder.IsVoid()) { - // Note whether the file uses a non-native byte ordering. - // If it does, we'll have to swap bytes for PRUnichar values. - // "BE" and "LE" are the only recognized values, anything - // else is garbage and the file will be treated as native-endian - // (no swapping). - nsCAutoString byteOrderValue(byteOrder); - reader.NormalizeValue(byteOrderValue); -#ifdef IS_LITTLE_ENDIAN - data.swapBytes = byteOrderValue.EqualsLiteral("BE"); -#else - data.swapBytes = byteOrderValue.EqualsLiteral("LE"); -#endif - } - } - - // Now add the results to history - mozIStorageConnection *conn = history->GetStorageConnection(); - NS_ENSURE_TRUE(conn, NS_ERROR_NOT_INITIALIZED); - mozStorageTransaction transaction(conn, PR_FALSE); -#ifdef IN_MEMORY_LINKS - mozIStorageConnection *memoryConn = history->GetMemoryStorageConnection(); - mozStorageTransaction memTransaction(memoryConn, PR_FALSE); -#endif - - reader.EnumerateRows(AddToHistoryCB, &data); - - // Make sure we don't have any duplicate items in the database. - rv = history->RemoveDuplicateURIs(); - NS_ENSURE_SUCCESS(rv, rv); - -#ifdef IN_MEMORY_LINKS - memTransaction.Commit(); -#endif - return transaction.Commit(); -} diff --git a/mozilla/browser/components/places/src/nsMorkHistoryImporter.h b/mozilla/browser/components/places/src/nsMorkHistoryImporter.h deleted file mode 100644 index 9eb1dedc4da..00000000000 --- a/mozilla/browser/components/places/src/nsMorkHistoryImporter.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- 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) 2006 - * 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 ***** */ - -#ifndef nsMorkHistoryImporter_h_ -#define nsMorkHistoryImporter_h_ - -#include "nsINavHistoryService.h" -#include "nsMorkReader.h" - -template class nsTArray; - -// The nsMorkHistoryImporter object parses a Mork-format history file and -// adds the history items to the NavHistoryService. It is invoked the first -// time the history service is created for a given profile, if a Mork history -// (history.dat) file exists. - -class nsMorkHistoryImporter : public nsIMorkHistoryImporter -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIMORKHISTORYIMPORTER - -private: - // Enumerator callback to add a single row to the NavHistory. - static PLDHashOperator PR_CALLBACK - AddToHistoryCB(const nsCSubstring &aRowID, - const nsTArray *aValues, - void *aData); -}; - -#endif // nsMorkHistoryImporter_h_ diff --git a/mozilla/browser/components/places/src/nsNavBookmarks.cpp b/mozilla/browser/components/places/src/nsNavBookmarks.cpp deleted file mode 100644 index 34fc33331b6..00000000000 --- a/mozilla/browser/components/places/src/nsNavBookmarks.cpp +++ /dev/null @@ -1,2355 +0,0 @@ -/* -*- 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 "nsAppDirectoryServiceDefs.h" -#include "nsNavBookmarks.h" -#include "nsNavHistory.h" -#include "mozStorageHelper.h" -#include "nsIServiceManager.h" -#include "nsNetUtil.h" -#include "nsIRemoteContainer.h" -#include "nsUnicharUtils.h" -#include "nsFaviconService.h" -#include "nsAnnotationService.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; -const PRInt32 nsNavBookmarks::kGetFolderInfoIndex_Type = 2; - -// These columns sit to the right of the kGetInfoIndex_* columns. -const PRInt32 nsNavBookmarks::kGetChildrenIndex_Position = 9; -const PRInt32 nsNavBookmarks::kGetChildrenIndex_ItemChild = 10; -const PRInt32 nsNavBookmarks::kGetChildrenIndex_FolderChild = 11; -const PRInt32 nsNavBookmarks::kGetChildrenIndex_FolderTitle = 12; - -nsNavBookmarks* nsNavBookmarks::sInstance = nsnull; - -#define BOOKMARKS_ANNO_PREFIX "bookmarks/" -#define ANNO_FOLDER_READONLY BOOKMARKS_ANNO_PREFIX "readonly" - -nsNavBookmarks::nsNavBookmarks() - : mRoot(0), mBookmarksRoot(0), mToolbarRoot(0), mTagRoot(0), mBatchLevel(0), - mBatchHasTransaction(PR_FALSE) -{ - 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); - mozIStorageConnection *dbConn = DBConn(); - mozStorageTransaction transaction(dbConn, PR_FALSE); - - rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT id, name, type FROM moz_bookmarks_folders WHERE id = ?1"), - getter_AddRefs(mDBGetFolderInfo)); - NS_ENSURE_SUCCESS(rv, rv); - - { - nsCOMPtr statement; - rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT folder_child FROM moz_bookmarks 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); - } - } - - nsCAutoString buffer; - - nsCOMPtr bundleService = - do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = bundleService->CreateBundle( - "chrome://browser/locale/places/places.properties", - getter_AddRefs(mBundle)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBFindURIBookmarks - rv = dbConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT a.* " - "FROM moz_bookmarks a, moz_history h " - "WHERE h.url = ?1 AND a.item_child = h.id"), - getter_AddRefs(mDBFindURIBookmarks)); - NS_ENSURE_SUCCESS(rv, rv); - - // Construct a result where the first columns exactly match those returned by - // mDBGetURLPageInfo, and additionally contains columns for position, - // item_child, and folder_child from moz_bookmarks. This selects only - // _item_ children which are in moz_history. - // Results are kGetInfoIndex_* - NS_NAMED_LITERAL_CSTRING(selectItemChildren, - "SELECT h.id, h.url, h.title, h.user_title, h.rev_host, h.visit_count, " - "(SELECT MAX(visit_date) FROM moz_historyvisit WHERE page_id = h.id), " - "f.url, null, a.position, a.item_child, a.folder_child, null " - "FROM moz_bookmarks a " - "JOIN moz_history h ON a.item_child = h.id " - "LEFT OUTER JOIN moz_favicon f ON h.favicon = f.id " - "WHERE a.parent = ?1 AND a.position >= ?2 AND a.position <= ?3 "); - - // Construct a result where the first columns are padded out to the width - // of mDBGetVisitPageInfo, containing additional columns for position, - // item_child, and folder_child from moz_bookmarks, and name from - // moz_bookmarks_folders. This selects only _folder_ children which are - // in moz_bookmarks_folders. Results are kGetInfoIndex_* kGetChildrenIndex_* - NS_NAMED_LITERAL_CSTRING(selectFolderChildren, - "SELECT null, null, null, null, null, null, null, null, null, a.position, a.item_child, a.folder_child, c.name " - "FROM moz_bookmarks a " - "JOIN moz_bookmarks_folders c ON c.id = a.folder_child " - "WHERE a.parent = ?1 AND a.position >= ?2 AND a.position <= ?3"); - - // Construct a result where the first columns are padded out to the width - // of mDBGetVisitPageInfo, containing additional columns for position, - // item_child, and folder_child from moz_bookmarks. This selects only - // _separator_ children which are in moz_bookmarks. Results are - // kGetInfoIndex_* kGetChildrenIndex_*. item_child and folder_child will - // be NULL for separators. - NS_NAMED_LITERAL_CSTRING(selectSeparatorChildren, - "SELECT null, null, null, null, null, null, null, null, null, a.position, null, null, null " - "FROM moz_bookmarks a " - "WHERE a.parent = ?1 AND a.position >= ?2 AND a.position <= ?3 AND " - "a.item_child ISNULL and a.folder_child ISNULL"); - - NS_NAMED_LITERAL_CSTRING(orderByPosition, " ORDER BY a.position"); - - // mDBGetChildren: select all children of a given folder, sorted by position - rv = dbConn->CreateStatement(selectItemChildren + - NS_LITERAL_CSTRING(" UNION ALL ") + - selectFolderChildren + - NS_LITERAL_CSTRING(" UNION ALL ") + - selectSeparatorChildren + orderByPosition, - getter_AddRefs(mDBGetChildren)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBFolderCount: count all of the children of a given folder - rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT COUNT(*) FROM moz_bookmarks WHERE parent = ?1"), - getter_AddRefs(mDBFolderCount)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBIndexOfItem: find the position of an item within a folder - rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT position FROM moz_bookmarks WHERE item_child = ?1 AND parent = ?2"), - getter_AddRefs(mDBIndexOfItem)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT position FROM moz_bookmarks WHERE folder_child = ?1 AND parent = ?2"), - getter_AddRefs(mDBIndexOfFolder)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT item_child, folder_child FROM moz_bookmarks WHERE parent = ?1 AND position = ?2"), - getter_AddRefs(mDBGetChildAt)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBGetRedirectDestinations - // input = page ID, time threshold; output = unique ID input has redirected to - rv = dbConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT dest_v.page_id " - "FROM moz_historyvisit source_v " - "LEFT JOIN moz_historyvisit dest_v ON dest_v.from_visit = source_v.visit_id " - "WHERE source_v.page_id = ?1 " - "AND source_v.visit_date >= ?2 " - "AND (dest_v.visit_type = 5 OR dest_v.visit_type = 6) " - "GROUP BY dest_v.page_id"), - getter_AddRefs(mDBGetRedirectDestinations)); - NS_ENSURE_SUCCESS(rv, rv); - - FillBookmarksHash(); - - // must be last: This may cause bookmarks to be imported, which will exercise - // most of the bookmark system - // keywords - rv = dbConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT k.keyword FROM moz_history h " - "JOIN moz_keywords k ON h.id = k.page_id " - "WHERE h.url = ?1"), - getter_AddRefs(mDBGetKeywordForURI)); - NS_ENSURE_SUCCESS(rv, rv); - rv = dbConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT url FROM moz_keywords k " - "JOIN moz_history h ON k.page_id = h.id " - "WHERE k.keyword = ?1"), - getter_AddRefs(mDBGetURIForKeyword)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = InitRoots(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = transaction.Commit(); - NS_ENSURE_SUCCESS(rv, rv); - - // allows us to notify on title changes. MUST BE LAST so it is impossible - // to fail after this call, or the history service will have a reference to - // us and we won't go away. - history->AddObserver(this, PR_FALSE); - - // DO NOT PUT STUFF HERE that can fail. See observer comment above. - - return NS_OK; -} - - -// nsNavBookmarks::InitTables -// -// All commands that initialize the schema of the DB go in here. This is -// called from history init before the dummy DB connection is started that -// will prevent us from modifying the schema. - -nsresult // static -nsNavBookmarks::InitTables(mozIStorageConnection* aDBConn) -{ - nsresult rv; - PRBool exists; - rv = aDBConn->TableExists(NS_LITERAL_CSTRING("moz_bookmarks"), &exists); - NS_ENSURE_SUCCESS(rv, rv); - if (! exists) { - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_bookmarks (" - "item_child INTEGER, " - "folder_child INTEGER, " - "parent INTEGER, " - "position INTEGER)")); - NS_ENSURE_SUCCESS(rv, rv); - - // this index will make it faster to determine if a given item is - // bookmarked (used by history queries and vacuuming, for example) - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX moz_bookmarks_itemindex ON moz_bookmarks (item_child)")); - NS_ENSURE_SUCCESS(rv, rv); - - // the most common operation is to find the children given a parent - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX moz_bookmarks_parentindex ON moz_bookmarks (parent)")); - NS_ENSURE_SUCCESS(rv, rv); - } - - // moz_bookmarks_folders - // Note the use of AUTOINCREMENT for the primary key. This ensures that each - // new primary key is unique, reducing the chance that stale references to - // bookmark folders will reference a random folder. This slows down inserts - // a little bit, which is why we don't always use it, but bookmark folders - // are not created very often. - // NOTE: The folder undo code depends on the autoincrement behavior because - // it must be able to undo deleting of a folder back to the old folder ID, - // which we assume has not been taken by something else. - rv = aDBConn->TableExists(NS_LITERAL_CSTRING("moz_bookmarks_folders"), &exists); - NS_ENSURE_SUCCESS(rv, rv); - if (! exists) { - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_bookmarks_folders (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "name LONGVARCHAR, " - "type LONGVARCHAR)")); - NS_ENSURE_SUCCESS(rv, rv); - } - - // moz_bookmarks_roots - rv = aDBConn->TableExists(NS_LITERAL_CSTRING("moz_bookmarks_roots"), &exists); - NS_ENSURE_SUCCESS(rv, rv); - if (!exists) { - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_bookmarks_roots (" - "root_name VARCHAR(16) UNIQUE, " - "folder_id INTEGER)")); - NS_ENSURE_SUCCESS(rv, rv); - } - - // moz_keywords - rv = aDBConn->TableExists(NS_LITERAL_CSTRING("moz_keywords"), &exists); - NS_ENSURE_SUCCESS(rv, rv); - if (! exists) { - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE TABLE moz_keywords (" - "keyword VARCHAR(32) UNIQUE," - "page_id INTEGER)")); - NS_ENSURE_SUCCESS(rv, rv); - - // it should be fast to go url->ID and ID->url - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX moz_keywords_keywordindex ON moz_keywords (keyword)")); - NS_ENSURE_SUCCESS(rv, rv); - rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX moz_keywords_pageindex ON moz_keywords (page_id)")); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - - -struct RenumberItem { - PRInt64 folderChild; - nsCOMPtr itemURI; - PRInt32 position; -}; - -struct RenumberItemsArray { - nsVoidArray items; - ~RenumberItemsArray(); -}; - -RenumberItemsArray::~RenumberItemsArray() -{ - for (PRInt32 i = 0; i < items.Count(); ++i) { - delete NS_STATIC_CAST(RenumberItem*, items[i]); - } -} - -// nsNavBookmarks::InitRoots -// -// This locates and creates if necessary the root items in the bookmarks -// folder hierarchy. These items are stored in a special roots table that -// maps short predefined names to folder IDs. -// -// Normally, these folders will exist already and we will save their IDs -// which are exposed through the bookmark service interface. -// -// If the root does not exist, a folder is created for it and the ID is -// saved in the root table. No user-visible name is given to these folders -// and they have no parent or other attributes. -// -// These attributes are set when the default_places.html file is imported. -// It defines the hierarchy, and has special attributes that tell us when -// a folder is one of our well-known roots. We then insert the root in the -// defined point in the hierarchy and set its attributes from this. -// -// This should be called as the last part of the init process so that -// all of the statements are set up and the service is ready to use. - -nsresult -nsNavBookmarks::InitRoots() -{ - nsresult rv; - nsCOMPtr getRootStatement; - rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING("SELECT folder_id FROM moz_bookmarks_roots WHERE root_name = ?1"), - getter_AddRefs(getRootStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool importDefaults = PR_FALSE; - rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("places"), &mRoot, &importDefaults); - NS_ENSURE_SUCCESS(rv, rv); - - getRootStatement->Reset(); - rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("menu"), &mBookmarksRoot, nsnull); - NS_ENSURE_SUCCESS(rv, rv); - - getRootStatement->Reset(); - rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("toolbar"), &mToolbarRoot, nsnull); - NS_ENSURE_SUCCESS(rv, rv); - - getRootStatement->Reset(); - rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("tags"), &mTagRoot, nsnull); - NS_ENSURE_SUCCESS(rv, rv); - - if (importDefaults) { - // when there is no places root, we should define the hierarchy by - // importing the default one. - nsCOMPtr defaultPlaces; - rv = NS_NewURI(getter_AddRefs(defaultPlaces), - NS_LITERAL_CSTRING("chrome://browser/locale/places/default_places.html"), - nsnull); - NS_ENSURE_SUCCESS(rv, rv); - rv = ImportBookmarksHTMLInternal(defaultPlaces, PR_TRUE, 0); - NS_ENSURE_SUCCESS(rv, rv); - - // migrate the user's old bookmarks - // FIXME: move somewhere else to some profile migrator thingy - nsCOMPtr bookmarksFile; - rv = NS_GetSpecialDirectory(NS_APP_BOOKMARKS_50_FILE, - getter_AddRefs(bookmarksFile)); - if (bookmarksFile) { - PRBool bookmarksFileExists; - rv = bookmarksFile->Exists(&bookmarksFileExists); - if (NS_SUCCEEDED(rv) && bookmarksFileExists) { - nsCOMPtr ioservice = do_GetService( - "@mozilla.org/network/io-service;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr bookmarksFileURI; - rv = ioservice->NewFileURI(bookmarksFile, - getter_AddRefs(bookmarksFileURI)); - NS_ENSURE_SUCCESS(rv, rv); - rv = ImportBookmarksHTMLInternal(bookmarksFileURI, PR_FALSE, 0); - NS_ENSURE_SUCCESS(rv, rv); - } - } - } - return NS_OK; -} - - -// nsNavBookmarks::CreateRoot -// -// This gets or creates a root folder of the given type. aWasCreated -// (optional) is true if the folder had to be created, false if we just used -// an old one. The statement that gets a folder ID from a root name is -// passed in so the DB only needs to parse the statement once, and we don't -// have to have a global for this. Creation is less optimized because it -// happens rarely. - -nsresult -nsNavBookmarks::CreateRoot(mozIStorageStatement* aGetRootStatement, - const nsCString& name, PRInt64* aID, - PRBool* aWasCreated) -{ - PRBool hasResult = PR_FALSE; - nsresult rv = aGetRootStatement->BindUTF8StringParameter(0, name); - NS_ENSURE_SUCCESS(rv, rv); - rv = aGetRootStatement->ExecuteStep(&hasResult); - NS_ENSURE_SUCCESS(rv, rv); - if (hasResult) { - if (aWasCreated) - *aWasCreated = PR_FALSE; - rv = aGetRootStatement->GetInt64(0, aID); - NS_ENSURE_SUCCESS(rv, rv); - NS_ASSERTION(*aID != 0, "Root is 0 for some reason, folders can't have 0 ID"); - return NS_OK; - } - if (aWasCreated) - *aWasCreated = PR_TRUE; - - // create folder with no name or attributes - nsCOMPtr insertStatement; - rv = CreateFolder(0, NS_LITERAL_STRING(""), -1, aID); - NS_ENSURE_SUCCESS(rv, rv); - - // save root ID - rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks_roots (root_name,folder_id) VALUES (?1, ?2)"), - getter_AddRefs(insertStatement)); - NS_ENSURE_SUCCESS(rv, rv); - rv = insertStatement->BindUTF8StringParameter(0, name); - NS_ENSURE_SUCCESS(rv, rv); - rv = insertStatement->BindInt64Parameter(1, *aID); - NS_ENSURE_SUCCESS(rv, rv); - rv = insertStatement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - - -// nsNavBookmarks::FillBookmarksHash -// -// This initializes the bookmarks hashtable that tells us which bookmark -// a given URI redirects to. This hashtable includes all URIs that -// redirect to bookmarks. -// -// This is called from the bookmark init function and so is wrapped -// in that transaction (for better performance). - -nsresult -nsNavBookmarks::FillBookmarksHash() -{ - nsresult rv; - PRBool hasMore; - - // first init the hashtable - NS_ENSURE_TRUE(mBookmarksHash.Init(1024), NS_ERROR_OUT_OF_MEMORY); - - // first populate the table with all bookmarks - nsCOMPtr statement; - rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING( - "SELECT h.id " - "FROM moz_bookmarks b " - "LEFT JOIN moz_history h ON b.item_child = h.id where b.item_child IS NOT NULL"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - while (NS_SUCCEEDED(statement->ExecuteStep(&hasMore)) && hasMore) { - PRInt64 pageID; - rv = statement->GetInt64(0, &pageID); - NS_ENSURE_TRUE(mBookmarksHash.Put(pageID, pageID), NS_ERROR_OUT_OF_MEMORY); - } - - // Find all pages h2 that have been redirected to from a bookmarked URI: - // bookmarked -> url (h1) url (h2) - // | ^ - // . | - // visit (v1) -> destination visit (v2) - // This should catch most redirects, which are only one level. More levels of - // redirection will be handled separately. - rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING( - "SELECT v1.page_id, v2.page_id " - "FROM moz_bookmarks b " - "LEFT JOIN moz_historyvisit v1 on b.item_child = v1.page_id " - "LEFT JOIN moz_historyvisit v2 on v2.from_visit = v1.visit_id " - "WHERE b.item_child IS NOT NULL " - "AND v2.visit_type = 5 OR v2.visit_type = 6 " // perm. or temp. RDRs - "GROUP BY v2.page_id"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - while (NS_SUCCEEDED(statement->ExecuteStep(&hasMore)) && hasMore) { - PRInt64 fromId, toId; - statement->GetInt64(0, &fromId); - statement->GetInt64(1, &toId); - - NS_ENSURE_TRUE(mBookmarksHash.Put(toId, fromId), NS_ERROR_OUT_OF_MEMORY); - - // handle redirects deeper than one level - rv = RecursiveAddBookmarkHash(fromId, toId, 0); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - - -// nsNavBookmarks::AddBookmarkToHash -// -// Given a bookmark that was potentially added, this goes through all -// redirects that this page may have resulted in and adds them to our hash. -// Note that this takes the ID of the URL in the history system, which we -// generally have when calling this function and which makes it faster. -// -// For better performance, this call should be in a DB transaction. -// -// @see RecursiveAddBookmarkHash - -nsresult -nsNavBookmarks::AddBookmarkToHash(PRInt64 aBookmarkId, PRTime aMinTime) -{ - // this function might be called before our hashtable is initialized (for - // example, on history import), just ignore these, we'll pick up the add when - // the hashtable is initialized later - if (! mBookmarksHash.IsInitialized()) - return NS_OK; - if (! mBookmarksHash.Put(aBookmarkId, aBookmarkId)) - return NS_ERROR_OUT_OF_MEMORY; - return RecursiveAddBookmarkHash(aBookmarkId, aBookmarkId, aMinTime); -} - - -// nsNavBookmkars::RecursiveAddBookmarkHash -// -// Used to add a new level of redirect information to the bookmark hash. -// Given a source bookmark 'aBookmark' and 'aCurrentSouce' that has already -// been added to the hashtable, this will add all redirect destinations of -// 'aCurrentSort'. Will call itself recursively to walk down the chain. -// -// 'aMinTime' is the minimum time to consider visits from. Visits previous -// to this will not be considered. This allows the search to be much more -// efficient if you know something happened recently. Use 0 for the min time -// to search all history for redirects. - -nsresult -nsNavBookmarks::RecursiveAddBookmarkHash(PRInt64 aBookmarkID, - PRInt64 aCurrentSource, - PRTime aMinTime) -{ - nsresult rv; - nsTArray found; - - // scope for the DB statement. The statement must be reset by the time we - // recursively call ourselves again, because our recursive call will use the - // same statement. - { - mozStorageStatementScoper scoper(mDBGetRedirectDestinations); - rv = mDBGetRedirectDestinations->BindInt64Parameter(0, aCurrentSource); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBGetRedirectDestinations->BindInt64Parameter(1, aMinTime); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMore; - while (NS_SUCCEEDED(mDBGetRedirectDestinations->ExecuteStep(&hasMore)) && - hasMore) { - - // add this newly found redirect destination to the hashtable - PRInt64 curID; - rv = mDBGetRedirectDestinations->GetInt64(0, &curID); - NS_ENSURE_SUCCESS(rv, rv); - - // It is very important we ignore anything already in our hashtable. It - // is actually pretty common to get loops of redirects. For example, - // a restricted page will redirect you to a login page, which will - // redirect you to the restricted page again with the proper cookie. - PRInt64 alreadyExistingOne; - if (mBookmarksHash.Get(curID, &alreadyExistingOne)) - continue; - - if (! mBookmarksHash.Put(curID, aBookmarkID)) - return NS_ERROR_OUT_OF_MEMORY; - - // save for recursion later - found.AppendElement(curID); - } - } - - // recurse on each found item now that we're done with the statement - for (PRUint32 i = 0; i < found.Length(); i ++) { - rv = RecursiveAddBookmarkHash(aBookmarkID, found[i], aMinTime); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - - -// nsNavBookmarks::UpdateBookmarkHashOnRemove -// -// Call this when a bookmark is removed. It will see if the bookmark still -// exists anywhere in the system, and, if not, remove all references to it -// in the bookmark hashtable. -// -// The callback takes a pointer to what bookmark is being removed (as -// an Int64 history page ID) as the userArg and removes all redirect -// destinations that reference it. - -PR_STATIC_CALLBACK(PLDHashOperator) -RemoveBookmarkHashCallback(nsTrimInt64HashKey::KeyType aKey, - PRInt64& aBookmark, void* aUserArg) -{ - const PRInt64* removeThisOne = NS_REINTERPRET_CAST(const PRInt64*, aUserArg); - if (aBookmark == *removeThisOne) - return PL_DHASH_REMOVE; - return PL_DHASH_NEXT; -} -nsresult -nsNavBookmarks::UpdateBookmarkHashOnRemove(PRInt64 aBookmarkId) -{ - // note we have to use the DB version here since the hashtable may be - // out-of-date - PRBool inDB; - nsresult rv = IsBookmarkedInDatabase(aBookmarkId, &inDB); - NS_ENSURE_SUCCESS(rv, rv); - if (inDB) - return NS_OK; // bookmark still exists, don't need to update hashtable - - // remove it - mBookmarksHash.Enumerate(RemoveBookmarkHashCallback, - NS_REINTERPRET_CAST(void*, &aBookmarkId)); - return NS_OK; -} - - -// nsNavBookmarks::IsBookmarkedInDatabase -// -// This checks to see if the specified URI is actually bookmarked, bypassing -// our hashtable. Normal IsBookmarked checks just use the hashtable. - -nsresult -nsNavBookmarks::IsBookmarkedInDatabase(PRInt64 aBookmarkID, - PRBool *aIsBookmarked) -{ - // we'll just select position since it's just an int32 and may be faster. - // We don't actually care about the data, just whether there is any. - nsCOMPtr statement; - nsresult rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING( - "SELECT position FROM moz_bookmarks WHERE item_child = ?1"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->BindInt64Parameter(0, aBookmarkID); - NS_ENSURE_SUCCESS(rv, rv); - - return statement->ExecuteStep(aIsBookmarked); -} - - -nsresult -nsNavBookmarks::AdjustIndices(PRInt64 aFolder, - PRInt32 aStartIndex, PRInt32 aEndIndex, - PRInt32 aDelta) -{ - NS_ASSERTION(aStartIndex <= aEndIndex, "start index must be <= end index"); - - mozIStorageConnection *dbConn = DBConn(); - mozStorageTransaction transaction(dbConn, PR_FALSE); - - nsCAutoString buffer; - buffer.AssignLiteral("UPDATE moz_bookmarks 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 != PR_INT32_MAX) { - buffer.AppendLiteral(" AND position <= "); - buffer.AppendInt(aEndIndex); - } - - nsresult rv = DBConn()->ExecuteSimpleSQL(buffer); - NS_ENSURE_SUCCESS(rv, rv); - - RenumberItemsArray itemsArray; - nsVoidArray *items = &itemsArray.items; - { - mozStorageStatementScoper scope(mDBGetChildren); - - rv = mDBGetChildren->BindInt64Parameter(0, aFolder); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBGetChildren->BindInt32Parameter(1, aStartIndex + aDelta); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBGetChildren->BindInt32Parameter(2, aEndIndex + aDelta); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool results; - while (NS_SUCCEEDED(mDBGetChildren->ExecuteStep(&results)) && results) { - RenumberItem *item = new RenumberItem(); - if (!item) { - return NS_ERROR_OUT_OF_MEMORY; - } - - if (mDBGetChildren->IsNull(kGetChildrenIndex_ItemChild)) { - item->folderChild = mDBGetChildren->AsInt64(kGetChildrenIndex_FolderChild); - } else { - nsCAutoString spec; - mDBGetChildren->GetUTF8String(nsNavHistory::kGetInfoIndex_URL, spec); - rv = NS_NewURI(getter_AddRefs(item->itemURI), spec, nsnull); - NS_ENSURE_SUCCESS(rv, rv); - } - item->position = mDBGetChildren->AsInt32(kGetChildrenIndex_Position); - if (!items->AppendElement(item)) { - delete item; - return NS_ERROR_OUT_OF_MEMORY; - } - } - } - - rv = transaction.Commit(); - NS_ENSURE_SUCCESS(rv, rv); - - nsBookmarksUpdateBatcher batch; - - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::GetPlacesRoot(PRInt64 *aRoot) -{ - *aRoot = mRoot; - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::GetBookmarksRoot(PRInt64 *aRoot) -{ - *aRoot = mBookmarksRoot; - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::GetToolbarRoot(PRInt64 *aRoot) -{ - *aRoot = mToolbarRoot; - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::GetTagRoot(PRInt64 *aRoot) -{ - *aRoot = mTagRoot; - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::InsertItem(PRInt64 aFolder, nsIURI *aItem, PRInt32 aIndex) -{ - // You can pass -1 to indicate append, but no other negative number is allowed - if (aIndex < -1) - return NS_ERROR_INVALID_ARG; - - mozIStorageConnection *dbConn = DBConn(); - mozStorageTransaction transaction(dbConn, PR_FALSE); - - PRInt64 childID; - nsresult rv = History()->GetUrlIdFor(aItem, &childID, PR_TRUE); - NS_ENSURE_SUCCESS(rv, rv); - - // see if this item is already in the folder - PRBool hasItem = PR_FALSE; - PRInt32 previousIndex = -1; - { // scope the mDBIndexOfItem statement - mozStorageStatementScoper scoper(mDBIndexOfItem); - rv = mDBIndexOfItem->BindInt64Parameter(0, childID); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBIndexOfItem->BindInt64Parameter(1, aFolder); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBIndexOfItem->ExecuteStep(&hasItem); - NS_ENSURE_SUCCESS(rv, rv); - if (hasItem) - previousIndex = mDBIndexOfItem->AsInt32(0); - } - - PRInt32 index = (aIndex == -1) ? FolderCount(aFolder) : aIndex; - - if (hasItem && index == previousIndex) - return NS_OK; // item already at its desired position: nothing to do - - if (hasItem) { - // remove any old items - rv = RemoveItem(aFolder, aItem); - NS_ENSURE_SUCCESS(rv, rv); - - // since we just removed the item, everything after it shifts back by one - if (index > previousIndex) - index --; - } - - rv = AdjustIndices(aFolder, index, PR_INT32_MAX, 1); - NS_ENSURE_SUCCESS(rv, rv); - - nsCAutoString buffer; - buffer.AssignLiteral("INSERT INTO moz_bookmarks (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); - - AddBookmarkToHash(childID, 0); - - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - 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; - { - mozStorageStatementScoper scope(mDBIndexOfItem); - mDBIndexOfItem->BindInt64Parameter(0, childID); - mDBIndexOfItem->BindInt64Parameter(1, aFolder); - - PRBool results; - rv = mDBIndexOfItem->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 ? mDBIndexOfItem->AsInt32(0) : -1; - } - - buffer.AssignLiteral("DELETE FROM moz_bookmarks 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, PR_INT32_MAX, -1); - NS_ENSURE_SUCCESS(rv, rv); - } - - rv = transaction.Commit(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = UpdateBookmarkHashOnRemove(childID); - NS_ENSURE_SUCCESS(rv, rv); - - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - OnItemRemoved(aItem, aFolder, childIndex)) - - return NS_OK; -} - - -NS_IMETHODIMP -nsNavBookmarks::ReplaceItem(PRInt64 aFolder, nsIURI *aItem, nsIURI *aNewItem) -{ - mozIStorageConnection *dbConn = DBConn(); - nsNavHistory *history = History(); - 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_ERROR_INVALID_ARG; // the item isn't in history at all - } - - PRInt64 newChildID; - rv = history->GetUrlIdFor(aNewItem, &newChildID, PR_TRUE); - NS_ENSURE_SUCCESS(rv, rv); - NS_ASSERTION(newChildID != 0, "must have an item id"); - - nsCAutoString buffer; - buffer.AssignLiteral("UPDATE moz_bookmarks SET item_child = "); - buffer.AppendInt(newChildID); - buffer.AppendLiteral(" WHERE item_child = "); - buffer.AppendInt(childID); - buffer.AppendLiteral(" AND parent = "); - buffer.AppendInt(aFolder); - - rv = dbConn->ExecuteSimpleSQL(buffer); - NS_ENSURE_SUCCESS(rv, rv); - - rv = transaction.Commit(); - NS_ENSURE_SUCCESS(rv, rv); - - // update the bookmark hash, something could have gone away, and something - // else could have been created - rv = UpdateBookmarkHashOnRemove(childID); - NS_ENSURE_SUCCESS(rv, rv); - rv = AddBookmarkToHash(newChildID, 0); - NS_ENSURE_SUCCESS(rv, rv); - - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - OnItemReplaced(aFolder, aItem, aNewItem)) - - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::CreateFolder(PRInt64 aParent, const nsAString &aName, - PRInt32 aIndex, PRInt64 *aNewFolder) -{ - // CreateFolderWithID returns the index of the new folder, but that's not - // used here. To avoid any risk of corrupting data should this function - // be changed, we'll use a local variable to hold it. The PR_TRUE argument - // will cause notifications to be sent to bookmark observers. - PRInt32 localIndex = aIndex; - return CreateFolderWithID(-1, aParent, aName, PR_TRUE, &localIndex, aNewFolder); -} - -NS_IMETHODIMP -nsNavBookmarks::CreateContainer(PRInt64 aParent, const nsAString &aName, - const nsAString &aType, PRInt32 aIndex, - PRInt64 *aNewFolder) -{ - return CreateContainerWithID(-1, aParent, aName, aType, aIndex, aNewFolder); -} - -nsresult -nsNavBookmarks::CreateFolderWithID(PRInt64 aFolder, PRInt64 aParent, - const nsAString& aName, - PRBool aSendNotifications, - PRInt32* aIndex, PRInt64* aNewFolder) -{ - // You can pass -1 to indicate append, but no other negative number is allowed - if (*aIndex < -1) - return NS_ERROR_INVALID_ARG; - - mozIStorageConnection *dbConn = DBConn(); - mozStorageTransaction transaction(dbConn, PR_FALSE); - - PRInt32 index = (*aIndex == -1) ? FolderCount(aParent) : *aIndex; - - nsresult rv = AdjustIndices(aParent, index, PR_INT32_MAX, 1); - NS_ENSURE_SUCCESS(rv, rv); - - { - nsCOMPtr statement; - if (aFolder == -1) { - rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks_folders (name, type) VALUES (?1, null)"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->BindStringParameter(0, aName); - NS_ENSURE_SUCCESS(rv, rv); - } - else { - rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks_folders (id, name, type) VALUES (?1, ?2, null)"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->BindInt64Parameter(0, aFolder); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindStringParameter(1, aName); - NS_ENSURE_SUCCESS(rv, rv); - } - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - } - - PRInt64 child; - rv = dbConn->GetLastInsertRowID(&child); - NS_ENSURE_SUCCESS(rv, rv); - - nsCAutoString buffer; - buffer.AssignLiteral("INSERT INTO moz_bookmarks (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); - - // When creating a livemark container, we need to delay sending notifications - // until the container type has been set. In that case, they'll be sent by - // CreateContainerWithID rather than here. - if (aSendNotifications) { - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - OnFolderAdded(child, aParent, index)) - } - - *aIndex = index; - *aNewFolder = child; - return NS_OK; -} - -nsresult -nsNavBookmarks::CreateContainerWithID(PRInt64 aFolder, PRInt64 aParent, - const nsAString &aName, const nsAString &aType, - PRInt32 aIndex, PRInt64 *aNewFolder) -{ - // Containers are wrappers around read-only folders, with a specific type. - // CreateFolderWithID will return the index of the newly created folder, - // which we will need later on in order to send notifications. The PR_FALSE - // argument disables sending notifiactions, since we need to defer that until - // the folder type has been set. - PRInt32 localIndex = aIndex; - nsresult rv = CreateFolderWithID(aFolder, aParent, aName, PR_FALSE, &localIndex, aNewFolder); - NS_ENSURE_SUCCESS(rv, rv); - - // Set the type. - nsCOMPtr statement; - rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING("UPDATE moz_bookmarks_folders SET type = ?2 WHERE id = ?1"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->BindInt64Parameter(0, *aNewFolder); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindStringParameter(1, aType); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - // Send notifications after folder type has been set. - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - OnFolderAdded(*aNewFolder, aParent, localIndex)) - - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::InsertSeparator(PRInt64 aParent, PRInt32 aIndex) -{ - // You can pass -1 to indicate append, but no other negative number is allowed - if (aIndex < -1) - return NS_ERROR_INVALID_ARG; - - mozIStorageConnection *dbConn = DBConn(); - mozStorageTransaction transaction(dbConn, PR_FALSE); - - PRInt32 index = (aIndex == -1) ? FolderCount(aParent) : aIndex; - - nsresult rv = AdjustIndices(aParent, index, PR_INT32_MAX, 1); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr statement; - rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks " - "(parent, position) VALUES (?1,?2)"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->BindInt64Parameter(0, aParent); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindInt32Parameter(1, index); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = transaction.Commit(); - NS_ENSURE_SUCCESS(rv, rv); - - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - OnSeparatorAdded(aParent, index)) - - return NS_OK; -} - - -NS_IMETHODIMP -nsNavBookmarks::RemoveChildAt(PRInt64 aParent, PRInt32 aIndex) -{ - mozIStorageConnection *dbConn = DBConn(); - mozStorageTransaction transaction(dbConn, PR_FALSE); - nsresult rv; - PRInt64 item, folder; - - { - mozStorageStatementScoper scope(mDBGetChildAt); - rv = mDBGetChildAt->BindInt64Parameter(0, aParent); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBGetChildAt->BindInt32Parameter(1, aIndex); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMore; - rv = mDBGetChildAt->ExecuteStep(&hasMore); - NS_ENSURE_SUCCESS(rv, rv); - if (!hasMore) { - // Child doesn't exist - return NS_ERROR_INVALID_ARG; - } - - if (mDBGetChildAt->IsNull(0)) { - item = 0; - folder = mDBGetChildAt->AsInt64(1); - } else { - folder = 0; - item = mDBGetChildAt->AsInt64(0); - } - } - - if (item != 0) { - // We're removing an item, go find its URI. - mozIStorageStatement *pageInfo = History()->DBGetIdPageInfo(); - nsCOMPtr uri; - { - mozStorageStatementScoper scope(pageInfo); - rv = pageInfo->BindInt64Parameter(0, item); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMore; - rv = pageInfo->ExecuteStep(&hasMore); - NS_ENSURE_SUCCESS(rv, rv); - if (!hasMore) { - return NS_ERROR_INVALID_ARG; - } - nsCAutoString spec; - rv = pageInfo->GetUTF8String(1, spec); - NS_ENSURE_SUCCESS(rv, rv); - - rv = NS_NewURI(getter_AddRefs(uri), spec); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Commit this transaction so that we don't notify observers mid-tranaction - rv = transaction.Commit(); - NS_ENSURE_SUCCESS(rv, rv); - - return RemoveItem(aParent, uri); - } - if (folder != 0) { - // Commit this transaction so that we don't notify observers mid-tranaction - rv = transaction.Commit(); - NS_ENSURE_SUCCESS(rv, rv); - - return RemoveFolder(folder); - } - - // No item or folder, so this is a separator. - nsCOMPtr statement; - rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_bookmarks WHERE parent = ?1 AND position = ?2"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->BindInt64Parameter(0, aParent); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindInt32Parameter(1, aIndex); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = AdjustIndices(aParent, aIndex + 1, PR_INT32_MAX, -1); - NS_ENSURE_SUCCESS(rv, rv); - - rv = transaction.Commit(); - NS_ENSURE_SUCCESS(rv, rv); - - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - OnSeparatorRemoved(aParent, aIndex)) - return NS_OK; -} - -nsresult -nsNavBookmarks::GetParentAndIndexOfFolder(PRInt64 aFolder, PRInt64* aParent, - PRInt32* aIndex) -{ - nsCAutoString buffer; - buffer.AssignLiteral("SELECT parent, position FROM moz_bookmarks WHERE folder_child = "); - buffer.AppendInt(aFolder); - - nsCOMPtr statement; - nsresult 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 - } - - *aParent = statement->AsInt64(0); - *aIndex = statement->AsInt32(1); - - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::RemoveFolder(PRInt64 aFolder) -{ - // If this is a container bookmark, try to notify its service. - nsresult rv; - nsCAutoString folderType; - rv = GetFolderType(aFolder, folderType); - NS_ENSURE_SUCCESS(rv, rv); - if (folderType.Length() > 0) { - // There is a type associated with this folder; it's a livemark. - nsCOMPtr bmcServ = do_GetService(folderType.get()); - if (bmcServ) { - rv = bmcServ->OnContainerRemoving(aFolder); - if (NS_FAILED(rv)) - NS_WARNING("Remove folder container notification failed."); - } - } - - mozIStorageConnection *dbConn = DBConn(); - mozStorageTransaction transaction(dbConn, PR_FALSE); - - PRInt64 parent; - PRInt32 index; - rv = GetParentAndIndexOfFolder(aFolder, &parent, &index); - NS_ENSURE_SUCCESS(rv, rv); - - // Remove all of the folder's children - RemoveFolderChildren(aFolder); - - // Remove the folder from its parent - nsCAutoString buffer; - buffer.AssignLiteral("DELETE FROM moz_bookmarks WHERE folder_child = "); - buffer.AppendInt(aFolder); - rv = dbConn->ExecuteSimpleSQL(buffer); - NS_ENSURE_SUCCESS(rv, rv); - - // And remove the folder from the folder table - buffer.AssignLiteral("DELETE FROM moz_bookmarks_folders WHERE id = "); - buffer.AppendInt(aFolder); - rv = dbConn->ExecuteSimpleSQL(buffer); - NS_ENSURE_SUCCESS(rv, rv); - - rv = AdjustIndices(parent, index + 1, PR_INT32_MAX, -1); - NS_ENSURE_SUCCESS(rv, rv); - - rv = transaction.Commit(); - NS_ENSURE_SUCCESS(rv, rv); - - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - OnFolderRemoved(aFolder, parent, index)) - - return NS_OK; -} - -NS_IMPL_ISUPPORTS1(nsNavBookmarks::RemoveFolderTransaction, nsITransaction) - -NS_IMETHODIMP -nsNavBookmarks::GetRemoveFolderTransaction(PRInt64 aFolder, nsITransaction** aResult) -{ - // Create and initialize a RemoveFolderTransaction object that can be used to - // recreate the folder safely later. - - nsAutoString title; - nsresult rv = GetFolderTitle(aFolder, title); - NS_ENSURE_SUCCESS(rv, rv); - - PRInt64 parent; - PRInt32 index; - rv = GetParentAndIndexOfFolder(aFolder, &parent, &index); - NS_ENSURE_SUCCESS(rv, rv); - - nsCAutoString type; - rv = GetFolderType(aFolder, type); - NS_ENSURE_SUCCESS(rv, rv); - - RemoveFolderTransaction* rft = - new RemoveFolderTransaction(aFolder, parent, title, index, type); - if (!rft) - return NS_ERROR_OUT_OF_MEMORY; - - NS_ADDREF(*aResult = rft); - return NS_OK; -} - - -NS_IMETHODIMP -nsNavBookmarks::RemoveFolderChildren(PRInt64 aFolder) -{ - mozStorageTransaction transaction(DBConn(), PR_FALSE); - - nsTArray separatorChildren; // separator indices - nsTArray folderChildren; - nsCOMArray itemChildren; - nsresult rv; - { - mozStorageStatementScoper scope(mDBGetChildren); - rv = mDBGetChildren->BindInt64Parameter(0, aFolder); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBGetChildren->BindInt32Parameter(1, 0); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBGetChildren->BindInt32Parameter(2, PR_INT32_MAX); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMore; - while (NS_SUCCEEDED(mDBGetChildren->ExecuteStep(&hasMore)) && hasMore) { - PRBool isFolder = ! mDBGetChildren->IsNull(kGetChildrenIndex_FolderChild); - if (isFolder) { - // folder - folderChildren.AppendElement( - mDBGetChildren->AsInt64(kGetChildrenIndex_FolderChild)); - } else if (mDBGetChildren->IsNull(kGetChildrenIndex_ItemChild)) { - // separator - separatorChildren.AppendElement(mDBGetChildren->AsInt32(kGetChildrenIndex_Position)); - } else { - // item (URI) - nsCOMPtr uri; - nsCAutoString spec; - mDBGetChildren->GetUTF8String(nsNavHistory::kGetInfoIndex_URL, spec); - rv = NS_NewURI(getter_AddRefs(uri), spec); - NS_ENSURE_SUCCESS(rv, rv); - PRBool success = itemChildren.AppendObject(uri); - NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY); - } - } - } - - // Remove separators. The list of separators will already be sorted since - // we order by position, so by enumerating it backwards, we avoid having to - // deal with shifting indices. - PRUint32 i; - for (i = separatorChildren.Length() - 1; i != PRUint32(-1); --i) { - rv = RemoveChildAt(aFolder, separatorChildren[i]); - NS_ENSURE_SUCCESS(rv, rv); - } - - // remove folders - for (i = 0; i < folderChildren.Length(); ++i) { - rv = RemoveFolder(folderChildren[i]); - NS_ENSURE_SUCCESS(rv, rv); - } - - // remove items - for (i = 0; i < PRUint32(itemChildren.Count()); ++i) { - rv = RemoveItem(aFolder, itemChildren[i]); - NS_ENSURE_SUCCESS(rv, rv); - } - - transaction.Commit(); - return NS_OK; -} - - -NS_IMETHODIMP -nsNavBookmarks::MoveFolder(PRInt64 aFolder, PRInt64 aNewParent, PRInt32 aIndex) -{ - // You can pass -1 to indicate append, but no other negative number is allowed - if (aIndex < -1) - return NS_ERROR_INVALID_ARG; - - mozIStorageConnection *dbConn = DBConn(); - mozStorageTransaction transaction(dbConn, PR_FALSE); - - nsCOMPtr statement; - nsresult rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT parent, position FROM moz_bookmarks WHERE folder_child = ?1"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - PRInt64 parent; - PRInt32 oldIndex; - { - mozStorageStatementScoper scope(statement); - rv = statement->BindInt64Parameter(0, aFolder); - 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); - oldIndex = statement->AsInt32(1); - } - - // Make sure aNewParent is not aFolder or a subfolder of aFolder - { - mozStorageStatementScoper scope(statement); - PRInt64 p = aNewParent; - - while (p) { - if (p == aFolder) { - return NS_ERROR_INVALID_ARG; - } - - rv = statement->BindInt64Parameter(0, p); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool results; - rv = statement->ExecuteStep(&results); - NS_ENSURE_SUCCESS(rv, rv); - p = results ? statement->AsInt64(0) : 0; - } - } - - PRInt32 newIndex; - if (aIndex == -1) { - newIndex = FolderCount(aNewParent); - // If the parent remains the same, then the folder is really being moved - // to count - 1 (since it's being removed from the old position) - if (parent == aNewParent) { - --newIndex; - } - } else { - newIndex = aIndex; - - if (parent == aNewParent && newIndex > oldIndex) { - // when an item is being moved lower in the same folder, the new index - // refers to the index before it was removed. Removal causes everything - // to shift up. - --newIndex; - } - } - - if (aNewParent == parent && newIndex == oldIndex) { - // Nothing to do! - return NS_OK; - } - - // First we remove the item from its old position. - nsCAutoString buffer; - buffer.AssignLiteral("DELETE FROM moz_bookmarks WHERE folder_child = "); - buffer.AppendInt(aFolder); - rv = dbConn->ExecuteSimpleSQL(buffer); - NS_ENSURE_SUCCESS(rv, rv); - - // Now renumber to account for the move - if (parent == aNewParent) { - // We can optimize the updates if moving within the same container. - // We only shift the items between the old and new positions, since the - // insertion will offset the deletion. - if (oldIndex > newIndex) { - rv = AdjustIndices(parent, newIndex, oldIndex - 1, 1); - } else { - rv = AdjustIndices(parent, oldIndex + 1, newIndex, -1); - } - } else { - // We're moving between containers, so this happens in two steps. - // First, fill the hole from the deletion. - rv = AdjustIndices(parent, oldIndex + 1, PR_INT32_MAX, -1); - NS_ENSURE_SUCCESS(rv, rv); - // Now, make room in the new parent for the insertion. - rv = AdjustIndices(aNewParent, newIndex, PR_INT32_MAX, 1); - } - NS_ENSURE_SUCCESS(rv, rv); - - { - nsCOMPtr statement; - rv = dbConn->CreateStatement(NS_LITERAL_CSTRING( - "INSERT INTO moz_bookmarks (folder_child, parent, position) " - "VALUES (?1, ?2, ?3)"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->BindInt64Parameter(0, aFolder); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindInt64Parameter(1, aNewParent); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindInt32Parameter(2, newIndex); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - } - - rv = transaction.Commit(); - NS_ENSURE_SUCCESS(rv, rv); - - // notify bookmark observers - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - OnFolderMoved(aFolder, parent, oldIndex, - aNewParent, newIndex)) - - // notify remote container provider if there is one - nsCAutoString type; - rv = GetFolderType(aFolder, type); - NS_ENSURE_SUCCESS(rv, rv); - if (! type.IsEmpty()) { - nsCOMPtr container = - do_GetService(type.get(), &rv); - if (NS_SUCCEEDED(rv)) { - rv = container->OnContainerMoved(aFolder, aNewParent, newIndex); - NS_ENSURE_SUCCESS(rv, rv); - } - } - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::GetChildFolder(PRInt64 aFolder, const nsAString& aSubFolder, - PRInt64* _result) -{ - // note: we allow empty folder names - nsresult rv; - if (aFolder == 0) - return NS_ERROR_INVALID_ARG; - - // If this gets used a lot, we'll want a precompiled statement - nsCOMPtr statement; - rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING("SELECT c.id FROM moz_bookmarks a JOIN moz_bookmarks_folders c ON a.folder_child = c.id WHERE a.parent = ?1 AND c.name = ?2"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - statement->BindInt64Parameter(0, aFolder); - statement->BindStringParameter(1, aSubFolder); - - PRBool hasResult = PR_FALSE; - rv = statement->ExecuteStep(&hasResult); - NS_ENSURE_SUCCESS(rv, rv); - - if (! hasResult) { - // item not found - *_result = 0; - return NS_OK; - } - - return statement->GetInt64(0, _result); -} - -NS_IMETHODIMP -nsNavBookmarks::SetItemTitle(nsIURI *aURI, const nsAString &aTitle) -{ - return History()->SetPageUserTitle(aURI, aTitle); -} - - -NS_IMETHODIMP -nsNavBookmarks::GetItemTitle(nsIURI *aURI, nsAString &aTitle) -{ - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); - history->SyncDB(); - - 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) { - aTitle.SetIsVoid(PR_TRUE); - return NS_OK; - } - - // First check for a user title - if (!statement->IsNull(nsNavHistory::kGetInfoIndex_UserTitle)) - return statement->GetString(nsNavHistory::kGetInfoIndex_UserTitle, aTitle); - - // If there is no user title, check for a history title. - return statement->GetString(nsNavHistory::kGetInfoIndex_Title, aTitle); -} - -NS_IMETHODIMP -nsNavBookmarks::SetFolderTitle(PRInt64 aFolder, const nsAString &aTitle) -{ - nsCOMPtr statement; - nsresult rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING("UPDATE moz_bookmarks_folders SET name = ?2 WHERE id = ?1"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->BindInt64Parameter(0, aFolder); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindStringParameter(1, aTitle); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - 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); -} - - -nsresult -nsNavBookmarks::GetFolderType(PRInt64 aFolder, nsACString &aType) -{ - 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->GetUTF8String(kGetFolderInfoIndex_Type, aType); -} - -nsresult -nsNavBookmarks::ResultNodeForFolder(PRInt64 aID, - nsNavHistoryQueryOptions *aOptions, - nsNavHistoryResultNode **aNode) -{ - mozStorageStatementScoper scope(mDBGetFolderInfo); - mDBGetFolderInfo->BindInt64Parameter(0, aID); - - PRBool results; - nsresult rv = mDBGetFolderInfo->ExecuteStep(&results); - NS_ENSURE_SUCCESS(rv, rv); - NS_ASSERTION(results, "ResultNodeForFolder expects a valid folder id"); - - // type (empty for normal ones, nonempty for container providers) - nsCAutoString folderType; - rv = mDBGetFolderInfo->GetUTF8String(kGetFolderInfoIndex_Type, folderType); - NS_ENSURE_SUCCESS(rv, rv); - - // title - nsCAutoString title; - rv = mDBGetFolderInfo->GetUTF8String(kGetFolderInfoIndex_Title, title); - - *aNode = new nsNavHistoryFolderResultNode(title, aOptions, aID, folderType); - if (! *aNode) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(*aNode); - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::GetFolderURI(PRInt64 aFolder, nsIURI **aURI) -{ - // Create a query for the folder; the URI is the querystring from that - // query. We could create a proper query and serialize it, which might - // make it less prone to breakage since we'd only have one code path. - // However, this gets called a lot (every time we make a folder node) - // and constructing fake queries and options each time just to - // serialize them would be a waste. Therefore, we just synthesize the - // correct string here. - // - // If you change this, change IsSimpleFolderURI which detects this string. - nsCAutoString spec("place:folder="); - spec.AppendInt(aFolder); - spec.AppendLiteral("&group=3"); // GROUP_BY_FOLDER - return NS_NewURI(aURI, spec); -} - -NS_IMETHODIMP -nsNavBookmarks::GetFolderReadonly(PRInt64 aFolder, PRBool *aResult) -{ - // Ask the folder's nsIRemoteContainer for the readonly property. - *aResult = PR_FALSE; - nsCAutoString type; - nsresult rv = GetFolderType(aFolder, type); - NS_ENSURE_SUCCESS(rv, rv); - - if (!type.IsEmpty()) { - nsCOMPtr container = - do_GetService(type.get(), &rv); - if (NS_SUCCEEDED(rv)) { - rv = container->GetChildrenReadOnly(aResult); - NS_ENSURE_SUCCESS(rv, rv); - } - } - - return NS_OK; -} - -nsresult -nsNavBookmarks::QueryFolderChildren(PRInt64 aFolderId, - nsNavHistoryQueryOptions *aOptions, - nsCOMArray *aChildren) -{ - nsresult rv; - mozStorageStatementScoper scope(mDBGetChildren); - - rv = mDBGetChildren->BindInt64Parameter(0, aFolderId); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBGetChildren->BindInt32Parameter(1, 0); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBGetChildren->BindInt32Parameter(2, PR_INT32_MAX); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool results; - - nsCOMPtr options = do_QueryInterface(aOptions, &rv); - PRInt32 index = -1; - while (NS_SUCCEEDED(mDBGetChildren->ExecuteStep(&results)) && results) { - - // The results will be in order of index. Even if we don't add a node - // because it was excluded, we need to count it's index, so do that - // before doing anything else. Index was initialized to -1 above, so - // it will start counting at 0 the first time through the loop. - index ++; - - PRBool isFolder = !mDBGetChildren->IsNull(kGetChildrenIndex_FolderChild); - nsCOMPtr node; - if (isFolder) { - PRInt64 folder = mDBGetChildren->AsInt64(kGetChildrenIndex_FolderChild); - - if (options->ExcludeReadOnlyFolders()) { - // see if it's read only and skip it - PRBool readOnly = PR_FALSE; - GetFolderReadonly(folder, &readOnly); - if (readOnly) - continue; // skip - } - - rv = ResultNodeForFolder(folder, aOptions, getter_AddRefs(node)); - if (NS_FAILED(rv)) - continue; - } else if (mDBGetChildren->IsNull(kGetChildrenIndex_ItemChild)) { - // separator - if (aOptions->ExcludeItems()) { - continue; - } - node = new nsNavHistorySeparatorResultNode(); - NS_ENSURE_TRUE(node, NS_ERROR_OUT_OF_MEMORY); - } else { - rv = History()->RowToResult(mDBGetChildren, options, - getter_AddRefs(node)); - NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 nodeType; - node->GetType(&nodeType); - if ((nodeType == nsINavHistoryResultNode::RESULT_TYPE_QUERY && - aOptions->ExcludeQueries()) || - (nodeType != nsINavHistoryResultNode::RESULT_TYPE_QUERY && - nodeType != nsINavHistoryResultNode::RESULT_TYPE_FOLDER && - aOptions->ExcludeItems())) { - continue; - } - } - - // this method fills all bookmark queries, so we store the index of the - // item in its parent - node->mBookmarkIndex = index; - - NS_ENSURE_TRUE(aChildren->AppendObject(node), NS_ERROR_OUT_OF_MEMORY); - } - return NS_OK; -} - -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) -{ - nsNavHistory* history = History(); - NS_ENSURE_TRUE(history, NS_ERROR_UNEXPECTED); - - // convert the URL to an ID - PRInt64 urlID; - nsresult rv = history->GetUrlIdFor(aURI, &urlID, PR_FALSE); - NS_ENSURE_SUCCESS(rv, rv); - if (! urlID) { - // never seen this before, not even in history - *aBookmarked = PR_FALSE; - return NS_OK; - } - - PRInt64 bookmarkedID; - PRBool foundOne = mBookmarksHash.Get(urlID, &bookmarkedID); - - // IsBookmarked only tests if this exact URI is bookmarked, so we need to - // check that the destination matches - if (foundOne) - *aBookmarked = (urlID == bookmarkedID); - else - *aBookmarked = PR_FALSE; - -#ifdef DEBUG - // sanity check for the bookmark hashtable - PRBool realBookmarked; - rv = IsBookmarkedInDatabase(urlID, &realBookmarked); - NS_ASSERTION(realBookmarked == *aBookmarked, - "Bookmark hash table out-of-sync with the database"); -#endif - - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::GetBookmarkedURIFor(nsIURI* aURI, nsIURI** _retval) -{ - *_retval = nsnull; - - nsNavHistory* history = History(); - NS_ENSURE_TRUE(history, NS_ERROR_UNEXPECTED); - - // convert the URL to an ID - PRInt64 urlID; - nsresult rv = history->GetUrlIdFor(aURI, &urlID, PR_FALSE); - NS_ENSURE_SUCCESS(rv, rv); - if (! urlID) { - // never seen this before, not even in history, leave result NULL - return NS_OK; - } - - PRInt64 bookmarkID; - if (mBookmarksHash.Get(urlID, &bookmarkID)) { - // found one, convert ID back to URL. This statement is NOT refcounted - mozIStorageStatement* statement = history->DBGetIdPageInfo(); - NS_ENSURE_TRUE(statement, NS_ERROR_UNEXPECTED); - mozStorageStatementScoper scoper(statement); - - rv = statement->BindInt64Parameter(0, bookmarkID); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMore; - if (NS_SUCCEEDED(statement->ExecuteStep(&hasMore)) && hasMore) { - nsCAutoString spec; - statement->GetUTF8String(nsNavHistory::kGetInfoIndex_URL, spec); - return NS_NewURI(_retval, spec); - } - } - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::ChangeBookmarkURI(nsIURI *aOldURI, nsIURI *aNewURI) -{ - nsresult rv; - - if (!aOldURI || !aNewURI) - return NS_ERROR_NULL_POINTER; - - // This method is only meaningful if oldURI is actually bookmarked. - PRBool bookmarked = PR_FALSE; - rv = IsBookmarked(aOldURI, &bookmarked); - NS_ENSURE_SUCCESS(rv, rv); - if (!bookmarked) - return NS_ERROR_INVALID_ARG; - - PRBool equal = PR_FALSE; - rv = aOldURI->Equals(aNewURI, &equal); - NS_ENSURE_SUCCESS(rv, rv); - if (equal) // no-op - return NS_OK; - - // Now that we're satisfied with the quality of our input, the - // actual work starts here. - - nsTArray folders; - rv = GetBookmarkFoldersTArray(aOldURI, &folders); - NS_ENSURE_SUCCESS(rv, rv); - - // The bookmark operations within the area in which "batch" is in scope - // will be within a batched operation. This allows the batch to be - // closed properly if we exit early due to an error condition. - nsBookmarksUpdateBatcher batch; - - // in folders, replace all instances of old URI with new URI - for (PRUint32 i = 0; i < folders.Length(); i++) { - rv = ReplaceItem(folders[i], aOldURI, aNewURI); - NS_ENSURE_SUCCESS(rv, rv); - } - - // copy title from old URI to new URI - nsAutoString title; - rv = GetItemTitle(aOldURI, title); - NS_ENSURE_SUCCESS(rv, rv); - if (!title.IsEmpty()) { - rv = SetItemTitle(aNewURI, title); - NS_ENSURE_SUCCESS(rv, rv); - } - - // copy keyword (shortcut) from old URI to new URI - nsAutoString keyword; - rv = GetKeywordForURI(aOldURI, keyword); - NS_ENSURE_SUCCESS(rv, rv); - if (!keyword.IsEmpty()) { - rv = SetKeywordForURI(aNewURI, keyword); - NS_ENSURE_SUCCESS(rv, rv); - } - - // copy annotations from old URI to new URI - nsAnnotationService* annoService = - nsAnnotationService::GetAnnotationService(); - NS_ENSURE_TRUE(annoService, NS_ERROR_UNEXPECTED); - rv = annoService->CopyAnnotations(aOldURI, aNewURI, PR_TRUE); - NS_ENSURE_SUCCESS(rv, rv); - - // associate favicon from old URI (if present) with new URI (if none already) - nsFaviconService* faviconService = nsFaviconService::GetFaviconService(); - NS_ENSURE_TRUE(faviconService, NS_ERROR_UNEXPECTED); - nsCOMPtr sourceFaviconURI; - rv = faviconService->GetFaviconForPage(aOldURI, - getter_AddRefs(sourceFaviconURI)); - if (NS_SUCCEEDED(rv)) { // oldURI has favicon - nsCOMPtr destFaviconURI; - rv = faviconService->GetFaviconForPage(aNewURI, - getter_AddRefs(destFaviconURI)); - if (NS_FAILED(rv)) { // newURI has no favicon - rv = faviconService->SetFaviconUrlForPage(aNewURI, sourceFaviconURI); - NS_ENSURE_SUCCESS(rv, rv); - } - } - - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::GetBookmarkFoldersTArray(nsIURI *aURI, - nsTArray *aResult) -{ - mozStorageStatementScoper scope(mDBFindURIBookmarks); - mozStorageTransaction transaction(DBConn(), PR_FALSE); - - nsresult rv = BindStatementURI(mDBFindURIBookmarks, 0, aURI); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool more; - while (NS_SUCCEEDED((rv = mDBFindURIBookmarks->ExecuteStep(&more))) && more) { - if (! aResult->AppendElement( - mDBFindURIBookmarks->AsInt64(kFindBookmarksIndex_Parent))) - return NS_ERROR_OUT_OF_MEMORY; - } - - NS_ENSURE_SUCCESS(rv, rv); - - return transaction.Commit(); -} - -NS_IMETHODIMP -nsNavBookmarks::GetBookmarkFolders(nsIURI *aURI, PRUint32 *aCount, - PRInt64 **aFolders) -{ - *aCount = 0; - *aFolders = nsnull; - nsresult rv; - nsTArray folders; - - // Get the information from the DB as a TArray - rv = GetBookmarkFoldersTArray(aURI, &folders); - NS_ENSURE_SUCCESS(rv, rv); - - // Copy the results into a new array for output - if (folders.Length()) { - *aFolders = NS_STATIC_CAST(PRInt64*, - nsMemory::Alloc(sizeof(PRInt64) * folders.Length())); - if (! *aFolders) - return NS_ERROR_OUT_OF_MEMORY; - for (PRUint32 i = 0; i < folders.Length(); i ++) - (*aFolders)[i] = folders[i]; - } - *aCount = folders.Length(); - - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::IndexOfItem(PRInt64 aFolder, nsIURI *aItem, PRInt32 *aIndex) -{ - mozStorageTransaction transaction(DBConn(), PR_FALSE); - - PRInt64 id; - nsresult rv = History()->GetUrlIdFor(aItem, &id, PR_FALSE); - NS_ENSURE_SUCCESS(rv, rv); - if (id == 0) { - *aIndex = -1; - return NS_OK; - } - - mozStorageStatementScoper scope(mDBIndexOfItem); - mDBIndexOfItem->BindInt64Parameter(0, id); - mDBIndexOfItem->BindInt64Parameter(1, aFolder); - PRBool results; - rv = mDBIndexOfItem->ExecuteStep(&results); - NS_ENSURE_SUCCESS(rv, rv); - if (!results) { - *aIndex = -1; - return NS_OK; - } - - *aIndex = mDBIndexOfItem->AsInt32(0); - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::IndexOfFolder(PRInt64 aParent, - PRInt64 aFolder, PRInt32 *aIndex) -{ - mozStorageTransaction transaction(DBConn(), PR_FALSE); - - mozStorageStatementScoper scope(mDBIndexOfFolder); - mDBIndexOfFolder->BindInt64Parameter(0, aFolder); - mDBIndexOfFolder->BindInt64Parameter(1, aParent); - PRBool results; - nsresult rv = mDBIndexOfFolder->ExecuteStep(&results); - NS_ENSURE_SUCCESS(rv, rv); - if (!results) { - *aIndex = -1; - return NS_OK; - } - - *aIndex = mDBIndexOfFolder->AsInt32(0); - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::SetKeywordForURI(nsIURI* aURI, const nsAString& aKeyword) -{ - nsresult rv; - mozStorageTransaction transaction(DBConn(), PR_FALSE); - - nsNavHistory *history = History(); - NS_ENSURE_TRUE(history, NS_ERROR_UNEXPECTED); - PRInt64 pageId; - nsCOMPtr statement; - - // Shortcuts are always lowercased internally. - nsAutoString kwd(aKeyword); - ToLowerCase(kwd); - - // When we are clearing a keyword, don't force URI creation. If they give - // us a brand new URI and an empty keyword, there is obviously nothing to do - PRBool forceURICreation = ! kwd.IsEmpty(); - rv = history->GetUrlIdFor(aURI, &pageId, forceURICreation); - if (! pageId) - return NS_OK; // no keyword & URL not found: nothing to do - - if (aURI) { - // delete any existing keyword for the given URI, only create the ID if - rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING( - "DELETE FROM moz_keywords WHERE page_id = ?1"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindInt64Parameter(0, pageId); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - } - if (! kwd.IsEmpty()) { - rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING( - "DELETE FROM moz_keywords WHERE keyword = ?1"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindStringParameter(0, kwd); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - } - - // when either is empty, that was asking us to clear the value, and we're done - if (! aURI || kwd.IsEmpty()) - return transaction.Commit(); - - // otherwise, we have a URI/keyword pair and want to create it... - - // this statement will overwrite any old keyword with that value since the - // keyword column is unique and we use "OR REPLACE" conflict resolution - rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING( - "INSERT OR REPLACE INTO moz_keywords (keyword, page_id) VALUES (?1, ?2)"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindStringParameter(0, kwd); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindInt64Parameter(1, pageId); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - return transaction.Commit(); -} - -NS_IMETHODIMP -nsNavBookmarks::GetKeywordForURI(nsIURI* aURI, nsAString& aKeyword) -{ - aKeyword.Truncate(0); - - mozStorageStatementScoper scoper(mDBGetKeywordForURI); - nsresult rv = BindStatementURI(mDBGetKeywordForURI, 0, aURI); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMore = PR_FALSE; - rv = mDBGetKeywordForURI->ExecuteStep(&hasMore); - if (NS_FAILED(rv) || ! hasMore) { - aKeyword.SetIsVoid(PR_TRUE); - return NS_OK; // not found: return void keyword string - } - - // found, get the keyword - return mDBGetKeywordForURI->GetString(0, aKeyword); -} - -NS_IMETHODIMP -nsNavBookmarks::GetURIForKeyword(const nsAString& aKeyword, nsIURI** aURI) -{ - *aURI = nsnull; - if (aKeyword.IsEmpty()) - return NS_ERROR_INVALID_ARG; - - // Shortcuts are always lowercased internally. - nsAutoString kwd(aKeyword); - ToLowerCase(kwd); - - mozStorageStatementScoper scoper(mDBGetURIForKeyword); - nsresult rv = mDBGetURIForKeyword->BindStringParameter(0, kwd); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMore = PR_FALSE; - rv = mDBGetURIForKeyword->ExecuteStep(&hasMore); - if (NS_FAILED(rv) || ! hasMore) - return NS_OK; // not found: leave URI null - - // found, get the URI - nsCAutoString spec; - rv = mDBGetURIForKeyword->GetUTF8String(0, spec); - NS_ENSURE_SUCCESS(rv, rv); - return NS_NewURI(aURI, spec); -} - -NS_IMETHODIMP -nsNavBookmarks::BeginUpdateBatch() -{ - if (mBatchLevel++ == 0) { - mozIStorageConnection* conn = DBConn(); - PRBool transactionInProgress = PR_TRUE; // default to no transaction on err - conn->GetTransactionInProgress(&transactionInProgress); - mBatchHasTransaction = ! transactionInProgress; - if (mBatchHasTransaction) - conn->BeginTransaction(); - - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - OnBeginUpdateBatch()) - } - mozIStorageConnection *dbConn = DBConn(); - mozStorageTransaction transaction(dbConn, PR_FALSE); - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::EndUpdateBatch() -{ - if (--mBatchLevel == 0) { - if (mBatchHasTransaction) - DBConn()->CommitTransaction(); - mBatchHasTransaction = PR_FALSE; - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - OnEndUpdateBatch()) - } - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::AddObserver(nsINavBookmarkObserver *aObserver, - PRBool aOwnsWeak) -{ - return mObservers.AppendWeakElement(aObserver, aOwnsWeak); -} - -NS_IMETHODIMP -nsNavBookmarks::RemoveObserver(nsINavBookmarkObserver *aObserver) -{ - return mObservers.RemoveWeakElement(aObserver); -} - -// 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::OnVisit(nsIURI *aURI, PRInt64 aVisitID, PRTime aTime, - PRInt64 aSessionID, PRInt64 aReferringID, - PRUint32 aTransitionType) -{ - // If the page is bookmarked, we need to notify observers - PRBool bookmarked = PR_FALSE; - IsBookmarked(aURI, &bookmarked); - if (bookmarked) { - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - OnItemVisited(aURI, aVisitID, aTime)) - } - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::OnDeleteURI(nsIURI *aURI) -{ - // If the page is bookmarked, we need to notify observers - PRBool bookmarked = PR_FALSE; - IsBookmarked(aURI, &bookmarked); - if (bookmarked) { - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - OnItemChanged(aURI, NS_LITERAL_CSTRING("cleartime"), - EmptyString())) - } - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::OnClearHistory() -{ - // TODO(bryner): we should notify on visited-time change for all URIs - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::OnTitleChanged(nsIURI* aURI, const nsAString& aPageTitle, - const nsAString& aUserTitle, - PRBool aIsUserTitleChanged) -{ - PRBool bookmarked = PR_FALSE; - IsBookmarked(aURI, &bookmarked); - if (bookmarked) { - if (aUserTitle.IsVoid()) { - // use "real" title because the user title is NULL. Either the user title - // was "unset" or the page title changed. - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - OnItemChanged(aURI, NS_LITERAL_CSTRING("title"), - aPageTitle)); - } else if (aIsUserTitleChanged) { - // there is a user title and it changed - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - OnItemChanged(aURI, NS_LITERAL_CSTRING("title"), - aUserTitle)); - } - } - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::OnPageChanged(nsIURI *aURI, PRUint32 aWhat, - const nsAString &aValue) -{ - PRBool bookmarked = PR_FALSE; - IsBookmarked(aURI, &bookmarked); - if (bookmarked) { - if (aWhat == nsINavHistoryObserver::ATTRIBUTE_FAVICON) { - ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver, - OnItemChanged(aURI, NS_LITERAL_CSTRING("favicon"), - aValue)); - } - } - return NS_OK; -} - -NS_IMETHODIMP -nsNavBookmarks::OnPageExpired(nsIURI* aURI, PRTime aVisitTime, - PRBool aWholeEntry) -{ - // pages that are bookmarks shouldn't expire, so we don't need to handle it - return NS_OK; -} diff --git a/mozilla/browser/components/places/src/nsNavBookmarks.h b/mozilla/browser/components/places/src/nsNavBookmarks.h deleted file mode 100644 index 0229b2a8b6c..00000000000 --- a/mozilla/browser/components/places/src/nsNavBookmarks.h +++ /dev/null @@ -1,263 +0,0 @@ -/* -*- 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 ***** */ - -#ifndef nsNavBookmarks_h_ -#define nsNavBookmarks_h_ - -#include "nsINavBookmarksService.h" -#include "nsIStringBundle.h" -#include "nsITransaction.h" -#include "nsNavHistory.h" -#include "nsNavHistoryResult.h" // need for Int64 hashtable -#include "nsToolkitCompsCID.h" - -class nsIOutputStream; - -class nsNavBookmarks : public nsINavBookmarksService, - public nsINavHistoryObserver -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSINAVBOOKMARKSSERVICE - NS_DECL_NSINAVHISTORYOBSERVER - - nsNavBookmarks(); - nsresult Init(); - - // called by nsNavHistory::Init - static nsresult InitTables(mozIStorageConnection* aDBConn); - - 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; - } - - nsresult AddBookmarkToHash(PRInt64 aBookmarkId, PRTime aMinTime); - - nsresult ResultNodeForFolder(PRInt64 aID, nsNavHistoryQueryOptions *aOptions, - nsNavHistoryResultNode **aNode); - - // Find all the children of a folder, using the given query and options. - // For each child, a ResultNode is created and added to |children|. - // The results are ordered by folder position. - nsresult QueryFolderChildren(PRInt64 aFolderId, - nsNavHistoryQueryOptions *aOptions, - nsCOMArray *children); - - // If aFolder is -1, uses the autoincrement id for folder index. Returns - // the index of the new folder in aIndex, whether it was passed in or - // generated by autoincrement. If aSendNotifications is true, sends - // OnFolderAdded notifications to bookmark observers. - nsresult CreateFolderWithID(PRInt64 aFolder, PRInt64 aParent, - const nsAString& title, - PRBool aSendNotifications, - PRInt32 *aIndex, PRInt64* aNewFolder); - - // Creates a new container of the given type. If aFolder is -1, uses the - // autoincrement id for folder index. Sends OnFolderAdded notifications - // to all observers after the folder has been created and its type has - // been set. - nsresult CreateContainerWithID(PRInt64 aFolder, PRInt64 aParent, - const nsAString& title, const nsAString& type, - PRInt32 aIndex, PRInt64* aNewFolder); - - // Returns a statement to get information about a folder id - mozIStorageStatement* DBGetFolderInfo() { return mDBGetFolderInfo; } - // constants for the above statement - static const PRInt32 kGetFolderInfoIndex_FolderID; - static const PRInt32 kGetFolderInfoIndex_Title; - static const PRInt32 kGetFolderInfoIndex_Type; - -private: - static nsNavBookmarks *sInstance; - - ~nsNavBookmarks(); - - nsresult InitRoots(); - nsresult CreateRoot(mozIStorageStatement* aGetRootStatement, - const nsCString& name, PRInt64* aID, - PRBool* aWasCreated); - - nsresult AdjustIndices(PRInt64 aFolder, - PRInt32 aStartIndex, PRInt32 aEndIndex, - PRInt32 aDelta); - PRInt32 FolderCount(PRInt64 aFolder); - nsresult GetFolderType(PRInt64 aFolder, nsACString &aType); - - // remove me when there is better query initialization - nsNavHistory* History() { return nsNavHistory::GetHistoryService(); } - - mozIStorageStatement* DBGetURLPageInfo() - { return History()->DBGetURLPageInfo(); } - - mozIStorageConnection* DBConn() { return History()->GetStorageConnection(); } - - nsMaybeWeakPtrArray mObservers; - PRInt64 mRoot; - PRInt64 mBookmarksRoot; - PRInt64 mToolbarRoot; - PRInt64 mTagRoot; - - // the level of nesting of batches, 0 when no batches are open - PRInt32 mBatchLevel; - - // true if the outermost batch has an associated transaction that should - // be committed when our batch level reaches 0 again. - PRBool mBatchHasTransaction; - - // This stores a mapping from all pages reachable by redirects from bookmarked - // pages to the bookmarked page. Used by GetBookmarkedURIFor. - nsDataHashtable mBookmarksHash; - nsresult FillBookmarksHash(); - nsresult RecursiveAddBookmarkHash(PRInt64 aBookmarkId, PRInt64 aCurrentSource, - PRTime aMinTime); - nsresult UpdateBookmarkHashOnRemove(PRInt64 aBookmarkId); - - nsresult GetParentAndIndexOfFolder(PRInt64 aFolder, PRInt64* aParent, - PRInt32* aIndex); - - nsresult IsBookmarkedInDatabase(PRInt64 aBookmarkID, PRBool* aIsBookmarked); - - nsCOMPtr mDBGetFolderInfo; // kGetFolderInfoIndex_* results - - 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; - - nsCOMPtr mDBIndexOfItem; - nsCOMPtr mDBIndexOfFolder; - nsCOMPtr mDBGetChildAt; - - nsCOMPtr mDBGetRedirectDestinations; - - // keywords - nsCOMPtr mDBGetKeywordForURI; - nsCOMPtr mDBGetURIForKeyword; - - nsCOMPtr mBundle; - - class RemoveFolderTransaction : public nsITransaction { - public: - RemoveFolderTransaction(PRInt64 aID, PRInt64 aParent, - const nsAString& aTitle, PRInt32 aIndex, - const nsACString& aType) - : mID(aID), - mParent(aParent), - mIndex(aIndex){ - mTitle = aTitle; - mType = aType; - } - - NS_DECL_ISUPPORTS - - NS_IMETHOD DoTransaction() { - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - return bookmarks->RemoveFolder(mID); - } - - NS_IMETHOD UndoTransaction() { - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - PRInt64 newFolder; - // If the transaction has no specific type, default to a folder, and send notifications - // to all bookmark observers (controlled by the PR_TRUE argument to CreateFolderWithID). - if (mType.IsEmpty()) - return bookmarks->CreateFolderWithID(mID, mParent, mTitle, PR_TRUE, &mIndex, &newFolder); - nsAutoString type; type.AssignWithConversion(mType); - return bookmarks->CreateContainerWithID(mID, mParent, mTitle, type, mIndex, &newFolder); - } - - NS_IMETHOD RedoTransaction() { - return DoTransaction(); - } - - NS_IMETHOD GetIsTransient(PRBool* aResult) { - *aResult = PR_FALSE; - return NS_OK; - } - - NS_IMETHOD Merge(nsITransaction* aTransaction, PRBool* aResult) { - *aResult = PR_FALSE; - return NS_OK; - } - - private: - PRInt64 mID; - PRInt64 mParent; - nsString mTitle; - nsCString mType; - PRInt32 mIndex; - }; - - // in nsBookmarksHTML - nsresult ImportBookmarksHTMLInternal(nsIURI* aURL, - PRBool aAllowRootChanges, - PRInt64 aFolder); - nsresult WriteContainer(PRInt64 aFolder, const nsCString& aIndent, - nsIOutputStream* aOutput); - nsresult WriteContainerHeader(PRInt64 aFolder, const nsCString& aIndent, - nsIOutputStream* aOutput); - nsresult WriteContainerTitle(PRInt64 aFolder, nsIOutputStream* aOutput); - nsresult WriteLivemark(PRInt64 aFolderId, const nsCString& aIndent, - nsIOutputStream* aOutput); - nsresult WriteContainerContents(PRInt64 aFolder, const nsCString& aIndent, - nsIOutputStream* aOutput); -}; - -struct nsBookmarksUpdateBatcher -{ - nsBookmarksUpdateBatcher() { nsNavBookmarks::GetBookmarksService()->BeginUpdateBatch(); } - ~nsBookmarksUpdateBatcher() { nsNavBookmarks::GetBookmarksService()->EndUpdateBatch(); } -}; - - -#endif // nsNavBookmarks_h_ diff --git a/mozilla/browser/components/places/src/nsNavHistory.cpp b/mozilla/browser/components/places/src/nsNavHistory.cpp deleted file mode 100644 index 29bd9a6cd74..00000000000 --- a/mozilla/browser/components/places/src/nsNavHistory.cpp +++ /dev/null @@ -1,4302 +0,0 @@ -//* -*- 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 Mozilla History System - * - * 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): - * Brett Wilson (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 -#include "nsNavHistory.h" -#include "nsNavBookmarks.h" -#include "nsMorkHistoryImporter.h" -#include "nsAnnotationService.h" - -#include "nsIArray.h" -#include "nsArrayEnumerator.h" -#include "nsCollationCID.h" -#include "nsCOMPtr.h" -#include "nsCRT.h" -#include "nsDateTimeFormatCID.h" -#include "nsDebug.h" -#include "nsEnumeratorUtils.h" -#include "nsFaviconService.h" -#include "nsIChannelEventSink.h" -#include "nsIComponentManager.h" -#include "nsILocaleService.h" -#include "nsILocalFile.h" -#include "nsIPrefBranch2.h" -#include "nsIServiceManager.h" -#include "nsISimpleEnumerator.h" -#include "nsISupportsPrimitives.h" -#include "nsIURI.h" -#include "nsIURL.h" -#include "nsNetUtil.h" -#include "nsPrintfCString.h" -#include "nsPromiseFlatString.h" -#include "nsString.h" -#include "nsUnicharUtils.h" -#include "prsystem.h" -#include "prtime.h" -#include "prprf.h" - -#include "mozIStorageService.h" -#include "mozIStorageConnection.h" -#include "mozIStorageValueArray.h" -#include "mozIStorageStatement.h" -#include "mozIStorageFunction.h" -#include "mozStorageCID.h" -#include "mozStorageHelper.h" -#include "nsAppDirectoryServiceDefs.h" - -// Microsecond timeout for "recent" events such as typed and bookmark following. -// If you typed it more than this time ago, it's not recent. -// This is 15 minutes m s/m us/s -#define RECENT_EVENT_THRESHOLD (15 * 60 * 1000000) - -// Microseconds ago to look for redirects when updating bookmarks. Used to -// compute the threshold for nsNavBookmarks::AddBookmarkToHash -#define BOOKMARK_REDIRECT_TIME_THRESHOLD (2 * 60 * 100000) - -// The maximum number of things that we will store in the recent events list -// before calling ExpireNonrecentEvents. This number should be big enough so it -// is very difficult to get that many unconsumed events (for example, typed but -// never visited) in the RECENT_EVENT_THRESHOLD. Otherwise, we'll start -// checking each one for every page visit, which will be somewhat slower. -#define RECENT_EVENT_QUEUE_MAX_LENGTH 128 - -// preference ID strings -#define PREF_BRANCH_BASE "browser." -#define PREF_BROWSER_HISTORY_EXPIRE_DAYS "history_expire_days" -#define PREF_AUTOCOMPLETE_ONLY_TYPED "urlbar.matchOnlyTyped" -#define PREF_AUTOCOMPLETE_ENABLED "urlbar.autocomplete.enabled" -#define PREF_DB_CACHE_PERCENTAGE "history_cache_percentage" - -// Default (integer) value of PREF_DB_CACHE_PERCENTAGE from 0-100 -// This is 6% of machine memory, giving 15MB for a user with 256MB of memory. -// The most that will be used is the size of the DB file. Normal history sizes -// look like 10MB would be a high average for a typical user, so the maximum -// should not normally be required. -#define DEFAULT_DB_CACHE_PERCENTAGE 6 - -// The default page size the database should use. This must be a power of 2. -// Larger pages may mean less paging, but when something is changed, the -// entire page is written to the journal and then the main file, meaning a -// lot more data has to be handled. -#define DEFAULT_DB_PAGE_SIZE 1024 - -// the value of mLastNow expires every 3 seconds -#define HISTORY_EXPIRE_NOW_TIMEOUT (3 * PR_MSEC_PER_SEC) - -// see bug #319004 -- clamp title and URL to generously-large but not too large -// length -#define HISTORY_URI_LENGTH_MAX 65536 -#define HISTORY_TITLE_LENGTH_MAX 4096 - -// Lazy adding - -#ifdef LAZY_ADD - -// time that we'll wait before committing messages -#define LAZY_MESSAGE_TIMEOUT (3 * PR_MSEC_PER_SEC) - -// the maximum number of times we'll postpone a lazy timer before committing -// See StartLazyTimer() -#define MAX_LAZY_TIMER_DEFERMENTS 2 - -#endif // LAZY_ADD - -NS_IMPL_ADDREF(nsNavHistory) -NS_IMPL_RELEASE(nsNavHistory) - -NS_INTERFACE_MAP_BEGIN(nsNavHistory) - NS_INTERFACE_MAP_ENTRY(nsINavHistoryService) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIGlobalHistory2, nsIGlobalHistory3) - NS_INTERFACE_MAP_ENTRY(nsIGlobalHistory3) - NS_INTERFACE_MAP_ENTRY(nsIBrowserHistory) - NS_INTERFACE_MAP_ENTRY(nsIObserver) - NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) - NS_INTERFACE_MAP_ENTRY(nsIAutoCompleteSearch) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsINavHistoryService) -NS_INTERFACE_MAP_END - -static nsresult GetReversedHostname(nsIURI* aURI, nsAString& host); -static void GetReversedHostname(const nsString& aForward, nsAString& aReversed); -static void GetSubstringFromNthDot(const nsCString& aInput, PRInt32 aStartingSpot, - PRInt32 aN, PRBool aIncludeDot, - nsACString& aSubstr); -static nsresult GenerateTitleFromURI(nsIURI* aURI, nsAString& aTitle); -static PRInt32 GetTLDCharCount(const nsCString& aHost); -static PRInt32 GetTLDType(const nsCString& aHostTail); -static PRBool IsNumericHostName(const nsCString& aHost); -static PRInt64 GetSimpleBookmarksQueryFolder( - const nsCOMArray& aQueries); -static void ParseSearchQuery(const nsString& aQuery, nsStringArray* aTerms); - -inline void ReverseString(const nsString& aInput, nsAString& aReversed) -{ - aReversed.Truncate(0); - for (PRInt32 i = aInput.Length() - 1; i >= 0; i --) - aReversed.Append(aInput[i]); -} -inline void parameterString(PRInt32 paramIndex, nsACString& aParamString) -{ - aParamString = nsPrintfCString("?%d", paramIndex + 1); -} - - -// UpdateBatchScoper -// -// This just sets begin/end of batch updates to correspond to C++ scopes so -// we can be sure end always gets called. - -class UpdateBatchScoper -{ -public: - UpdateBatchScoper(nsNavHistory& aNavHistory) : mNavHistory(aNavHistory) - { - mNavHistory.BeginUpdateBatch(); - } - ~UpdateBatchScoper() - { - mNavHistory.EndUpdateBatch(); - } -protected: - nsNavHistory& mNavHistory; -}; - -// if adding a new one, be sure to update nsNavBookmarks statements and -// its kGetChildrenIndex_* constants -const PRInt32 nsNavHistory::kGetInfoIndex_PageID = 0; -const PRInt32 nsNavHistory::kGetInfoIndex_URL = 1; -const PRInt32 nsNavHistory::kGetInfoIndex_Title = 2; -const PRInt32 nsNavHistory::kGetInfoIndex_UserTitle = 3; -const PRInt32 nsNavHistory::kGetInfoIndex_RevHost = 4; -const PRInt32 nsNavHistory::kGetInfoIndex_VisitCount = 5; -const PRInt32 nsNavHistory::kGetInfoIndex_VisitDate = 6; -const PRInt32 nsNavHistory::kGetInfoIndex_FaviconURL = 7; -const PRInt32 nsNavHistory::kGetInfoIndex_SessionId = 8; - -const PRInt32 nsNavHistory::kAutoCompleteIndex_URL = 0; -const PRInt32 nsNavHistory::kAutoCompleteIndex_Title = 1; -const PRInt32 nsNavHistory::kAutoCompleteIndex_VisitCount = 2; -const PRInt32 nsNavHistory::kAutoCompleteIndex_Typed = 3; - -static nsDataHashtable* gTldTypes; -static const char* gQuitApplicationMessage = "quit-application"; -static const char* gXpcomShutdown = "xpcom-shutdown"; - -// annotation names -const char nsNavHistory::kAnnotationPreviousEncoding[] = "history/encoding"; - -nsIAtom* nsNavHistory::sMenuRootAtom = nsnull; -nsIAtom* nsNavHistory::sToolbarRootAtom = nsnull; -nsIAtom* nsNavHistory::sSessionStartAtom = nsnull; -nsIAtom* nsNavHistory::sSessionContinueAtom = nsnull; -nsIAtom* nsNavHistory::sContainerAtom = nsnull; - -nsNavHistory* nsNavHistory::gHistoryService; - -// nsNavHistory::nsNavHistory - -nsNavHistory::nsNavHistory() : mNowValid(PR_FALSE), - mExpireNowTimer(nsnull), - mExpire(this), - mBatchesInProgress(0), - mAutoCompleteOnlyTyped(PR_FALSE) -{ -#ifdef LAZY_ADD - mLazyTimerSet = PR_TRUE; - mLazyTimerDeferments = 0; -#endif - NS_ASSERTION(! gHistoryService, "YOU ARE CREATING 2 COPIES OF THE HISTORY SERVICE. Everything will break."); - gHistoryService = this; - - sMenuRootAtom = NS_NewAtom("menu-root"); - sToolbarRootAtom = NS_NewAtom("toolbar-root"); - sSessionStartAtom = NS_NewAtom("session-start"); - sSessionContinueAtom = NS_NewAtom("session-continue"); - sContainerAtom = NS_NewAtom("container"); -} - - -// nsNavHistory::~nsNavHistory - -nsNavHistory::~nsNavHistory() -{ - // remove the static reference to the service. Check to make sure its us - // in case somebody creates an extra instance of the service. - NS_ASSERTION(gHistoryService == this, "YOU CREATED 2 COPIES OF THE HISTORY SERVICE."); - gHistoryService = nsnull; - - NS_IF_RELEASE(sMenuRootAtom); - NS_IF_RELEASE(sToolbarRootAtom); - NS_IF_RELEASE(sSessionStartAtom); - NS_IF_RELEASE(sSessionContinueAtom); - NS_IF_RELEASE(sContainerAtom); -} - - -// nsNavHistory::Init - -nsresult -nsNavHistory::Init() -{ - nsresult rv; - - gExpandedItems.Init(128); - - // prefs (must be before DB init, which uses the pref service) - nsCOMPtr prefService = - do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = prefService->GetBranch(PREF_BRANCH_BASE, getter_AddRefs(mPrefBranch)); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool doImport; - rv = InitDB(&doImport); - NS_ENSURE_SUCCESS(rv, rv); - rv = InitStatements(); - NS_ENSURE_SUCCESS(rv, rv); -#ifdef IN_MEMORY_LINKS - rv = InitMemDB(); - NS_ENSURE_SUCCESS(rv, rv); -#endif - - rv = InitAutoComplete(); - NS_ENSURE_SUCCESS(rv, rv); - - // extract the last session ID so we know where to pick up. There is no index - // over sessions so the naive statement "SELECT MAX(session) FROM - // moz_historyvisit" won't have good performance. Instead we select the - // session of the last visited page because we do have indices over dates. - // We still do MAX(session) in case there are duplicate sessions for the same - // date, but there will generally be very few (1) of these. - { - nsCOMPtr selectSession; - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT MAX(session) FROM " - "(SELECT MAX(visit_date) AS visit_date FROM moz_historyvisit) maxvd " - "JOIN moz_historyvisit v ON maxvd.visit_date = v.visit_date"), - getter_AddRefs(selectSession)); - NS_ENSURE_SUCCESS(rv, rv); - PRBool hasSession; - if (NS_SUCCEEDED(selectSession->ExecuteStep(&hasSession)) && hasSession) - mLastSessionID = selectSession->AsInt64(0); - else - mLastSessionID = 1; - } - - // string bundle for localization - nsCOMPtr bundleService = - do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = bundleService->CreateBundle( - "chrome://browser/locale/places/places.properties", - getter_AddRefs(mBundle)); - NS_ENSURE_SUCCESS(rv, rv); - - // locale - nsCOMPtr ls = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = ls->GetApplicationLocale(getter_AddRefs(mLocale)); - NS_ENSURE_SUCCESS(rv, rv); - - // collation - nsCOMPtr cfact = do_CreateInstance( - NS_COLLATIONFACTORY_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = cfact->CreateCollation(mLocale, getter_AddRefs(mCollation)); - NS_ENSURE_SUCCESS(rv, rv); - - // date formatter - mDateFormatter = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - // annotation service - just ignore errors - //mAnnotationService = do_GetService("@mozilla.org/annotation-service;1", &rv); - - // prefs - LoadPrefs(); - - // recent events hash tables - NS_ENSURE_TRUE(mRecentTyped.Init(128), NS_ERROR_OUT_OF_MEMORY); - NS_ENSURE_TRUE(mRecentBookmark.Init(128), NS_ERROR_OUT_OF_MEMORY); - NS_ENSURE_TRUE(mRecentRedirects.Init(128), NS_ERROR_OUT_OF_MEMORY); - - rv = CreateLookupIndexes(); - if (NS_FAILED(rv)) - return rv; - - // The AddObserver calls must be the last lines in this function, because - // this function may fail, and thus, this object would be not completely - // initialized), but the observerservice would still keep a reference to us - // and notify us about shutdown, which may cause crashes. - - nsCOMPtr observerService = - do_GetService("@mozilla.org/observer-service;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr pbi = do_QueryInterface(mPrefBranch); - if (pbi) { - pbi->AddObserver(PREF_AUTOCOMPLETE_ONLY_TYPED, this, PR_FALSE); - pbi->AddObserver(PREF_BROWSER_HISTORY_EXPIRE_DAYS, this, PR_FALSE); - } - - observerService->AddObserver(this, gQuitApplicationMessage, PR_FALSE); - observerService->AddObserver(this, gXpcomShutdown, PR_FALSE); - - if (doImport) { - nsCOMPtr importer = new nsMorkHistoryImporter(); - NS_ENSURE_TRUE(importer, NS_ERROR_OUT_OF_MEMORY); - - nsCOMPtr historyFile; - rv = NS_GetSpecialDirectory(NS_APP_HISTORY_50_FILE, - getter_AddRefs(historyFile)); - if (NS_SUCCEEDED(rv) && historyFile) { - importer->ImportHistory(historyFile, this); - } - } - - // Don't add code that can fail here! Do it up above, before we add our - // observers. - - return NS_OK; -} - - -// nsNavHistory::InitDB -// - -nsresult -nsNavHistory::InitDB(PRBool *aDoImport) -{ - nsresult rv; - PRBool tableExists; - *aDoImport = PR_FALSE; - - // init DB - nsCOMPtr dbFile; - rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, - getter_AddRefs(dbFile)); - NS_ENSURE_SUCCESS(rv, rv); - rv = dbFile->Append(NS_LITERAL_STRING("bookmarks_history.sqlite")); - NS_ENSURE_SUCCESS(rv, rv); - - mDBService = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBService->OpenDatabase(dbFile, getter_AddRefs(mDBConn)); - NS_ENSURE_SUCCESS(rv, rv); - - // Set the database page size. This will only have any effect on empty files, - // so must be done before anything else. If the file already exists, we'll - // get that file's page size and this will have no effect. - nsCAutoString pageSizePragma("PRAGMA page_size="); - pageSizePragma.AppendInt(DEFAULT_DB_PAGE_SIZE); - rv = mDBConn->ExecuteSimpleSQL(pageSizePragma); - NS_ENSURE_SUCCESS(rv, rv); - - // dummy database connection - rv = mDBService->OpenDatabase(dbFile, getter_AddRefs(mDummyDBConn)); - NS_ENSURE_SUCCESS(rv, rv); - - mozStorageTransaction transaction(mDBConn, PR_FALSE); - - // Initialize the other places services' database tables. We do this before: - // - // - Starting the dummy statement, because once the dummy statement has - // started we can't modify the schema. Stopping and re-starting the - // dummy statement is pretty heavyweight - // - // - Creating our statements. Some of our statements depend on these external - // tables, such as the bookmarks or favicon tables. - rv = nsNavBookmarks::InitTables(mDBConn); - NS_ENSURE_SUCCESS(rv, rv); - rv = nsFaviconService::InitTables(mDBConn); - NS_ENSURE_SUCCESS(rv, rv); - rv = nsAnnotationService::InitTables(mDBConn); - NS_ENSURE_SUCCESS(rv, rv); - - // Get the page size. This may be different than was set above if the database - // file already existed and has a different page size. - PRInt32 pageSize; - { - nsCOMPtr statement; - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("PRAGMA page_size"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasResult; - rv = statement->ExecuteStep(&hasResult); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(hasResult, NS_ERROR_FAILURE); - pageSize = statement->AsInt32(0); - } - - // compute the size of the database cache - PRInt32 cachePercentage; - if (NS_FAILED(mPrefBranch->GetIntPref(PREF_DB_CACHE_PERCENTAGE, - &cachePercentage))) - cachePercentage = DEFAULT_DB_CACHE_PERCENTAGE; - if (cachePercentage > 50) - cachePercentage = 50; // sanity check, don't take too much - if (cachePercentage < 0) - cachePercentage = 0; - PRInt64 cacheSize = PR_GetPhysicalMemorySize() * cachePercentage / 100; - PRInt64 cachePages = cacheSize / pageSize; - - // set the cache size - nsCAutoString cacheSizePragma("PRAGMA cache_size="); - cacheSizePragma.AppendInt(cachePages); - rv = mDBConn->ExecuteSimpleSQL(cacheSizePragma); - NS_ENSURE_SUCCESS(rv, rv); - - // moz_history - rv = mDBConn->TableExists(NS_LITERAL_CSTRING("moz_history"), &tableExists); - NS_ENSURE_SUCCESS(rv, rv); - if (! tableExists) { - *aDoImport = PR_TRUE; - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_history (" - "id INTEGER PRIMARY KEY, " - "url LONGVARCHAR, " - "title LONGVARCHAR, " - "user_title LONGVARCHAR, " - "rev_host LONGVARCHAR, " - "visit_count INTEGER DEFAULT 0, " - "hidden INTEGER DEFAULT 0 NOT NULL, " - "typed INTEGER DEFAULT 0 NOT NULL, " - "favicon INTEGER)")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBConn->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE INDEX moz_history_urlindex ON moz_history (url)")); - NS_ENSURE_SUCCESS(rv, rv); - } - - // FIXME: this should be moved inside the moz_history table creation block. - // It is left outside and the return value is ignored because alpha 1 did not - // have this index. When it is likely that all alpha users have run a more - // recent build, we can move this to only happen on init so that startup time - // is faster. This index is used for favicon expiration, see - // nsNavHistoryExpire::ExpireItems - mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX moz_history_faviconindex ON moz_history (favicon)")); - - // moz_historyvisit - rv = mDBConn->TableExists(NS_LITERAL_CSTRING("moz_historyvisit"), &tableExists); - NS_ENSURE_SUCCESS(rv, rv); - if (! tableExists) { - rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_historyvisit (" - "visit_id INTEGER PRIMARY KEY, " - "from_visit INTEGER, " - "page_id INTEGER, " - "visit_date INTEGER, " - "visit_type INTEGER, " - "session INTEGER)")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBConn->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE INDEX moz_historyvisit_pageindex ON moz_historyvisit (page_id)")); - NS_ENSURE_SUCCESS(rv, rv); - } - - // This must be outside of the visit creation above because the alpha1 shipped - // without this index. This makes a big difference in startup time for - // large profiles because of finding bookmark redirects using the referring - // page. For final release, if we think everybody running alpha1 has run - // alpha2 or later, we can put it in the if statement above for faster - // startup time (same as above for the moz_history_faviconindex) - mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "CREATE INDEX moz_historyvisit_fromindex ON moz_historyvisit (from_visit)")); - - rv = transaction.Commit(); - NS_ENSURE_SUCCESS(rv, rv); - - // --- PUT SCHEMA-MODIFYING THINGS (like create table) ABOVE THIS LINE --- - - // now that the schema has been finalized, we can initialize the dummy stmt. - rv = StartDummyStatement(); - NS_ENSURE_SUCCESS(rv, rv); - - // This causes the database data to be preloaded up to the maximum cache size - // set above. This dramatically speeds up some later operations. Failures - // here are not fatal since we can run fine without this. It must happen - // after the dummy statement is running for the cache to be initialized. - if (cachePages > 0) { - rv = mDBConn->Preload(); - if (NS_FAILED(rv)) - NS_WARNING("Preload of database failed"); - } - - // DO NOT PUT ANY SCHEMA-MODIFYING THINGS HERE - return NS_OK; -} - - -// nsNavHistory::InitStatements -// -// Called after InitDB, this creates our stored statements - -nsresult -nsNavHistory::InitStatements() -{ - nsresult rv; - - // mDBGetURLPageInfo - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT h.id, h.url, h.title, h.user_title, h.rev_host, h.visit_count " - "FROM moz_history h " - "WHERE h.url = ?1"), - getter_AddRefs(mDBGetURLPageInfo)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBGetURLPageInfoFull - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT h.id, h.url, h.title, h.user_title, h.rev_host, h.visit_count, " - "(SELECT MAX(visit_date) FROM moz_historyvisit WHERE page_id = h.id), " - "f.url " - "FROM moz_history h " - "LEFT OUTER JOIN moz_favicon f ON h.favicon = f.id " - "WHERE h.url = ?1 "), - getter_AddRefs(mDBGetURLPageInfoFull)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBGetIdPageInfo - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT h.id, h.url, h.title, h.user_title, h.rev_host, h.visit_count " - "FROM moz_history h WHERE h.id = ?1"), - getter_AddRefs(mDBGetIdPageInfo)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBGetIdPageInfoFull - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT h.id, h.url, h.title, h.user_title, h.rev_host, h.visit_count, " - "(SELECT MAX(visit_date) FROM moz_historyvisit WHERE page_id = h.id), " - "f.url " - "FROM moz_history h " - "LEFT OUTER JOIN moz_favicon f ON h.favicon = f.id " - "WHERE h.id = ?1"), - getter_AddRefs(mDBGetIdPageInfoFull)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBFullAutoComplete - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT h.url, h.title, h.visit_count, h.typed " - "FROM moz_history h " - "JOIN moz_historyvisit v ON h.id = v.page_id " - "WHERE h.hidden <> 1 " - "GROUP BY h.id " - "ORDER BY h.visit_count " - "LIMIT ?1"), - getter_AddRefs(mDBFullAutoComplete)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBRecentVisitOfURL - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT v.visit_id, v.session " - "FROM moz_history h JOIN moz_historyvisit v ON h.id = v.page_id " - "WHERE h.url = ?1 " - "ORDER BY v.visit_date DESC " - "LIMIT 1"), - getter_AddRefs(mDBRecentVisitOfURL)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBInsertVisit - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "INSERT INTO moz_historyvisit " - "(from_visit, page_id, visit_date, visit_type, session) " - "VALUES (?1, ?2, ?3, ?4, ?5)"), - getter_AddRefs(mDBInsertVisit)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBGetPageVisitStats (see InternalAdd) - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT id, visit_count, typed, hidden " - "FROM moz_history " - "WHERE url = ?1"), - getter_AddRefs(mDBGetPageVisitStats)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBUpdatePageVisitStats (see InternalAdd) - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "UPDATE moz_history " - "SET visit_count = ?2, hidden = ?3, typed = ?4 " - "WHERE id = ?1"), - getter_AddRefs(mDBUpdatePageVisitStats)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBAddNewPage (see InternalAddNewPage) - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "INSERT INTO moz_history " - "(url, title, rev_host, hidden, typed, visit_count) " - "VALUES (?1, ?2, ?3, ?4, ?5, ?6)"), - getter_AddRefs(mDBAddNewPage)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBVisitToURLResult, should match kGetInfoIndex_* (see GetQueryResults) - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT h.id, h.url, h.title, h.user_title, h.rev_host, h.visit_count, " - "(SELECT MAX(visit_date) FROM moz_historyvisit WHERE page_id = h.id), " - "f.url, null " - "FROM moz_history h " - "JOIN moz_historyvisit v ON h.id = v.page_id " - "LEFT OUTER JOIN moz_favicon f ON h.favicon = f.id " - "WHERE v.visit_id = ?1"), - getter_AddRefs(mDBVisitToURLResult)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBVisitToVisitResult, should match kGetInfoIndex_* (see GetQueryResults) - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT h.id, h.url, h.title, h.user_title, h.rev_host, h.visit_count, " - "v.visit_date, f.url, v.session " - "FROM moz_history h " - "JOIN moz_historyvisit v ON h.id = v.page_id " - "LEFT OUTER JOIN moz_favicon f ON h.favicon = f.id " - "WHERE v.visit_id = ?1"), - getter_AddRefs(mDBVisitToVisitResult)); - NS_ENSURE_SUCCESS(rv, rv); - - // mDBUrlToURLResult, should match kGetInfoIndex_* - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT h.id, h.url, h.title, h.user_title, h.rev_host, h.visit_count, " - "(SELECT MAX(visit_date) FROM moz_historyvisit WHERE page_id = h.id), " - "f.url, null " - "FROM moz_history h " - "LEFT OUTER JOIN moz_favicon f ON h.favicon = f.id " - "WHERE h.url = ?1"), - getter_AddRefs(mDBUrlToUrlResult)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - - -#ifdef IN_MEMORY_LINKS -// nsNavHistory::InitMemDB -// -// Should be called after InitDB - -nsresult -nsNavHistory::InitMemDB() -{ - nsresult rv = mDBService->OpenSpecialDatabase("memory", getter_AddRefs(mMemDBConn)); - NS_ENSURE_SUCCESS(rv, rv); - - // create our table and index - rv = mMemDBConn->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE TABLE moz_memhistory (url LONGVARCHAR UNIQUE)")); - NS_ENSURE_SUCCESS(rv, rv); - - // prepackaged statements - rv = mMemDBConn->CreateStatement( - NS_LITERAL_CSTRING("SELECT url FROM moz_memhistory WHERE url = ?1"), - getter_AddRefs(mMemDBGetPage)); - NS_ENSURE_SUCCESS(rv, rv); - rv = mMemDBConn->CreateStatement( - NS_LITERAL_CSTRING("INSERT OR IGNORE INTO moz_memhistory VALUES (?1)"), - getter_AddRefs(mMemDBAddPage)); - NS_ENSURE_SUCCESS(rv, rv); - - // Copy the URLs over: sort by URL because the original table already has - // and index. We can therefor not spend time sorting the whole thing another - // time by inserting in order. - nsCOMPtr selectStatement; - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("SELECT url FROM moz_history WHERE visit_count > 0 ORDER BY url"), - getter_AddRefs(selectStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMore = PR_FALSE; - //rv = mMemDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN TRANSACTION")); - mozStorageTransaction transaction(mMemDBConn, PR_FALSE); - nsCString url; - while(NS_SUCCEEDED(rv = selectStatement->ExecuteStep(&hasMore)) && hasMore) { - rv = selectStatement->GetUTF8String(0, url); - if (NS_SUCCEEDED(rv) && ! url.IsEmpty()) { - rv = mMemDBAddPage->BindUTF8StringParameter(0, url); - if (NS_SUCCEEDED(rv)) - mMemDBAddPage->Execute(); - } - } - transaction.Commit(); - - return NS_OK; -} -#endif - - -// nsNavHistory::StartDummyStatement -// -// sqlite page caches are discarded when a statement is complete. This sucks -// for things like history queries where we do many small reads. This means -// that for every small transaction, we have to re-read from disk (or the OS -// cache) all pages associated with that transaction. -// -// To get around this, we keep a different connection. This dummy connection -// has a statement that stays open and thus keeps its pager cache in memory. -// When the shared pager cache is enabled before either connection has been -// opened (this is done by the storage service on DB init), our main -// connection will get the same pager cache, which will be persisted. -// -// HOWEVER, when a statement is open on a database, it is disallowed to -// change the schema of the database (add or modify tables or indices). -// We deal with this in two ways. First, for initialization, all the -// services that depend on the places connection are told to create their -// tables using static functions. This happens before StartDummyStatement -// is called in InitDB so that this problem doesn't happen. -// -// If some service needs to change the schema for some reason after this, -// they can call StopDummyStatement which will terminate the statement and -// clear the cache. This will allow the database schema to be modified, but -// will have bad performance implications (because the cache will need to -// be re-loaded). It is also possible for some buggy function to leave a -// statement open that will prevent modifictation of the DB. - -nsresult -nsNavHistory::StartDummyStatement() -{ - nsresult rv; - NS_ASSERTION(mDummyDBConn, "The dummy connection should have been set up by Init"); - - // do nothing if the dummy statement is already running - if (mDBDummyStatement) - return NS_OK; - - // Make sure the dummy table exists - PRBool tableExists; - rv = mDBConn->TableExists(NS_LITERAL_CSTRING("moz_dummy_table"), &tableExists); - NS_ENSURE_SUCCESS(rv, rv); - if (! tableExists) { - rv = mDBConn->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE TABLE moz_dummy_table (id INTEGER PRIMARY KEY)")); - NS_ENSURE_SUCCESS(rv, rv); - } - - // This table is guaranteed to have something in it and will keep the dummy - // statement open. If the table is empty, it won't hold the statement open. - // the PRIMARY KEY value on ID means that it is unique. The OR IGNORE means - // that if there is already a value of 1 there, this insert will be ignored, - // which is what we want so as to avoid growing the table infinitely. - rv = mDBConn->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("INSERT OR IGNORE INTO moz_dummy_table VALUES (1)")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDummyDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT id FROM moz_dummy_table LIMIT 1"), - getter_AddRefs(mDBDummyStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - // we have to step the dummy statement so that it will hold a lock on the DB - PRBool dummyHasResults; - rv = mDBDummyStatement->ExecuteStep(&dummyHasResults); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - - -// nsNavHistory::StopDummyStatement -// -// @see StartDummyStatement for how this works. -// -// It is very important that if the dummy statement is ever stopped, that -// it is restarted as soon as possible, or else the whole browser will run -// without DB cache, which will slow everything down. - -nsresult -nsNavHistory::StopDummyStatement() -{ - // do nothing if the dummy statement isn't running - if (! mDBDummyStatement) - return NS_OK; - - nsresult rv = mDBDummyStatement->Reset(); - NS_ENSURE_SUCCESS(rv, rv); - - mDBDummyStatement = nsnull; - return NS_OK; -} - - -// nsNavHistory::SaveExpandItem -// -// This adds an item to the persistent list of items that should be -// expanded. - -void -nsNavHistory::SaveExpandItem(const nsAString& aTitle) -{ - gExpandedItems.Put(aTitle, 1); -} - - -// nsNavHistory::GetUrlIdFor -// -// Called by the bookmarks and annotation services, this function returns the -// ID of the row for the given URL, optionally creating one if it doesn't -// exist. A newly created entry will have no visits. -// -// If aAutoCreate is false and the item doesn't exist, the entry ID will be -// zero. -// -// This DOES NOT check for bad URLs other than that they're nonempty. - -nsresult -nsNavHistory::GetUrlIdFor(nsIURI* aURI, PRInt64* aEntryID, - PRBool aAutoCreate) -{ - *aEntryID = 0; - - mozStorageTransaction transaction(mDBConn, PR_FALSE); - mozStorageStatementScoper statementResetter(mDBGetURLPageInfo); - nsresult rv = BindStatementURI(mDBGetURLPageInfo, 0, aURI); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasEntry = PR_FALSE; - rv = mDBGetURLPageInfo->ExecuteStep(&hasEntry); - NS_ENSURE_SUCCESS(rv, rv); - - if (hasEntry) { - return mDBGetURLPageInfo->GetInt64(kGetInfoIndex_PageID, aEntryID); - } else if (aAutoCreate) { - // create a new hidden, untyped, unvisited entry - mDBGetURLPageInfo->Reset(); - statementResetter.Abandon(); - nsString voidString; - voidString.SetIsVoid(PR_TRUE); - rv = InternalAddNewPage(aURI, voidString, PR_TRUE, PR_FALSE, 0, aEntryID); - if (NS_SUCCEEDED(rv)) - transaction.Commit(); - return rv; - } else { - // Doesn't exist: don't do anything, entry ID was already set to 0 above - return NS_OK; - } -} - - -// nsNavHistory::SaveCollapseItem -// -// For now, just remove things that should be collapsed. Maybe in the -// future we'll want to save things that have been explicitly collapsed -// versus things that were just never expanded in the first place. This could -// be done by doing a put with a value of 0. - -void -nsNavHistory::SaveCollapseItem(const nsAString& aTitle) -{ - gExpandedItems.Remove(aTitle); -} - - -// nsNavHistory::InternalAddNewPage -// -// Adds a new page to the DB. THIS SHOULD BE THE ONLY PLACE NEW ROWS ARE -// CREATED. This allows us to maintain better consistency. -// -// If non-null, the new page ID will be placed into aPageID. - -nsresult -nsNavHistory::InternalAddNewPage(nsIURI* aURI, const nsAString& aTitle, - PRBool aHidden, PRBool aTyped, - PRInt32 aVisitCount, PRInt64* aPageID) -{ - mozStorageStatementScoper scoper(mDBAddNewPage); - nsresult rv = BindStatementURI(mDBAddNewPage, 0, aURI); - NS_ENSURE_SUCCESS(rv, rv); - - // title - if (aTitle.IsVoid()) { - // if no title is specified, make up a title based on the filename - nsAutoString title; - GenerateTitleFromURI(aURI, title); - rv = mDBAddNewPage->BindStringParameter(1, - StringHead(title, HISTORY_TITLE_LENGTH_MAX)); - } else { - rv = mDBAddNewPage->BindStringParameter(1, - StringHead(aTitle, HISTORY_TITLE_LENGTH_MAX)); - } - NS_ENSURE_SUCCESS(rv, rv); - - // host (reversed with trailing period) - nsAutoString revHost; - rv = GetReversedHostname(aURI, revHost); - // Not all URI types have hostnames, so this is optional. - if (NS_SUCCEEDED(rv)) { - rv = mDBAddNewPage->BindStringParameter(2, revHost); - } else { - rv = mDBAddNewPage->BindNullParameter(2); - } - NS_ENSURE_SUCCESS(rv, rv); - - // hidden - rv = mDBAddNewPage->BindInt32Parameter(3, aHidden); - NS_ENSURE_SUCCESS(rv, rv); - - // typed - rv = mDBAddNewPage->BindInt32Parameter(4, aTyped); - NS_ENSURE_SUCCESS(rv, rv); - - // visit count - rv = mDBAddNewPage->BindInt32Parameter(5, aVisitCount); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBAddNewPage->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - // If the caller wants the page ID, go get it - if (aPageID) { - rv = mDBConn->GetLastInsertRowID(aPageID); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - - -// nsNavHistory::InternalAddVisit -// -// Just a wrapper for inserting a new visit in the DB. - -nsresult -nsNavHistory::InternalAddVisit(PRInt64 aPageID, PRInt64 aReferringVisit, - PRInt64 aSessionID, PRTime aTime, - PRInt32 aTransitionType, PRInt64* visitID) -{ - nsresult rv; - mozStorageStatementScoper scoper(mDBInsertVisit); - - rv = mDBInsertVisit->BindInt64Parameter(0, aReferringVisit); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBInsertVisit->BindInt64Parameter(1, aPageID); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBInsertVisit->BindInt64Parameter(2, aTime); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBInsertVisit->BindInt32Parameter(3, aTransitionType); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBInsertVisit->BindInt64Parameter(4, aSessionID); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBInsertVisit->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - return mDBConn->GetLastInsertRowID(visitID); -} - - -// nsNavHistory::FindLastVisit -// -// This finds the most recent visit to the given URL. If found, it will put -// that visit's ID and session into the respective out parameters and return -// true. Returns false if no visit is found. -// -// This is used to compute the referring visit. - -PRBool -nsNavHistory::FindLastVisit(nsIURI* aURI, PRInt64* aVisitID, - PRInt64* aSessionID) -{ - mozStorageStatementScoper scoper(mDBRecentVisitOfURL); - nsresult rv = BindStatementURI(mDBRecentVisitOfURL, 0, aURI); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMore; - rv = mDBRecentVisitOfURL->ExecuteStep(&hasMore); - NS_ENSURE_SUCCESS(rv, rv); - if (hasMore) { - *aVisitID = mDBRecentVisitOfURL->AsInt64(0); - *aSessionID = mDBRecentVisitOfURL->AsInt64(1); - return PR_TRUE; - } - return PR_FALSE; -} - - -// nsNavHistory::IsURIStringVisited -// -// Takes a URL as a string and returns true if we've visited it. -// -// Be careful to always reset the statement since it will be reused. - -PRBool nsNavHistory::IsURIStringVisited(const nsACString& aURIString) -{ -#ifdef IN_MEMORY_LINKS - // check the memory DB - nsresult rv = mMemDBGetPage->BindUTF8StringParameter(0, aURIString); - NS_ENSURE_SUCCESS(rv, PR_FALSE); - PRBool hasPage = PR_FALSE; - mMemDBGetPage->ExecuteStep(&hasPage); - mMemDBGetPage->Reset(); - return hasPage; -#else - -#ifdef LAZY_ADD - // check the lazy list to see if this has recently been added - for (PRUint32 i = 0; i < mLazyMessages.Length(); i ++) { - if (mLazyMessages[i].type == LazyMessage::Type_AddURI) { - if (aURIString.Equals(mLazyMessages[i].uriSpec)) - return PR_TRUE; - } - } -#endif - - // check the main DB - nsresult rv; - mozStorageStatementScoper statementResetter(mDBGetURLPageInfo); - rv = mDBGetURLPageInfo->BindUTF8StringParameter(0, aURIString); - NS_ENSURE_SUCCESS(rv, PR_FALSE); - - PRBool hasMore = PR_FALSE; - rv = mDBGetURLPageInfo->ExecuteStep(&hasMore); - NS_ENSURE_SUCCESS(rv, PR_FALSE); - if (! hasMore) - return PR_FALSE; - - // Actually get the result to make sure the visit count > 0. there are - // several ways that we can get pages with visit counts of 0, and those - // should not count. - PRInt32 visitCount; - rv = mDBGetURLPageInfo->GetInt32(kGetInfoIndex_VisitCount, &visitCount); - NS_ENSURE_SUCCESS(rv, PR_FALSE); - - return visitCount > 0; -#endif -} - - -// nsNavHistory::LoadPrefs - -nsresult -nsNavHistory::LoadPrefs() -{ - if (! mPrefBranch) - return NS_OK; - - mPrefBranch->GetIntPref(PREF_BROWSER_HISTORY_EXPIRE_DAYS, &mExpireDays); - PRBool oldCompleteOnlyTyped = mAutoCompleteOnlyTyped; - mPrefBranch->GetBoolPref(PREF_AUTOCOMPLETE_ONLY_TYPED, - &mAutoCompleteOnlyTyped); - if (oldCompleteOnlyTyped != mAutoCompleteOnlyTyped) { - // update the autocomplete statement if the option has changed. - nsresult rv = CreateAutoCompleteQuery(); - NS_ENSURE_SUCCESS(rv, rv); - } - return NS_OK; -} - - -// nsNavHistory::GetNow -// -// This is a hack to avoid calling PR_Now() too often, as is the case when -// we're asked the ageindays of many history entries in a row. A timer is -// set which will clear our valid flag after a short timeout. - -PRTime -nsNavHistory::GetNow() -{ - if (!mNowValid) { - mLastNow = PR_Now(); - mNowValid = PR_TRUE; - if (!mExpireNowTimer) - mExpireNowTimer = do_CreateInstance("@mozilla.org/timer;1"); - - if (mExpireNowTimer) - mExpireNowTimer->InitWithFuncCallback(expireNowTimerCallback, this, - HISTORY_EXPIRE_NOW_TIMEOUT, - nsITimer::TYPE_ONE_SHOT); - } - - return mLastNow; -} - - -// nsNavHistory::expireNowTimerCallback - -void nsNavHistory::expireNowTimerCallback(nsITimer* aTimer, void* aClosure) -{ - nsNavHistory* history = NS_STATIC_CAST(nsNavHistory*, aClosure); - history->mNowValid = PR_FALSE; - history->mExpireNowTimer = nsnull; -} - - -// nsNavHistory::NormalizeTime -// -// Converts a nsINavHistoryQuery reference+offset time into a PRTime -// relative to the epoch. -// -// It is important that this function NOT use the current time optimization. -// It is called to update queries, and we really need to know what right -// now is because those incoming values will also have current times that -// we will have to compare against. - -PRTime // static -nsNavHistory::NormalizeTime(PRUint32 aRelative, PRTime aOffset) -{ - PRTime ref; - switch (aRelative) - { - case nsINavHistoryQuery::TIME_RELATIVE_EPOCH: - return aOffset; - case nsINavHistoryQuery::TIME_RELATIVE_TODAY: { - // round to midnight this morning - PRExplodedTime explodedTime; - PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &explodedTime); - explodedTime.tm_min = - explodedTime.tm_hour = - explodedTime.tm_sec = - explodedTime.tm_usec = 0; - ref = PR_ImplodeTime(&explodedTime); - break; - } - case nsINavHistoryQuery::TIME_RELATIVE_NOW: - ref = PR_Now(); - break; - default: - NS_NOTREACHED("Invalid relative time"); - return 0; - } - return ref + aOffset; -} - - -// nsNavHistory::CanLiveUpdateQuery -// -// Returns true if this set of queries/options can be live-updated. That is, -// we can look at a node and compare its attributes to the query and easily -// tell whether it belongs in the result set or not. -// -// QUERYUPDATE_TIME: -// This query is only limited by an inclusive time range on the first -// query object. The caller can quickly evaluate the time itself if it -// chooses. This is even simpler than "simple" below. -// QUERYUPDATE_SIMPLE: -// This query is evaluatable using EvaluateQueryForNode to do live -// updating. -// QUERYUPDATE_COMPLEX: -// This query is not evaluatable using EvaluateQueryForNode. When something -// happens that this query updates, you will need to re-run the query. -// QUERYUPDATE_COMPLEX_WITH_BOOKMARKS: -// A complex query that additionally has dependence on bookmarks. All -// bookmark-dependent queries fall under this category. -// -// aHasSearchTerms will be set to true if the query has any dependence on -// keywords. When there is no dependence on keywords, we can handle title -// change operations as simple instead of complex. - -PRUint32 -nsNavHistory::GetUpdateRequirements(const nsCOMArray& aQueries, - nsNavHistoryQueryOptions* aOptions, - PRBool* aHasSearchTerms) -{ - NS_ASSERTION(aQueries.Count() > 0, "Must have at least one query"); - - // first check if there are search terms - *aHasSearchTerms = PR_FALSE; - PRInt32 i; - for (i = 0; i < aQueries.Count(); i ++) { - aQueries[i]->GetHasSearchTerms(aHasSearchTerms); - if (*aHasSearchTerms) - break; - } - - // Whenever there is a maximum number of results, we must requery. This - // is because we can't generally know if any given addition/change causes - // the item to be in the top N items in the database. - if (aOptions->MaxResults() > 0) - return QUERYUPDATE_COMPLEX; - - PRBool nonTimeBasedItems = PR_FALSE; - for (i = 0; i < aQueries.Count(); i ++) { - nsNavHistoryQuery* query = aQueries[i]; - - if (query->Folders().Length() > 0 || query->OnlyBookmarked()) { - return QUERYUPDATE_COMPLEX_WITH_BOOKMARKS; - } - // Note: we don't currently have any complex non-bookmarked items, but these - // are expected to be added. Put detection of these items here. - if (! query->SearchTerms().IsEmpty() || - ! query->Domain().IsVoid() || - query->Uri() != nsnull) - nonTimeBasedItems = PR_TRUE; - } - - if (aQueries.Count() == 1 && ! nonTimeBasedItems) - return QUERYUPDATE_TIME; - return QUERYUPDATE_SIMPLE; -} - - -// nsNavHistory::EvaluateQueryForNode -// -// This runs the node through the given queries to see if satisfies the -// query conditions. Not every query parameters are handled by this code, -// but we handle the most common ones so that performance is better. -// -// We assume that the time on the node is the time that we want to compare. -// This is not necessarily true because URL nodes have the last access time, -// which is not necessarily the same. However, since this is being called -// to update the list, we assume that the last access time is the current -// access time that we are being asked to compare so it works out. -// -// Returns true if node matches the query, false if not. - -PRBool -nsNavHistory::EvaluateQueryForNode(const nsCOMArray& aQueries, - nsNavHistoryQueryOptions* aOptions, - nsNavHistoryResultNode* aNode) -{ - // lazily created from the node's string when we need to match URIs - nsCOMPtr nodeUri; - - for (PRInt32 i = 0; i < aQueries.Count(); i ++) { - PRBool hasIt; - nsCOMPtr query = aQueries[i]; - - // --- begin time --- - query->GetHasBeginTime(&hasIt); - if (hasIt) { - PRTime beginTime = NormalizeTime(query->BeginTimeReference(), - query->BeginTime()); - if (aNode->mTime < beginTime) - continue; // before our time range - } - - // --- end time --- - query->GetHasEndTime(&hasIt); - if (hasIt) { - PRTime endTime = NormalizeTime(query->EndTimeReference(), - query->EndTime()); - if (aNode->mTime > endTime) - continue; // after our time range - } - - // --- search terms --- - if (! query->SearchTerms().IsEmpty()) { - // we can use the existing filtering code, just give it our one object in - // an array. - nsCOMArray inputSet; - inputSet.AppendObject(aNode); - nsCOMArray filteredSet; - nsresult rv = FilterResultSet(inputSet, &filteredSet, query->SearchTerms()); - if (NS_FAILED(rv)) - continue; - if (! filteredSet.Count()) - continue; // did not make it through the filter, doesn't match - } - - // --- domain/host matching --- - query->GetHasDomain(&hasIt); - if (hasIt) { - if (! nodeUri) { - // lazy creation of nodeUri, which might be checked for multiple queries - if (NS_FAILED(NS_NewURI(getter_AddRefs(nodeUri), aNode->mURI))) - continue; - } - nsCAutoString host; - if (NS_FAILED(nodeUri->GetAsciiHost(host))) - continue; - nsCAutoString asciiRequest; - if (NS_FAILED(AsciiHostNameFromHostString(query->Domain(), asciiRequest))) - continue; - - if (query->DomainIsHost()) { - if (! asciiRequest.Equals(host)) - continue; // host names don't match - } - // check domain names - nsCAutoString domain; - DomainNameFromHostName(host, domain); - if (! asciiRequest.Equals(domain)) - continue; // domain names don't match - } - - // --- URI matching --- - if (query->Uri()) { - if (! nodeUri) { // lazy creation of nodeUri - if (NS_FAILED(NS_NewURI(getter_AddRefs(nodeUri), aNode->mURI))) - continue; - } - if (! query->UriIsPrefix()) { - // easy case: the URI is an exact match - PRBool equals; - nsresult rv = query->Uri()->Equals(nodeUri, &equals); - NS_ENSURE_SUCCESS(rv, rv); - if (! equals) - continue; - } else { - // harder case: match prefix, note that we need to get the ASCII string - // from the node's parsed URI instead of using the node's mUrl string, - // because that might not be normalized - nsCAutoString nodeUriString; - nodeUri->GetAsciiSpec(nodeUriString); - nsCAutoString queryUriString; - query->Uri()->GetAsciiSpec(queryUriString); - if (queryUriString.Length() > nodeUriString.Length()) - continue; // not long enough to match as prefix - nodeUriString.SetLength(queryUriString.Length()); - if (! nodeUriString.Equals(queryUriString)) - continue; // prefixes don't match - } - } - - // If we ever make it to the bottom of this loop, that means it passed all - // tests for the given query. Since queries are ORed together, that means - // it passed everything and we are done. - return PR_TRUE; - } - - // didn't match any query - return PR_FALSE; -} - - -// nsNavHistory::AsciiHostNameFromHostString -// -// We might have interesting encodings and different case in the host name. -// This will convert that host name into an ASCII host name by sending it -// through the URI canonicalization. The result can be used for comparison -// with other ASCII host name strings. - -nsresult // static -nsNavHistory::AsciiHostNameFromHostString(const nsACString& aHostName, - nsACString& aAscii) -{ - nsCOMPtr uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), aHostName); - NS_ENSURE_SUCCESS(rv, rv); - return uri->GetAsciiHost(aAscii); -} - - -// nsNavHistory::DomainNameFromHostName -// -// This does the www.mozilla.org -> mozilla.org and -// foo.theregister.co.uk -> theregister.co.uk conversion - -void // static -nsNavHistory::DomainNameFromHostName(const nsCString& aHostName, - nsACString& aDomainName) -{ - if (IsNumericHostName(aHostName)) { - // easy case - aDomainName = aHostName; - } else { - // compute the toplevel domain name - PRInt32 tldLength = GetTLDCharCount(aHostName); - if (tldLength < PRInt32(aHostName.Length())) { - // bugzilla.mozilla.org : tldLength = 3, topDomain = mozilla.org - GetSubstringFromNthDot(aHostName, - aHostName.Length() - tldLength - 2, - 1, PR_FALSE, aDomainName); - } - } -} - - -// Nav history ***************************************************************** - - -// nsNavHistory::GetHasHistoryEntries - -NS_IMETHODIMP -nsNavHistory::GetHasHistoryEntries(PRBool* aHasEntries) -{ - nsCOMPtr dbSelectStatement; - nsresult rv = mDBConn->CreateStatement( - NS_LITERAL_CSTRING("SELECT visit_id FROM moz_historyvisit LIMIT 1"), - getter_AddRefs(dbSelectStatement)); - NS_ENSURE_SUCCESS(rv, rv); - return dbSelectStatement->ExecuteStep(aHasEntries); -} - - -// nsNavHistory::SetPageUserTitle - -NS_IMETHODIMP -nsNavHistory::SetPageUserTitle(nsIURI* aURI, const nsAString& aUserTitle) -{ - return SetPageTitleInternal(aURI, PR_TRUE, aUserTitle); -} - - -// nsNavHistory::MarkPageAsFollowedBookmark -// -// @see MarkPageAsTyped - -NS_IMETHODIMP -nsNavHistory::MarkPageAsFollowedBookmark(nsIURI* aURI) -{ - // don't add when history is disabled - if (IsHistoryDisabled()) - return NS_OK; - - nsCAutoString uriString; - aURI->GetSpec(uriString); - - // if URL is already in the queue, then we need to remove the old one - PRInt64 unusedEventTime; - if (mRecentBookmark.Get(uriString, &unusedEventTime)) - mRecentBookmark.Remove(uriString); - - if (mRecentBookmark.Count() > RECENT_EVENT_QUEUE_MAX_LENGTH) - ExpireNonrecentEvents(&mRecentBookmark); - - mRecentTyped.Put(uriString, GetNow()); - return NS_OK; -} - - -// nsNavHistory::CanAddURI -// -// Filter out unwanted URIs such as "chrome:", "mailbox:", etc. -// -// The model is if we don't know differently then add which basically means -// we are suppose to try all the things we know not to allow in and then if -// we don't bail go on and allow it in. - -nsresult -nsNavHistory::CanAddURI(nsIURI* aURI, PRBool* canAdd) -{ - nsresult rv; - - nsCString scheme; - rv = aURI->GetScheme(scheme); - NS_ENSURE_SUCCESS(rv, rv); - - // first check the most common cases (HTTP, HTTPS) to allow in to avoid most - // of the work - if (scheme.EqualsLiteral("http")) { - *canAdd = PR_TRUE; - return NS_OK; - } - if (scheme.EqualsLiteral("https")) { - *canAdd = PR_TRUE; - return NS_OK; - } - - // now check for all bad things - if (scheme.EqualsLiteral("about") || - scheme.EqualsLiteral("imap") || - scheme.EqualsLiteral("news") || - scheme.EqualsLiteral("mailbox") || - scheme.EqualsLiteral("moz-anno") || - scheme.EqualsLiteral("view-source") || - scheme.EqualsLiteral("chrome") || - scheme.EqualsLiteral("data")) { - *canAdd = PR_FALSE; - return NS_OK; - } - *canAdd = PR_TRUE; - return NS_OK; -} - - -// nsNavHistory::SetPageDetails - -NS_IMETHODIMP -nsNavHistory::SetPageDetails(nsIURI* aURI, const nsAString& aTitle, - const nsAString& aUserTitle, PRUint32 aVisitCount, - PRBool aHidden, PRBool aTyped) -{ - // look up the page ID, creating a new one if necessary - PRInt64 pageID; - nsresult rv = GetUrlIdFor(aURI, &pageID, PR_TRUE); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr statement; - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "UPDATE moz_history " - "SET title = ?2, " - "user_title = ?3, " - "visit_count = ?4, " - "hidden = ?5, " - "typed = ?6 " - "WHERE id = ?1"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindInt64Parameter(0, pageID); - NS_ENSURE_SUCCESS(rv, rv); - - // for the titles, be careful to interpret isVoid as NULL SQL command so that - // we can tell the difference between "set to empty" and "unset" - if (aTitle.IsVoid()) - statement->BindNullParameter(1); - else - statement->BindStringParameter(1, StringHead(aTitle, HISTORY_TITLE_LENGTH_MAX)); - NS_ENSURE_SUCCESS(rv, rv); - if (aUserTitle.IsVoid()) - statement->BindNullParameter(2); - else - statement->BindStringParameter(2, StringHead(aUserTitle, HISTORY_TITLE_LENGTH_MAX)); - NS_ENSURE_SUCCESS(rv, rv); - - statement->BindInt32Parameter(3, aVisitCount); - NS_ENSURE_SUCCESS(rv, rv); - statement->BindInt32Parameter(4, aHidden ? 1 : 0); - NS_ENSURE_SUCCESS(rv, rv); - statement->BindInt32Parameter(5, aTyped ? 1 : 0); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - - -// nsNavHistory::AddVisit -// -// Adds or updates a page with the given URI. The ID of the new visit will -// be put into aVisitID. -// -// THE RETURNED NEW VISIT ID MAY BE 0 indicating that this page should not be -// added to the history. - -NS_IMETHODIMP -nsNavHistory::AddVisit(nsIURI* aURI, PRTime aTime, PRInt64 aReferringVisit, - PRInt32 aTransitionType, PRBool aIsRedirect, - PRInt64 aSessionID, PRInt64* aVisitID) -{ - nsresult rv; - - // Filter out unwanted URIs, silently failing - PRBool canAdd = PR_FALSE; - rv = CanAddURI(aURI, &canAdd); - NS_ENSURE_SUCCESS(rv, rv); - if (!canAdd) { - *aVisitID = 0; - return NS_OK; - } - - // in-memory version -#ifdef IN_MEMORY_LINKS - rv = BindStatementURI(mMemDBAddPage, 0, aURI); - NS_ENSURE_SUCCESS(rv, rv); - mMemDBAddPage->Execute(); -#endif - - // This will prevent corruption since we have to do a two-phase add. - // Generally this won't do anything because AddURI has its own transaction. - mozStorageTransaction transaction(mDBConn, PR_FALSE, - mozIStorageConnection::TRANSACTION_EXCLUSIVE); - - // see if this is an update (revisit) or a new page - mozStorageStatementScoper scoper(mDBGetPageVisitStats); - rv = BindStatementURI(mDBGetPageVisitStats, 0, aURI); - NS_ENSURE_SUCCESS(rv, rv); - PRBool alreadyVisited = PR_TRUE; - rv = mDBGetPageVisitStats->ExecuteStep(&alreadyVisited); - - PRInt64 pageID = 0; - PRBool hidden; - PRBool typed; - PRBool newItem = PR_FALSE; // used to send out notifications at the end - if (alreadyVisited) { - // Update the existing entry... - - rv = mDBGetPageVisitStats->GetInt64(0, &pageID); - NS_ENSURE_SUCCESS(rv, rv); - - PRInt32 oldVisitCount = 0; - rv = mDBGetPageVisitStats->GetInt32(1, &oldVisitCount); - NS_ENSURE_SUCCESS(rv, rv); - - PRInt32 oldTypedState = 0; - rv = mDBGetPageVisitStats->GetInt32(2, &oldTypedState); - NS_ENSURE_SUCCESS(rv, rv); - - PRInt32 oldHiddenState = 0; - rv = mDBGetPageVisitStats->GetInt32(3, &oldHiddenState); - NS_ENSURE_SUCCESS(rv, rv); - - // free the previous statement before we make a new one - mDBGetPageVisitStats->Reset(); - scoper.Abandon(); - - // embedded links and redirects will be hidden, but don't hide pages that - // are already unhidden. - // - // Note that we test the redirect flag and not for the redirect transition - // type. The transition type refers to how we got here, and whether a page - // is shown does not depend on whether you got to it through a redirect. - // Rather, we want to hide pages that do not themselves redirect somewhere - // else, which is what the redirect flag means. - hidden = oldHiddenState; - if (hidden && ! aIsRedirect && - aTransitionType != nsINavHistoryService::TRANSITION_EMBED) - hidden = PR_FALSE; // unhide - - typed = oldTypedState || (aTransitionType == TRANSITION_TYPED); - - // some items may have a visit count of 0 which will not count for link - // visiting, so be sure to note this transition - if (oldVisitCount == 0) - newItem = PR_TRUE; - - // update with new stats - mozStorageStatementScoper updateScoper(mDBUpdatePageVisitStats); - rv = mDBUpdatePageVisitStats->BindInt64Parameter(0, pageID); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBUpdatePageVisitStats->BindInt32Parameter(1, oldVisitCount + 1); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBUpdatePageVisitStats->BindInt32Parameter(2, hidden); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBUpdatePageVisitStats->BindInt32Parameter(3, typed); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBUpdatePageVisitStats->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - } else { - // New page - newItem = PR_TRUE; - - // free the previous statement before we make a new one - mDBGetPageVisitStats->Reset(); - scoper.Abandon(); - - // hide embedded links and redirects, everything else is visible, - // See the hidden computation code above for a little more explanation. - hidden = (aTransitionType == TRANSITION_EMBED || aIsRedirect); - - typed = (aTransitionType == TRANSITION_TYPED); - - // set as visited once, no title - nsString voidString; - voidString.SetIsVoid(PR_TRUE); - rv = InternalAddNewPage(aURI, voidString, hidden, typed, 1, &pageID); - NS_ENSURE_SUCCESS(rv, rv); - } - - rv = InternalAddVisit(pageID, aReferringVisit, aSessionID, aTime, - aTransitionType, aVisitID); - - // Notify observers: The hidden detection code must match that in - // GetQueryResults to maintain consistency. - // FIXME bug 325241: make a way to observe hidden URLs - transaction.Commit(); - if (! hidden && aTransitionType != TRANSITION_EMBED) { - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryObserver, - OnVisit(aURI, *aVisitID, aTime, aSessionID, - aReferringVisit, aTransitionType)); - } - - // Normally docshell send the link visited observer notification for us (this - // will tell all the documents to update their visited link coloring). - // However, for redirects (since we implement nsIGlobalHistory3) this will - // not happen and we need to send it ourselves. - if (newItem && aIsRedirect) { - nsCOMPtr obsService = - do_GetService("@mozilla.org/observer-service;1"); - if (obsService) - obsService->NotifyObservers(aURI, NS_LINK_VISITED_EVENT_TOPIC, nsnull); - } - - return NS_OK; -} - - -// nsNavHistory::GetNewQuery - -NS_IMETHODIMP nsNavHistory::GetNewQuery(nsINavHistoryQuery **_retval) -{ - *_retval = new nsNavHistoryQuery(); - if (! *_retval) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(*_retval); - return NS_OK; -} - -// nsNavHistory::GetNewQueryOptions - -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, nsINavHistoryQueryOptions *aOptions, - nsINavHistoryResult** _retval) -{ - return ExecuteQueries(&aQuery, 1, aOptions, _retval); -} - - -// nsNavHistory::ExecuteQueries -// -// This function is actually very simple, we just create the proper root node (either -// a bookmark folder or a complex query node) and assign it to the result. The node -// will then populate itself accordingly. -// -// Quick overview of query operation: When you call this function, we will construct -// the correct container node and set the options you give it. This node will then -// fill itself. Folder nodes will call nsNavBookmarks::QueryFolderChildren, and -// all other queries will call GetQueryResults. If these results contain other -// queries, those will be populated when the container is opened. - -NS_IMETHODIMP -nsNavHistory::ExecuteQueries(nsINavHistoryQuery** aQueries, PRUint32 aQueryCount, - nsINavHistoryQueryOptions *aOptions, - nsINavHistoryResult** _retval) -{ - nsresult rv; - NS_ENSURE_ARG_POINTER(aQueries); - NS_ENSURE_ARG_POINTER(aOptions); - if (! aQueryCount) - return NS_ERROR_INVALID_ARG; - - // concrete options - nsCOMPtr options = do_QueryInterface(aOptions); - NS_ENSURE_TRUE(options, NS_ERROR_INVALID_ARG); - - // concrete queries array - nsCOMArray queries; - for (PRUint32 i = 0; i < aQueryCount; i ++) { - nsCOMPtr query = do_QueryInterface(aQueries[i], &rv); - NS_ENSURE_SUCCESS(rv, rv); - queries.AppendObject(query); - } - - // root node - nsRefPtr rootNode; - PRInt64 folderId = GetSimpleBookmarksQueryFolder(queries); - if (folderId) { - // In the simple case where we're just querying children of a single bookmark - // folder, we can more efficiently generate results. - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY); - nsCOMPtr tempRootNode; - rv = bookmarks->ResultNodeForFolder(folderId, options, - getter_AddRefs(tempRootNode)); - NS_ENSURE_SUCCESS(rv, rv); - rootNode = tempRootNode->GetAsContainer(); - } else { - // complex query - rootNode = new nsNavHistoryQueryResultNode(EmptyCString(), EmptyCString(), - queries, options); - NS_ENSURE_TRUE(rootNode, NS_ERROR_OUT_OF_MEMORY); - } - - // result object - nsRefPtr result; - rv = nsNavHistoryResult::NewHistoryResult(aQueries, aQueryCount, options, rootNode, - getter_AddRefs(result)); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ADDREF(*_retval = result); - return NS_OK; -} - - -// nsNavHistory::GetQueryResults -// -// Call this to get the results from a complex query. This is used by -// nsNavHistoryQueryResultNode to populate its children. For simple bookmark -// queries, use nsNavBookmarks::QueryFolderChildren. -// -// THIS DOES NOT DO SORTING. You will need to sort the container yourself -// when you get the results. This is because sorting depends on tree -// statistics that will be built from the perspective of the tree. See -// nsNavHistoryQueryResultNode::FillChildren -// -// FIXME: This only does keyword searching for the first query, and does -// it ANDed with the all the rest of the queries. - -nsresult -nsNavHistory::GetQueryResults(const nsCOMArray& aQueries, - nsNavHistoryQueryOptions *aOptions, - nsCOMArray* aResults) -{ - NS_ENSURE_ARG_POINTER(aOptions); - NS_ASSERTION(aResults->Count() == 0, "Initial result array must be empty"); - if (! aQueries.Count()) - return NS_ERROR_INVALID_ARG; - - nsresult rv; - - PRInt32 sortingMode = aOptions->SortingMode(); - if (sortingMode < 0 || - sortingMode > nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING) { - return NS_ERROR_INVALID_ARG; - } - - PRBool asVisits = - (aOptions->ResultType() == nsINavHistoryQueryOptions::RESULTS_AS_VISIT || - aOptions->ResultType() == nsINavHistoryQueryOptions::RESULTS_AS_FULL_VISIT); - - nsCAutoString commonConditions("visit_count > 0 "); - if (! aOptions->IncludeHidden()) { - // The hiding code here must match the notification behavior in AddVisit - commonConditions.AppendLiteral("AND hidden <> 1 "); - - // Some items are unhidden but are subframe navigations that we shouldn't - // show. This happens especially on imported profiles because the previous - // history system didn't hide as many things as we do now. Some sites, - // especially Javascript-heavy ones, load things in frames to display them, - // resulting in a lot of these entries. This filters those visits out. - if (asVisits) - commonConditions.AppendLiteral("AND v.visit_type <> 4 "); // not TRANSITION_EMBED - } - - // Query string: Output parameters should be in order of kGetInfoIndex_* - // WATCH OUT: nsNavBookmarks::Init also creates some statements that share - // these same indices for passing to RowToResult. If you add something to - // this, you also need to update the bookmark statements to keep them in - // sync! - nsCAutoString queryString; - nsCAutoString groupBy; - if (asVisits) { - // if we want visits, this is easy, just combine all possible matches - // between the history and visits table and do our query. - // FIXME(brettw) Add full visit info - queryString = NS_LITERAL_CSTRING( - "SELECT h.id, h.url, h.title, h.user_title, h.rev_host, h.visit_count, " - "v.visit_date, f.url, v.session " - "FROM moz_history h " - "JOIN moz_historyvisit v ON h.id = v.page_id " - "LEFT OUTER JOIN moz_favicon f ON h.favicon = f.id " - "WHERE "); - } else { - // For URLs, it is more complicated, because we want each URL once. The - // GROUP BY clause gives us this. To get the max visit time, we populate - // one column by using a nested SELECT on the visit table. Also, ignore - // session information. - // FIXME(brettw) add nulls for full visit info - queryString = NS_LITERAL_CSTRING( - "SELECT h.id, h.url, h.title, h.user_title, h.rev_host, h.visit_count, " - "(SELECT MAX(visit_date) FROM moz_historyvisit WHERE page_id = h.id), " - "f.url, null " - "FROM moz_history h " - "LEFT OUTER JOIN moz_historyvisit v ON h.id = v.page_id " - "LEFT OUTER JOIN moz_favicon f ON h.favicon = f.id " - "WHERE "); - groupBy = NS_LITERAL_CSTRING(" GROUP BY h.id"); - } - - PRInt32 numParameters = 0; - nsCAutoString conditions; - PRInt32 i; - for (i = 0; i < aQueries.Count(); i ++) { - nsCString queryClause; - PRInt32 clauseParameters = 0; - rv = QueryToSelectClause(aQueries[i], numParameters, - &queryClause, &clauseParameters, - commonConditions); - NS_ENSURE_SUCCESS(rv, rv); - if (! queryClause.IsEmpty()) { - if (! conditions.IsEmpty()) // exists previous clause: multiple ones are ORed - conditions += NS_LITERAL_CSTRING(" OR "); - conditions += NS_LITERAL_CSTRING("(") + queryClause + - NS_LITERAL_CSTRING(")"); - numParameters += clauseParameters; - } - } - - // in cases where there were no queries, we need to use the common conditions - // (normally these are appended to each clause that are not annotation-based) - if (! conditions.IsEmpty()) { - queryString += conditions; - } else { - queryString += commonConditions; - } - queryString += groupBy; - - // 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. - // - // FIXME: do some performance tests, I'm not sure that the indices are getting - // used, in which case we should just remove this except when there are max - // results. - switch(sortingMode) { - case nsINavHistoryQueryOptions::SORT_BY_NONE: - break; - 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. - // - // If the user wants few results, we limit them by date, necessitating - // a sort by date here (see the IDL definition for maxResults). We'll - // still do the official sort by title later. - if (aOptions->MaxResults() > 0) - queryString += NS_LITERAL_CSTRING(" ORDER BY v.visit_date DESC"); - break; - case nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING: - queryString += NS_LITERAL_CSTRING(" ORDER BY v.visit_date ASC"); - break; - case nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING: - queryString += NS_LITERAL_CSTRING(" ORDER BY v.visit_date DESC"); - break; - case nsINavHistoryQueryOptions::SORT_BY_URI_ASCENDING: - queryString += NS_LITERAL_CSTRING(" ORDER BY h.url ASC"); - break; - case nsINavHistoryQueryOptions::SORT_BY_URI_DESCENDING: - queryString += NS_LITERAL_CSTRING(" ORDER BY h.url DESC"); - break; - case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING: - queryString += NS_LITERAL_CSTRING(" ORDER BY h.visit_count ASC"); - break; - case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING: - queryString += NS_LITERAL_CSTRING(" ORDER BY h.visit_count DESC"); - break; - default: - NS_NOTREACHED("Invalid sorting mode"); - } - - // limit clause if there are 'maxResults' - if (aOptions->MaxResults() > 0) { - queryString += NS_LITERAL_CSTRING(" LIMIT "); - queryString.AppendInt(aOptions->MaxResults()); - queryString.AppendLiteral(" "); - } - - //printf("Constructed the query: %s\n", PromiseFlatCString(queryString).get()); - - // Put this in a transaction. Even though we are only reading, this will - // speed up the grouped queries to the annotation service for titles and - // full text searching. - mozStorageTransaction transaction(mDBConn, PR_FALSE); - - // create statement - nsCOMPtr statement; - rv = mDBConn->CreateStatement(queryString, getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - // bind parameters - numParameters = 0; - for (i = 0; i < aQueries.Count(); i ++) { - PRInt32 clauseParameters = 0; - rv = BindQueryClauseParameters(statement, numParameters, - aQueries[i], &clauseParameters); - NS_ENSURE_SUCCESS(rv, rv); - numParameters += clauseParameters; - } - - PRBool hasSearchTerms; - rv = aQueries[0]->GetHasSearchTerms(&hasSearchTerms); - NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 groupCount; - const PRUint32 *groupings = aOptions->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, aOptions, aResults); - NS_ENSURE_SUCCESS(rv, rv); - } else { - // generate the toplevel results - nsCOMArray toplevel; - rv = ResultsAsList(statement, aOptions, &toplevel); - NS_ENSURE_SUCCESS(rv, rv); - - if (hasSearchTerms) { - // keyword search - if (groupCount == 0) { - // keyword search with no grouping: can filter directly into the result - FilterResultSet(toplevel, aResults, aQueries[0]->SearchTerms()); - } else { - // keyword searching with grouping: need intermediate filtered results - nsCOMArray filteredResults; - FilterResultSet(toplevel, &filteredResults, aQueries[0]->SearchTerms()); - rv = RecursiveGroup(filteredResults, groupings, groupCount, - aResults); - NS_ENSURE_SUCCESS(rv, rv); - } - } else { - // group unfiltered results - rv = RecursiveGroup(toplevel, groupings, groupCount, aResults); - NS_ENSURE_SUCCESS(rv, rv); - } - } - - // automatically expand things that were expanded before - // FIXME(brettw) - //if (gExpandedItems.Count() > 0) - // result->ApplyTreeState(gExpandedItems); - - return NS_OK; -} - - -// nsNavHistory::AddObserver - -NS_IMETHODIMP -nsNavHistory::AddObserver(nsINavHistoryObserver* aObserver, PRBool aOwnsWeak) -{ - return mObservers.AppendWeakElement(aObserver, aOwnsWeak); -} - - -// nsNavHistory::RemoveObserver - -NS_IMETHODIMP -nsNavHistory::RemoveObserver(nsINavHistoryObserver* aObserver) -{ - return mObservers.RemoveWeakElement(aObserver); -} - - -// nsNavHistory::BeginUpdateBatch - -NS_IMETHODIMP -nsNavHistory::BeginUpdateBatch() -{ - mBatchesInProgress ++; - if (mBatchesInProgress == 1) { - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryObserver, - OnBeginUpdateBatch()) - } - return NS_OK; -} - - -// nsNavHistory::EndUpdateBatch - -NS_IMETHODIMP -nsNavHistory::EndUpdateBatch() -{ - if (mBatchesInProgress == 0) - return NS_ERROR_FAILURE; - if (--mBatchesInProgress == 0) { - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryObserver, OnEndUpdateBatch()) - } - return NS_OK; -} - -// Browser history ************************************************************* - - -// nsNavHistory::AddPageWithDetails -// -// This function is used by the migration components to import history. -// -// Note that this always adds the page with one visit and no parent, which -// is appropriate for imported URIs. -// -// UNTESTED - -NS_IMETHODIMP -nsNavHistory::AddPageWithDetails(nsIURI *aURI, const PRUnichar *aTitle, - PRInt64 aLastVisited) -{ - PRInt64 visitID; - nsresult rv = AddVisit(aURI, aLastVisited, 0, TRANSITION_LINK, PR_FALSE, - 0, &visitID); - NS_ENSURE_SUCCESS(rv, rv); - - return SetPageTitleInternal(aURI, PR_FALSE, nsString(aTitle)); -} - - -// nsNavHistory::GetLastPageVisited -// -// This was once used when the new window is set to "previous page." It -// doesn't seem to be used anymore, so we don't spend any time precompiling -// the statement. - -NS_IMETHODIMP -nsNavHistory::GetLastPageVisited(nsACString & aLastPageVisited) -{ - nsCOMPtr statement; - nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT h.url " - "FROM moz_history h LEFT OUTER JOIN moz_historyvisit v ON h.id = v.page_id " - "WHERE v.visit_date IN " - "(SELECT MAX(visit_date) " - "FROM moz_historyvisit v2 LEFT JOIN moz_history h2 ON v2.page_id = h2.id " - "WHERE h2.hidden != 1)"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMatch = PR_FALSE; - if (NS_SUCCEEDED(statement->ExecuteStep(&hasMatch)) && hasMatch) { - return statement->GetUTF8String(0, aLastPageVisited); - } - aLastPageVisited.Truncate(0); - return NS_OK; -} - - -// nsNavHistory::GetCount -// -// This function is used in legacy code to see if there is any history to -// clear. Counting the actual number of history entries is very slow, so -// we just see if there are any and return 0 or 1, which is enough to make -// all the code that uses this function happy. - -NS_IMETHODIMP -nsNavHistory::GetCount(PRUint32 *aCount) -{ - PRBool hasEntries = PR_FALSE; - nsresult rv = GetHasHistoryEntries(&hasEntries); - if (hasEntries) - *aCount = 1; - else - *aCount = 0; - return rv; -} - - -// nsNavHistory::RemovePage -// -// Removes all visits and the main history entry for the given URI. -// Silently fails if we have no knowledge of the page. - -NS_IMETHODIMP -nsNavHistory::RemovePage(nsIURI *aURI) -{ - nsresult rv; - mozStorageTransaction transaction(mDBConn, PR_FALSE); - nsCOMPtr statement; - - // delete all visits for this page - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "DELETE FROM moz_historyvisit WHERE page_id IN (SELECT id 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* bookmarksService = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarksService, NS_ERROR_FAILURE); - PRBool bookmarked = PR_FALSE; - rv = bookmarksService->IsBookmarked(aURI, &bookmarked); - NS_ENSURE_SUCCESS(rv, rv); - - if (! bookmarked) { - // Only delete everything else if the page is not bookmarked. Note that we - // do NOT delete favicons. Any unreferenced favicons will be deleted next - // time the browser is shut down. - - // FIXME: delete annotations - - // delete main 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); - } - - // Observers: Be sure to finish transaction before calling observers. Note also - // that we always call the observers even though we aren't sure something - // actually got deleted. - transaction.Commit(); - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryObserver, OnDeleteURI(aURI)) - return NS_OK; -} - - -// nsNavHistory::RemovePagesFromHost -// -// This function will delete all history information about pages from a -// given host. If aEntireDomain is set, we will also delete pages from -// sub hosts (so if we are passed in "microsoft.com" we delete -// "www.microsoft.com", "msdn.microsoft.com", etc.). An empty host name -// means local files and anything else with no host name. You can also pass -// in the localized "(local files)" title given to you from a history query. -// -// Silently fails if we have no knowledge of the host -// -// This function is actually pretty simple, it just boils down to a DELETE -// statement, but is out of control due to observers and the two types of -// similar delete operations that we need to supports. - -NS_IMETHODIMP -nsNavHistory::RemovePagesFromHost(const nsACString& aHost, PRBool aEntireDomain) -{ - nsresult rv; - mozStorageTransaction transaction(mDBConn, PR_FALSE); - - // Local files don't have any host name. We don't want to delete all files in - // history when we get passed an empty string, so force to exact match - if (aHost.IsEmpty()) - aEntireDomain = PR_FALSE; - - // translate "(local files)" to an empty host name - // be sure to use the TitleForDomain to get the localized name - nsCString localFiles; - TitleForDomain(EmptyCString(), localFiles); - nsAutoString host16; - if (! aHost.Equals(localFiles)) - host16 = NS_ConvertUTF8toUTF16(aHost); - - // nsISupports version of the host string for passing to observers - nsCOMPtr hostSupports(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv)); - NS_ENSURE_SUCCESS(rv, rv); - rv = hostSupports->SetData(host16); - NS_ENSURE_SUCCESS(rv, rv); - - // see BindQueryClauseParameters for how this host selection works - nsAutoString revHostDot; - GetReversedHostname(host16, revHostDot); - NS_ASSERTION(revHostDot[revHostDot.Length() - 1] == '.', "Invalid rev. host"); - nsAutoString revHostSlash(revHostDot); - revHostSlash.Truncate(revHostSlash.Length() - 1); - revHostSlash.Append(NS_LITERAL_STRING("/")); - - // how we are selecting host names - nsCAutoString conditionString; - if (aEntireDomain) - conditionString.AssignLiteral("WHERE h.rev_host >= ?1 AND h.rev_host < ?2 "); - else - conditionString.AssignLiteral("WHERE h.rev_host = ?1"); - - // Tell the observers about the non-hidden items we are about to delete. - // Since this is a two-step process, if we get an error, we may tell them we - // will delete something but then not actually delete it. Too bad. - // - // Note also that we *include* bookmarked items here. We generally want to - // send out delete notifications for bookmarked items since in general, - // deleting the visits (like we always do) will cause the item to disappear - // from history views. This will also cause all visit dates to be deleted, - // which affects many bookmark views - nsCStringArray deletedURIs; - nsCOMPtr statement; - - // create statement depending on delete type - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "SELECT url FROM moz_history h ") + conditionString, - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindStringParameter(0, revHostDot); - NS_ENSURE_SUCCESS(rv, rv); - if (aEntireDomain) { - rv = statement->BindStringParameter(1, revHostSlash); - NS_ENSURE_SUCCESS(rv, rv); - } - - PRBool hasMore = PR_FALSE; - while ((statement->ExecuteStep(&hasMore) == NS_OK) && hasMore) { - nsCAutoString thisURIString; - if (NS_FAILED(statement->GetUTF8String(0, thisURIString)) || - thisURIString.IsEmpty()) - continue; // no URI - if (! deletedURIs.AppendCString(thisURIString)) - return NS_ERROR_OUT_OF_MEMORY; - } - - // first, delete all the visits - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "DELETE FROM moz_historyvisit WHERE page_id IN (SELECT id FROM moz_history h ") + conditionString + NS_LITERAL_CSTRING(")"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindStringParameter(0, revHostDot); - NS_ENSURE_SUCCESS(rv, rv); - if (aEntireDomain) { - rv = statement->BindStringParameter(1, revHostSlash); - NS_ENSURE_SUCCESS(rv, rv); - } - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - // FIXME: delete annotations - - // now, delete the actual history entries that are not bookmarked - rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - "DELETE FROM moz_history WHERE id IN " - "(SELECT id from moz_history h " - " LEFT OUTER JOIN moz_bookmarks b ON h.id = b.item_child ") - + conditionString + - NS_LITERAL_CSTRING("AND b.item_child IS NULL)"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindStringParameter(0, revHostDot); - NS_ENSURE_SUCCESS(rv, rv); - if (aEntireDomain) { - rv = statement->BindStringParameter(1, revHostSlash); - NS_ENSURE_SUCCESS(rv, rv); - } - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - transaction.Commit(); - - // notify observers - UpdateBatchScoper batch(*this); // sends Begin/EndUpdateBatch to obsrvrs. - if (deletedURIs.Count()) { - nsCOMPtr thisURI; - for (PRUint32 observerIndex = 0; observerIndex < mObservers.Length(); - observerIndex ++) { - const nsCOMPtr &obs = mObservers.ElementAt(observerIndex); - if (! obs) - continue; - - // send it all the URIs - for (PRInt32 i = 0; i < deletedURIs.Count(); i ++) { - if (NS_FAILED(NS_NewURI(getter_AddRefs(thisURI), *deletedURIs[i], - nsnull, nsnull))) - continue; // bad URI - obs->OnDeleteURI(thisURI); - } - } - } - return NS_OK; -} - - -// nsNavHistory::RemoveAllPages -// -// This function is used to clear history. - -NS_IMETHODIMP -nsNavHistory::RemoveAllPages() -{ - // expire everything - mExpire.ClearHistory(); - - // Compress DB. Currently commented out because compression is very slow. - // Deleted data will be overwritten with 0s by sqlite. Note that we have to - // stop the dummy statement before doing this. -#if 0 - StopDummyStatement(); - nsresult rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM")); - StartDummyStatement(); - NS_ENSURE_SUCCESS(rv, rv); -#endif - return NS_OK; -} - - -// nsNavHistory::HidePage -// -// Sets the 'hidden' column to true. If we've not heard of the page, we -// succeed and do nothing. - -NS_IMETHODIMP -nsNavHistory::HidePage(nsIURI *aURI) -{ - return NS_ERROR_NOT_IMPLEMENTED; - /* - // for speed to save disk accesses - mozStorageTransaction transaction(mDBConn, PR_FALSE, - mozIStorageConnection::TRANSACTION_EXCLUSIVE); - - // We need to do a query anyway to see if this URL is already in the DB. - // Might as well ask for the hidden column to save updates in some cases. - nsCOMPtr dbSelectStatement; - nsresult rv = mDBConn->CreateStatement( - NS_LITERAL_CSTRING("SELECT id,hidden FROM moz_history WHERE url = ?1"), - getter_AddRefs(dbSelectStatement)); - NS_ENSURE_SUCCESS(rv, rv); - rv = BindStatementURI(dbSelectStatement, 0, aURI); - NS_ENSURE_SUCCESS(rv, rv); - PRBool alreadyVisited = PR_TRUE; - rv = dbSelectStatement->ExecuteStep(&alreadyVisited); - NS_ENSURE_SUCCESS(rv, rv); - - // don't need to do anything if we've never heard of this page - if (!alreadyVisited) - return NS_OK; - - // modify the existing page if necessary - - PRInt32 oldHiddenState = 0; - rv = dbSelectStatement->GetInt32(1, &oldHiddenState); - NS_ENSURE_SUCCESS(rv, rv); - - if (!oldHiddenState) - return NS_OK; // already marked as hidden, we're done - - // find the old ID, which can be found faster than long URLs - PRInt32 entryid = 0; - rv = dbSelectStatement->GetInt32(0, &entryid); - NS_ENSURE_SUCCESS(rv, rv); - - // need to clear the old statement before we create a new one - dbSelectStatement = nsnull; - - nsCOMPtr dbModStatement; - rv = mDBConn->CreateStatement( - NS_LITERAL_CSTRING("UPDATE moz_history SET hidden = 1 WHERE id = ?1"), - getter_AddRefs(dbModStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - dbModStatement->BindInt32Parameter(0, entryid); - NS_ENSURE_SUCCESS(rv, rv); - - rv = dbModStatement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - // notify observers, finish transaction first - transaction.Commit(); - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryObserver, - OnPageChanged(aURI, - nsINavHistoryObserver::ATTRIBUTE_HIDDEN, - EmptyString())) - - return NS_OK; - */ -} - - -// nsNavHistory::MarkPageAsTyped -// -// Just sets the typed column to true, which will make this page more likely -// to float to the top of autocomplete suggestions. -// -// We can get this notification for pages that have not yet been added to the -// DB. This happens when you type a new URL. The AddURI is called only when -// the page is successfully found. If we don't have an entry yet, we add -// one for this page, marking it as typed but hidden, with a 0 visit count. -// This will get updated when AddURI is called, and it will clear the hidden -// flag for typed URLs. -// -// @see MarkPageAsFollowedBookmark - -NS_IMETHODIMP -nsNavHistory::MarkPageAsTyped(nsIURI *aURI) -{ - // don't add when history is disabled - if (IsHistoryDisabled()) - return NS_OK; - - nsCAutoString uriString; - aURI->GetSpec(uriString); - - // if URL is already in the typed queue, then we need to remove the old one - PRInt64 unusedEventTime; - if (mRecentTyped.Get(uriString, &unusedEventTime)) - mRecentTyped.Remove(uriString); - - if (mRecentTyped.Count() > RECENT_EVENT_QUEUE_MAX_LENGTH) - ExpireNonrecentEvents(&mRecentTyped); - - mRecentTyped.Put(uriString, GetNow()); - return NS_OK; -} - -// nsGlobalHistory2 ************************************************************ - - -// nsNavHistory::AddURI -// -// This is the main method of adding history entries. - -NS_IMETHODIMP -nsNavHistory::AddURI(nsIURI *aURI, PRBool aRedirect, - PRBool aToplevel, nsIURI *aReferrer) -{ - // don't add when history is disabled - if (IsHistoryDisabled()) - return NS_OK; - - PRTime now = PR_Now(); - - nsresult rv; -#ifdef LAZY_ADD - LazyMessage message; - rv = message.Init(LazyMessage::Type_AddURI, aURI); - NS_ENSURE_SUCCESS(rv, rv); - message.isRedirect = aRedirect; - message.isToplevel = aToplevel; - if (aReferrer) { - rv = aReferrer->Clone(getter_AddRefs(message.referrer)); - NS_ENSURE_SUCCESS(rv, rv); - } - message.time = now; - rv = AddLazyMessage(message); - NS_ENSURE_SUCCESS(rv, rv); -#else - rv = AddURIInternal(aURI, now, aRedirect, aToplevel, aReferrer); - NS_ENSURE_SUCCESS(rv, rv); -#endif - - mExpire.OnAddURI(now); - - return NS_OK; -} - - -// nsNavHistory::AddURIInternal -// -// This does the work of AddURI so it can be done lazily. - -nsresult -nsNavHistory::AddURIInternal(nsIURI* aURI, PRTime aTime, PRBool aRedirect, - PRBool aToplevel, nsIURI* aReferrer) -{ - mozStorageTransaction transaction(mDBConn, PR_FALSE); - - PRInt64 redirectBookmark = 0; - PRInt64 visitID, sessionID; - nsresult rv = AddVisitChain(aURI, aTime, aToplevel, aRedirect, aReferrer, - &visitID, &sessionID, &redirectBookmark); - NS_ENSURE_SUCCESS(rv, rv); - - // The bookmark cache of redirects may be out-of-date with this addition, so - // we need to update it. The issue here is if they bookmark "mozilla.org" by - // typing it in without ever having visited "www.mozilla.org". They will then - // get redirected to the latter, and we need to add mozilla.org -> - // www.mozilla.org to the bookmark hashtable. - // - // AddVisitChain will put the spec of a bookmarked URI if it encounters one - // into bookmarkURI. If this is non-empty, we know that something has happened - // with a bookmark and we should probably go update it. - if (redirectBookmark) { - nsNavBookmarks* bookmarkService = nsNavBookmarks::GetBookmarksService(); - if (bookmarkService) { - PRTime now = GetNow(); - bookmarkService->AddBookmarkToHash(redirectBookmark, - now - BOOKMARK_REDIRECT_TIME_THRESHOLD); - } - } - - return transaction.Commit(); -} - - -// nsNavHistory::AddVisitChain -// -// This function is sits between AddURI (which is called when a page is -// visited) and AddVisit (which creates the DB entries) to figure out what -// we should add and what are the detailed parameters that should be used -// (like referring visit ID and typed/bookmarked state). -// -// This function walks up the referring chain and recursively calls itself, -// each time calling InternalAdd to create a new history entry. (When we -// get notified of redirects, we don't actually add any history entries, just -// save them in mRecentRedirects. This function will add all of them for a -// given destination page when that page is actually visited.) -// See GetRedirectFor for more information about how redirects work. -// -// aRedirectBookmark should be empty when this function is first called. If -// there are any redirects that are bookmarks the specs will be placed in -// this buffer. The caller can then determine if any bookmarked items were -// visited so it knows whether to update the bookmark service's redirect -// hashtable. - -nsresult -nsNavHistory::AddVisitChain(nsIURI* aURI, PRTime aTime, - PRBool aToplevel, PRBool aIsRedirect, - nsIURI* aReferrer, PRInt64* aVisitID, - PRInt64* aSessionID, PRInt64* aRedirectBookmark) -{ - PRUint32 transitionType = 0; - PRInt64 referringVisit = 0; - PRTime visitTime = 0; - - nsCAutoString spec; - nsresult rv = aURI->GetSpec(spec); - NS_ENSURE_SUCCESS(rv, rv); - - nsCAutoString redirectSource; - if (GetRedirectFor(spec, redirectSource, &visitTime, &transitionType)) { - // this was a redirect: See GetRedirectFor for info on how this works - nsCOMPtr redirectURI; - rv = NS_NewURI(getter_AddRefs(redirectURI), redirectSource); - NS_ENSURE_SUCCESS(rv, rv); - - // remember if any redirect sources were bookmarked - nsNavBookmarks* bookmarkService = nsNavBookmarks::GetBookmarksService(); - PRBool isBookmarked; - if (bookmarkService && - NS_SUCCEEDED(bookmarkService->IsBookmarked(redirectURI, &isBookmarked)) - && isBookmarked) { - GetUrlIdFor(redirectURI, aRedirectBookmark, PR_FALSE); - } - - // Find the visit for the source. Note that we decrease the time counter, - // which will ensure that the referrer and this page will appear in history - // in the correct order. Since the times are in microseconds, it should not - // normally be possible to get two pages within one microsecond of each - // other so the referrer won't appear before a previous page viewed. - rv = AddVisitChain(redirectURI, aTime - 1, aToplevel, PR_TRUE, aReferrer, - &referringVisit, aSessionID, aRedirectBookmark); - NS_ENSURE_SUCCESS(rv, rv); - - } else if (aReferrer) { - // If there is a referrer, we know you came from somewhere, either manually - // or automatically. For toplevel windows, assume its manual and you want - // to see this in history. For other things, it's some kind of embedded - // navigation. This is true of images and other content the user doesn't - // want to see in their history, but also of embedded frames that the user - // navigated manually and probably DOES want to see in history. - // Unfortunately, there isn't any easy way to distinguish these. - // - // Generally, it boils down to the problem of detecting whether a frame - // content change is the result of a user action, which isn't well defined - // since script could change a frame's source as a result of user request, - // or just because it feels like loading a new ad. The "back" button will - // undo either of these actions. - if (aToplevel) - transitionType = nsINavHistoryService::TRANSITION_LINK; - else - transitionType = nsINavHistoryService::TRANSITION_EMBED; - - // Note that here we should NOT use the GetNow function. That function - // caches the value of "now" until next time the event loop runs. This - // gives better performance, but here we may get many notifications without - // running the event loop. We must preserve these events' ordering. This - // most commonly happens on redirects. - visitTime = PR_Now(); - - // try to turn the referrer into a visit - if (! FindLastVisit(aReferrer, &referringVisit, aSessionID)) { - // we couldn't find a visit for the referrer, don't set it - *aSessionID = GetNewSessionID(); - } - } else { - // When there is no referrer, we know the user must have gotten the link - // from somewhere, so check our sources to see if it was recently typed or - // has a bookmark selected. We don't handle drag-and-drop operations. - if (CheckIsRecentEvent(&mRecentTyped, spec)) - transitionType = nsINavHistoryService::TRANSITION_TYPED; - else if (CheckIsRecentEvent(&mRecentBookmark, spec)) - transitionType = nsINavHistoryService::TRANSITION_BOOKMARK; - - visitTime = PR_Now(); - *aSessionID = GetNewSessionID(); - } - - // this call will create the visit and create/update the page entry - return AddVisit(aURI, visitTime, referringVisit, transitionType, - aIsRedirect, *aSessionID, aVisitID); -} - - -// nsNavHistory::IsVisited -// -// Note that this ignores the "hidden" flag. This function just checks if the -// given page is in the DB for link coloring. The "hidden" flag affects -// the history list view and autocomplete. - -NS_IMETHODIMP -nsNavHistory::IsVisited(nsIURI *aURI, PRBool *_retval) -{ - // if history is disabled, we can optimize - if (IsHistoryDisabled()) { - *_retval = PR_FALSE; - return NS_OK; - } - - nsCAutoString utf8URISpec; - nsresult rv = aURI->GetSpec(utf8URISpec); - NS_ENSURE_SUCCESS(rv, rv); - - *_retval = IsURIStringVisited(utf8URISpec); - return NS_OK; -} - - -// nsNavHistory::SetPageTitle -// -// This sets the page "real" title. Use nsINavHistory::SetPageUserTitle to -// set any user-defined title. -// -// Note that we do not allow empty real titles and will silently ignore such -// requests. When a URL is added we give it a default title based on the -// URL. Most pages provide a title and it gets replaced to something better. -// Some pages don't: some say , and some don't have any title -// element. In BOTH cases, we get SetPageTitle(URI, ""), but in both cases, -// our default title is more useful to the user than "(no title)". -// -// User titles will accept empty strings so the user can still manually -// override it. - -NS_IMETHODIMP -nsNavHistory::SetPageTitle(nsIURI *aURI, - const nsAString & aTitle) -{ - if (aTitle.IsEmpty()) - return NS_OK; - -#ifdef LAZY_ADD - LazyMessage message; - nsresult rv = message.Init(LazyMessage::Type_Title, aURI); - NS_ENSURE_SUCCESS(rv, rv); - message.title = aTitle; - return AddLazyMessage(message); -#else - return SetPageTitleInternal(aURI, PR_FALSE, aTitle); -#endif -} - - -#ifndef MOZILLA_1_8_BRANCH -// nsNavHistory::GetURIGeckoFlags -// -// FIXME: should we try to use annotations for this stuff? - -NS_IMETHODIMP -nsNavHistory::GetURIGeckoFlags(nsIURI* aURI, PRUint32* aResult) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - - -// nsNavHistory::SetURIGeckoFlags -// -// FIXME: should we try to use annotations for this stuff? - -NS_IMETHODIMP -nsNavHistory::SetURIGeckoFlags(nsIURI* aURI, PRUint32 aFlags) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} -#endif - -// nsIGlobalHistory3 *********************************************************** - -// nsNavHistory::AddDocumentRedirect -// -// This adds a redirect mapping from the destination of the redirect to the -// source, time, and type. This mapping is used by GetRedirectFor when we -// get a page added to reconstruct the redirects that happened when a page -// is visited. See GetRedirectFor for more information - -// this is the expiration callback function that deletes stale entries -PLDHashOperator PR_CALLBACK nsNavHistory::ExpireNonrecentRedirects( - nsCStringHashKey::KeyType aKey, RedirectInfo& aData, void* aUserArg) -{ - PRInt64* threshold = NS_REINTERPRET_CAST(PRInt64*, aUserArg); - if (aData.mTimeCreated < *threshold) - return PL_DHASH_REMOVE; - return PL_DHASH_NEXT; -} -NS_IMETHODIMP -nsNavHistory::AddDocumentRedirect(nsIChannel *aOldChannel, - nsIChannel *aNewChannel, - PRInt32 aFlags, - PRBool aTopLevel) -{ - nsresult rv; - nsCOMPtr oldURI, newURI; - rv = aOldChannel->GetURI(getter_AddRefs(oldURI)); - NS_ENSURE_SUCCESS(rv, rv); - rv = aNewChannel->GetURI(getter_AddRefs(newURI)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCString oldSpec, newSpec; - rv = oldURI->GetSpec(oldSpec); - NS_ENSURE_SUCCESS(rv, rv); - rv = newURI->GetSpec(newSpec); - NS_ENSURE_SUCCESS(rv, rv); - - if (mRecentRedirects.Count() > RECENT_EVENT_QUEUE_MAX_LENGTH) { - // expire out-of-date ones - PRInt64 threshold = PR_Now() - RECENT_EVENT_THRESHOLD; - mRecentRedirects.Enumerate(ExpireNonrecentRedirects, - NS_REINTERPRET_CAST(void*, &threshold)); - } - - RedirectInfo info; - - // remove any old entries for this redirect destination - if (mRecentRedirects.Get(newSpec, &info)) - mRecentRedirects.Remove(newSpec); - - // save the new redirect info - info.mSourceURI = oldSpec; - info.mTimeCreated = PR_Now(); - if (aFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) - info.mType = TRANSITION_REDIRECT_TEMPORARY; - else - info.mType = TRANSITION_REDIRECT_PERMANENT; - mRecentRedirects.Put(newSpec, info); - - return NS_OK; -} - - -// nsIObserver ***************************************************************** - -NS_IMETHODIMP -nsNavHistory::Observe(nsISupports *aSubject, const char *aTopic, - const PRUnichar *aData) -{ - if (nsCRT::strcmp(aTopic, gQuitApplicationMessage) == 0) { - if (gTldTypes) { - delete gTldTypes; - gTldTypes = nsnull; - } - nsresult rv; - nsCOMPtr prefService = - do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); - if (NS_SUCCEEDED(rv)) - prefService->SavePrefFile(nsnull); - - // notify expiring system that we're quitting, it may want to do stuff - mExpire.OnQuit(); - } else if (nsCRT::strcmp(aTopic, gXpcomShutdown) == 0) { - nsresult rv; - nsCOMPtr observerService = - do_GetService("@mozilla.org/observer-service;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - observerService->RemoveObserver(this, gXpcomShutdown); - observerService->RemoveObserver(this, gQuitApplicationMessage); - } else if (nsCRT::strcmp(aTopic, "nsPref:changed") == 0) { - PRInt32 oldDays = mExpireDays; - LoadPrefs(); - if (oldDays != mExpireDays) - mExpire.OnExpirationChanged(); - } - - return NS_OK; -} - - -// Lazy stuff ****************************************************************** - -#ifdef LAZY_ADD - -// nsNavHistory::AddLazyLoadFaviconMessage - -nsresult -nsNavHistory::AddLazyLoadFaviconMessage(nsIURI* aPage, nsIURI* aFavicon, - PRBool aForceReload) -{ - LazyMessage message; - nsresult rv = message.Init(LazyMessage::Type_Favicon, aPage); - NS_ENSURE_SUCCESS(rv, rv); - rv = aFavicon->Clone(getter_AddRefs(message.favicon)); - NS_ENSURE_SUCCESS(rv, rv); - message.alwaysLoadFavicon = aForceReload; - return AddLazyMessage(message); -} - - -// nsNavHistory::StartLazyTimer -// -// This schedules flushing of the lazy message queue for the future. -// -// If we already have timer set, we canel it and schedule a new timer in -// the future. This saves you from having to wait if you open a bunch of -// pages in a row. However, we don't want to defer too long, so we'll only -// push it back MAX_LAZY_TIMER_DEFERMENTS times. After that we always -// let the timer go the next time. - -nsresult -nsNavHistory::StartLazyTimer() -{ - if (! mLazyTimer) { - mLazyTimer = do_CreateInstance("@mozilla.org/timer;1"); - if (! mLazyTimer) - return NS_ERROR_OUT_OF_MEMORY; - } else { - if (mLazyTimerSet) { - if (mLazyTimerDeferments >= MAX_LAZY_TIMER_DEFERMENTS) { - // already set and we don't want to push it back any later, use that one - return NS_OK; - } else { - // push back the active timer - mLazyTimer->Cancel(); - mLazyTimerDeferments ++; - } - } - } - nsresult rv = mLazyTimer->InitWithFuncCallback(LazyTimerCallback, this, - LAZY_MESSAGE_TIMEOUT, - nsITimer::TYPE_ONE_SHOT); - NS_ENSURE_SUCCESS(rv, rv); - mLazyTimerSet = PR_TRUE; - return NS_OK; -} - - -// nsNavHistory::AddLazyMessage - -nsresult -nsNavHistory::AddLazyMessage(const LazyMessage& aMessage) -{ - if (! mLazyMessages.AppendElement(aMessage)) - return NS_ERROR_OUT_OF_MEMORY; - return StartLazyTimer(); -} - - -// nsNavHistory::LazyTimerCallback - -void // static -nsNavHistory::LazyTimerCallback(nsITimer* aTimer, void* aClosure) -{ - nsNavHistory* that = NS_STATIC_CAST(nsNavHistory*, aClosure); - that->mLazyTimerSet = PR_FALSE; - that->mLazyTimerDeferments = 0; - that->CommitLazyMessages(); -} - - -// nsNavHistory::CommitLazyMessages - -void -nsNavHistory::CommitLazyMessages() -{ - mozStorageTransaction transaction(mDBConn, PR_TRUE); - for (PRUint32 i = 0; i < mLazyMessages.Length(); i ++) { - LazyMessage& message = mLazyMessages[i]; - switch (message.type) { - case LazyMessage::Type_AddURI: - AddURIInternal(message.uri, message.time, message.isRedirect, - message.isToplevel, message.referrer); - break; - case LazyMessage::Type_Title: - SetPageTitleInternal(message.uri, PR_FALSE, message.title); - break; - case LazyMessage::Type_Favicon: { - nsFaviconService* faviconService = nsFaviconService::GetFaviconService(); - if (faviconService) { - nsCString spec; - message.uri->GetSpec(spec); - faviconService->DoSetAndLoadFaviconForPage(message.uri, - message.favicon, - message.alwaysLoadFavicon); - } - break; - } - default: - NS_NOTREACHED("Invalid lazy message type"); - } - } - mLazyMessages.Clear(); -} -#endif // LAZY_ADD - - -// Query stuff ***************************************************************** - - -// nsNavHistory::QueryToSelectClause -// -// THE ORDER AND BEHAVIOR SHOULD BE IN SYNC WITH BindQueryClauseParameters -// -// I don't check return values from the query object getters because there's -// no way for those to fail. - -nsresult -nsNavHistory::QueryToSelectClause(nsNavHistoryQuery* aQuery, // const - PRInt32 aStartParameter, - nsCString* aClause, - PRInt32* aParamCount, - const nsACString& aCommonConditions) -{ - PRBool hasIt; - - aClause->Truncate(); - *aParamCount = 0; - nsCAutoString paramString; - - // note common condition visit_count > 0 is set under the annotation section - - // begin time - if (NS_SUCCEEDED(aQuery->GetHasBeginTime(&hasIt)) && hasIt) { - parameterString(aStartParameter + *aParamCount, paramString); - *aClause += NS_LITERAL_CSTRING("v.visit_date >= ") + paramString; - (*aParamCount) ++; - } - - // end time - if (NS_SUCCEEDED(aQuery->GetHasEndTime(&hasIt)) && hasIt) { - if (! aClause->IsEmpty()) - *aClause += NS_LITERAL_CSTRING(" AND "); - parameterString(aStartParameter + *aParamCount, paramString); - *aClause += NS_LITERAL_CSTRING("v.visit_date <= ") + paramString; - (*aParamCount) ++; - } - - // search terms FIXME - - // only bookmarked - if (aQuery->OnlyBookmarked()) { - if (! aClause->IsEmpty()) - *aClause += NS_LITERAL_CSTRING(" AND "); - - *aClause += NS_LITERAL_CSTRING("EXISTS (SELECT b.item_child FROM moz_bookmarks b WHERE b.item_child = h.id)"); - } - - // domain - if (NS_SUCCEEDED(aQuery->GetHasDomain(&hasIt)) && hasIt) { - if (! aClause->IsEmpty()) - *aClause += NS_LITERAL_CSTRING(" AND "); - - PRBool domainIsHost = PR_FALSE; - aQuery->GetDomainIsHost(&domainIsHost); - if (domainIsHost) { - parameterString(aStartParameter + *aParamCount, paramString); - *aClause += NS_LITERAL_CSTRING("h.rev_host = ") + paramString; - aClause->Append(' '); - (*aParamCount) ++; - } else { - // see domain setting in BindQueryClauseParameters for why we do this - parameterString(aStartParameter + *aParamCount, paramString); - *aClause += NS_LITERAL_CSTRING("h.rev_host >= ") + paramString; - (*aParamCount) ++; - - parameterString(aStartParameter + *aParamCount, paramString); - *aClause += NS_LITERAL_CSTRING(" AND h.rev_host < ") + paramString; - aClause->Append(' '); - (*aParamCount) ++; - } - } - - // URI - // - // Performance improvement: Selecting URI by prefixes this way is slow because - // sqlite will not use indices when you use substring. Currently, there is - // not really any use for URI queries, so this isn't worth optimizing a lot. - // In the future, we could do a >=,<= thing like we do for domain names to - // make it use the index. - if (NS_SUCCEEDED(aQuery->GetHasUri(&hasIt)) && hasIt) { - if (! aClause->IsEmpty()) - *aClause += NS_LITERAL_CSTRING(" AND "); - - nsCAutoString paramString; - parameterString(aStartParameter + *aParamCount, paramString); - (*aParamCount) ++; - - nsCAutoString match; - if (aQuery->UriIsPrefix()) { - // Prefix: want something of the form SUBSTR(h.url, 0, length(?1)) = ?1 - *aClause += NS_LITERAL_CSTRING("SUBSTR(h.url, 0, LENGTH(") + - paramString + NS_LITERAL_CSTRING(")) = ") + paramString; - } else { - *aClause += NS_LITERAL_CSTRING("h.url = ") + paramString; - } - aClause->Append(' '); - } - - // annotation - aQuery->GetHasAnnotation(&hasIt); - if (! aClause->IsEmpty()) - *aClause += NS_LITERAL_CSTRING(" AND "); - if (hasIt) { - nsCAutoString paramString; - parameterString(aStartParameter + *aParamCount, paramString); - (*aParamCount) ++; - - if (aQuery->AnnotationIsNot()) - aClause->AppendLiteral("NOT "); - aClause->AppendLiteral("EXISTS (SELECT anno_id FROM moz_anno anno JOIN moz_anno_name annoname ON anno.name_id = annoname.name_id WHERE anno.page = h.id AND annoname.name = "); - aClause->Append(paramString); - aClause->AppendLiteral(") "); - // annotation-based queries don't get the common conditions, so you get - // all URLs with that annotation - } else { - // all non-annotation queries return only visited items - aClause->Append(aCommonConditions); - } - - return NS_OK; -} - - -// nsNavHistory::BindQueryClauseParameters -// -// THE ORDER AND BEHAVIOR SHOULD BE IN SYNC WITH QueryToSelectClause - -nsresult -nsNavHistory::BindQueryClauseParameters(mozIStorageStatement* statement, - PRInt32 aStartParameter, - nsNavHistoryQuery* aQuery, // const - PRInt32* aParamCount) -{ - nsresult rv; - (*aParamCount) = 0; - - PRBool hasIt; - - // begin time - if (NS_SUCCEEDED(aQuery->GetHasBeginTime(&hasIt)) && hasIt) { - PRTime time = NormalizeTime(aQuery->BeginTimeReference(), - aQuery->BeginTime()); - rv = statement->BindInt64Parameter(aStartParameter + *aParamCount, time); - NS_ENSURE_SUCCESS(rv, rv); - (*aParamCount) ++; - } - - // end time - if (NS_SUCCEEDED(aQuery->GetHasEndTime(&hasIt)) && hasIt) { - PRTime time = NormalizeTime(aQuery->EndTimeReference(), - aQuery->EndTime()); - rv = statement->BindInt64Parameter(aStartParameter + *aParamCount, time); - NS_ENSURE_SUCCESS(rv, rv); - (*aParamCount) ++; - } - - // search terms FIXME - - // onlyBookmarked: nothing to bind - - // domain (see GetReversedHostname for more info on reversed host names) - if (NS_SUCCEEDED(aQuery->GetHasDomain(&hasIt)) && hasIt) { - nsString revDomain; - GetReversedHostname(NS_ConvertUTF8toUTF16(aQuery->Domain()), revDomain); - - if (aQuery->DomainIsHost()) { - rv = statement->BindStringParameter(aStartParameter + *aParamCount, revDomain); - NS_ENSURE_SUCCESS(rv, rv); - (*aParamCount) ++; - } else { - // for "mozilla.org" do query >= "gro.allizom." AND < "gro.allizom/" - // which will get everything starting with "gro.allizom." while using the - // index (using SUBSTRING() causes indexes to be discarded). - NS_ASSERTION(revDomain[revDomain.Length() - 1] == '.', "Invalid rev. host"); - rv = statement->BindStringParameter(aStartParameter + *aParamCount, revDomain); - NS_ENSURE_SUCCESS(rv, rv); - (*aParamCount) ++; - revDomain.Truncate(revDomain.Length() - 1); - revDomain.Append(PRUnichar('/')); - rv = statement->BindStringParameter(aStartParameter + *aParamCount, revDomain); - NS_ENSURE_SUCCESS(rv, rv); - (*aParamCount) ++; - } - } - - // URI - if (NS_SUCCEEDED(aQuery->GetHasUri(&hasIt)) && hasIt) { - BindStatementURI(statement, aStartParameter + *aParamCount, aQuery->Uri()); - (*aParamCount) ++; - } - - // annotation - aQuery->GetHasAnnotation(&hasIt); - if (hasIt) { - rv = statement->BindUTF8StringParameter(aStartParameter + *aParamCount, - aQuery->Annotation()); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - - -// nsNavHistory::ResultsAsList -// - -nsresult -nsNavHistory::ResultsAsList(mozIStorageStatement* statement, - nsNavHistoryQueryOptions* aOptions, - nsCOMArray* aResults) -{ - nsresult rv; - nsCOMPtr row = do_QueryInterface(statement, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMore = PR_FALSE; - while (NS_SUCCEEDED(statement->ExecuteStep(&hasMore)) && hasMore) { - nsCOMPtr result; - rv = RowToResult(row, aOptions, getter_AddRefs(result)); - NS_ENSURE_SUCCESS(rv, rv); - aResults->AppendObject(result); - } - return NS_OK; -} - - -// nsNavHistory::RecursiveGroup -// -// aSource and aDest must be different! -// -// This just calls the correct grouping subroutine. These will generate the -// grouping and return to us a list of nodes that are the groups. -// -// If we need to do another level of grouping, we go in and replace those -// container's children with another level of grouping. This is less -// efficient because we need to copy the lists around. However, multilevel -// grouping will be very uncommon so we are more interested in an optimized -// single level of grouping. - -nsresult -nsNavHistory::RecursiveGroup(const nsCOMArray& aSource, - const PRUint32* aGroupingMode, PRUint32 aGroupCount, - nsCOMArray* aDest) -{ - NS_ASSERTION(aGroupCount > 0, "Invalid group count"); - NS_ASSERTION(aDest->Count() == 0, "Destination array is not empty"); - NS_ASSERTION(&aSource != aDest, "Source and dest must be different for grouping"); - - nsresult rv; - switch (aGroupingMode[0]) { - case nsINavHistoryQueryOptions::GROUP_BY_HOST: - rv = GroupByHost(aSource, aDest, PR_FALSE); - break; - case nsINavHistoryQueryOptions::GROUP_BY_DOMAIN: - rv = GroupByHost(aSource, aDest, PR_TRUE); - break; - default: - // unknown grouping mode - return NS_ERROR_INVALID_ARG; - } - NS_ENSURE_SUCCESS(rv, rv); - - if (aGroupCount > 1) { - // Sort another level: We need to copy the array since we want the output - // to be our level's destionation arrays. - for (PRInt32 i = 0; i < aDest->Count(); i ++) { - nsNavHistoryResultNode* curNode = (*aDest)[i]; - if (curNode->IsContainer()) { - nsNavHistoryContainerResultNode* container = curNode->GetAsContainer(); - nsCOMArray temp(container->mChildren); - container->mChildren.Clear(); - rv = RecursiveGroup(temp, &aGroupingMode[1], aGroupCount - 1, - &container->mChildren); - NS_ENSURE_SUCCESS(rv, rv); - } - } - } - return NS_OK; -} - - -// nsNavHistory::GroupByHost -// -// OPTIMIZATION: This parses the URI of each node that is coming in. This -// makes it kind of slow. A previous version of this code used a host on -// each result node. This host name is populated from the database, so it -// doesn't have to be populated at query time. This is faster, but takes up -// a lot of space in result nodes that isn't very helpful. -// -// One option would be to store the host names in the nodes only if we will -// be grouping by host later. Once we group by host, we could set it to the -// empty string to free that heap data. Then this code would be fast but -// we would only be charged the overhead of an empty string on each node. - -nsresult -nsNavHistory::GroupByHost(const nsCOMArray& aSource, - nsCOMArray* aDest, - PRBool aIsDomain) -{ - nsDataHashtable hosts; - if (! hosts.Init(512)) - return NS_ERROR_OUT_OF_MEMORY; - - for (PRInt32 i = 0; i < aSource.Count(); i ++) { - if (! aSource[i]->IsURI()) { - // what do we do with non-URLs? I'll just dump them into the top level - aDest->AppendObject(aSource[i]); - continue; - } - - // get the host name - nsCOMPtr uri; - nsCAutoString fullHostName; - if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), aSource[i]->mURI)) || - NS_FAILED(uri->GetHost(fullHostName))) { - // invalid host name, just drop it in the top level - aDest->AppendObject(aSource[i]); - continue; - } - - nsCAutoString curHostName; - if (aIsDomain) { - DomainNameFromHostName(fullHostName, curHostName); - } else { - // just use the full host name - curHostName = fullHostName; - } - - nsNavHistoryContainerResultNode* curTopGroup = nsnull; - if (! hosts.Get(curHostName, &curTopGroup)) { - // need to create an entry for this host - nsCAutoString title; - TitleForDomain(curHostName, title); - - curTopGroup = new nsNavHistoryContainerResultNode(EmptyCString(), title, - EmptyCString(), nsNavHistoryResultNode::RESULT_TYPE_HOST, PR_TRUE, - EmptyCString()); - if (! curTopGroup) - return NS_ERROR_OUT_OF_MEMORY; - - if (! hosts.Put(curHostName, curTopGroup)) - return NS_ERROR_OUT_OF_MEMORY; - nsresult rv = aDest->AppendObject(curTopGroup); - NS_ENSURE_SUCCESS(rv, rv); - } - if (! curTopGroup->mChildren.AppendObject(aSource[i])) - return NS_ERROR_OUT_OF_MEMORY; - } - return NS_OK; -} - - -// nsNavHistory::FilterResultSet -// -// Currently, this just does title/url filtering. This should be expanded in -// the future. - -nsresult -nsNavHistory::FilterResultSet(const nsCOMArray& aSet, - nsCOMArray* aFiltered, - const nsString& aSearch) -{ - nsStringArray terms; - ParseSearchQuery(aSearch, &terms); - - // if there are no search terms, just return everything (i.e. do nothing) - if (terms.Count() == 0) { - aFiltered->AppendObjects(aSet); - return NS_OK; - } - - nsCStringArray searchAnnotations; - /* - if (mAnnotationService) { - searchAnnotations.AppendCString(NS_LITERAL_CSTRING("qwer")); - searchAnnotations.AppendCString(NS_LITERAL_CSTRING("asdf")); - searchAnnotations.AppendCString(NS_LITERAL_CSTRING("zxcv")); - //mAnnotationService->GetSearchableAnnotations(); - } - */ - - for (PRInt32 nodeIndex = 0; nodeIndex < aSet.Count(); nodeIndex ++) { - PRBool allTermsFound = PR_TRUE; - - nsStringArray curAnnotations; - /* - if (searchAnnotations.Count()) { - // come up with a list of all annotation *values* we need to search - for (PRInt32 annotIndex = 0; annotIndex < searchAnnotations.Count(); annotIndex ++) { - nsString annot; - if (NS_SUCCEEDED(mAnnotationService->GetAnnotationString( - aSet[nodeIndex]->mURI, - *searchAnnotations[annotIndex], - annot))) - curAnnotations.AppendString(annot); - } - } - */ - - for (PRInt32 termIndex = 0; termIndex < terms.Count(); termIndex ++) { - PRBool termFound = PR_FALSE; - // title and URL - if (CaseInsensitiveFindInReadable(*terms[termIndex], - NS_ConvertUTF8toUTF16(aSet[nodeIndex]->mTitle)) || - (aSet[nodeIndex]->IsURI() && - CaseInsensitiveFindInReadable(*terms[termIndex], - NS_ConvertUTF8toUTF16(aSet[nodeIndex]->mURI)))) - termFound = PR_TRUE; - // searchable annotations - /*if (! termFound) { - for (PRInt32 annotIndex = 0; annotIndex < curAnnotations.Count(); annotIndex ++) { - if (CaseInsensitiveFindInReadable(*terms[termIndex], - *curAnnotations[annotIndex])) - termFound = PR_TRUE; - } - }*/ - if (! termFound) { - allTermsFound = PR_FALSE; - break; - } - } - if (allTermsFound) - aFiltered->AppendObject(aSet[nodeIndex]); - } - return NS_OK; -} - - -// nsNavHistory::CheckIsRecentEvent -// -// Sees if this URL happened "recently." -// -// It is always removed from our recent list no matter what. It only counts -// as "recent" if the event happend more recently than our event -// threshold ago. - -PRBool -nsNavHistory::CheckIsRecentEvent(RecentEventHash* hashTable, - const nsACString& url) -{ - PRTime eventTime; - if (hashTable->Get(url, &eventTime)) { - hashTable->Remove(url); - if (eventTime > GetNow() - RECENT_EVENT_THRESHOLD) - return PR_TRUE; - return PR_FALSE; - } - return PR_FALSE; -} - - -// nsNavHistory::ExpireNonrecentEvents -// -// This goes through our - -PR_STATIC_CALLBACK(PLDHashOperator) -ExpireNonrecentEventsCallback(nsCStringHashKey::KeyType aKey, - PRInt64& aData, - void* userArg) -{ - PRInt64* threshold = NS_REINTERPRET_CAST(PRInt64*, userArg); - if (aData < *threshold) - return PL_DHASH_REMOVE; - return PL_DHASH_NEXT; -} -void -nsNavHistory::ExpireNonrecentEvents(RecentEventHash* hashTable) -{ - PRInt64 threshold = GetNow() - RECENT_EVENT_THRESHOLD; - hashTable->Enumerate(ExpireNonrecentEventsCallback, - NS_REINTERPRET_CAST(void*, &threshold)); -} - - -// nsNavHistory::GetRedirectFor -// -// Given a destination URI, this finds a recent redirect that resulted in -// this URI. If it finds one, it will put the redirect source info into -// the out params and return true. If there is no matching redirect, it will -// return false. -// -// @param aDestination The destination URI spec of the redirect to look for. -// @param aSource Will be filled with the redirect source URI when a -// redirect is found. -// @param aTime Will be filled with the time the redirect happened -// when a redirect is found. -// @param aRedirectType Will be filled with the redirect type when a redirect -// is found. Will be either -// TRANSITION_REDIRECT_PERMANENT or -// TRANSITION_REDIRECT_TEMPORARY -// @returns True if the redirect is found. -// -// HOW REDIRECT TRACKING WORKS -// --------------------------- -// When we get an AddDocumentRedirect message, we store the redirect in -// our mRecentRedirects which maps the destination URI to a source,time pair. -// When we get a new URI, we see if there were any redirects to this page -// in the hash table. If found, we know that the page came through the given -// redirect and add it. -// -// Example: Page S redirects throught R1, then R2, to give page D. Page S -// will have been already added to history. -// - AddDocumentRedirect(R1, R2) -// - AddDocumentRedirect(R2, D) -// - AddURI(uri=D, referrer=S) -// -// When we get the AddURI(D), we see the hash table has a value for D from R2. -// We have to recursively check that source since there could be more than -// one redirect, as in this case. Here we see there was a redirect to R2 from -// R1. The referrer for D is S, so we know S->R1->R2->D. -// -// Alternatively, the user could have typed or followed a bookmark from S. -// In this case, with two redirects we'll get: -// - MarkPageAsTyped(S) -// - AddDocumentRedirect(S, R) -// - AddDocumentRedirect(R, D) -// - AddURI(uri=D, referrer=null) -// We need to be careful to add a visit to S in this case with an incoming -// transition of typed and an outgoing transition of redirect. -// -// Note that this can get confused in some cases where you have a page -// open in more than one window loading at the same time. This should be rare, -// however, and should not affect much. - -PRBool -nsNavHistory::GetRedirectFor(const nsACString& aDestination, - nsACString& aSource, PRTime* aTime, - PRUint32* aRedirectType) -{ - RedirectInfo info; - if (mRecentRedirects.Get(aDestination, &info)) { - mRecentRedirects.Remove(aDestination); - if (info.mTimeCreated < GetNow() - RECENT_EVENT_THRESHOLD) - return PR_FALSE; // too long ago, probably invalid - aSource = info.mSourceURI; - *aTime = info.mTimeCreated; - *aRedirectType = info.mType; - return PR_TRUE; - } - return PR_FALSE; -} - - -// nsNavHistory::RowToResult -// -// Here, we just have a generic row. It could be a query, URL, visit, -// or full visit. - -nsresult -nsNavHistory::RowToResult(mozIStorageValueArray* aRow, - nsNavHistoryQueryOptions* aOptions, - nsNavHistoryResultNode** aResult) -{ - *aResult = nsnull; - NS_ASSERTION(aRow && aOptions && aResult, "Null pointer in RowToResult"); - - // URL - nsCAutoString url; - nsresult rv = aRow->GetUTF8String(kGetInfoIndex_URL, url); - NS_ENSURE_SUCCESS(rv, rv); - - // title - nsCAutoString title; - title.SetIsVoid(PR_TRUE); - if (! aOptions->ForceOriginalTitle()) { - rv = aRow->GetUTF8String(kGetInfoIndex_UserTitle, title); - NS_ENSURE_SUCCESS(rv, rv); - } - if (title.IsVoid()) { - rv = aRow->GetUTF8String(kGetInfoIndex_Title, title); - NS_ENSURE_SUCCESS(rv, rv); - } - - PRUint32 accessCount = aRow->AsInt32(kGetInfoIndex_VisitCount); - PRTime time = aRow->AsInt64(kGetInfoIndex_VisitDate); - - // favicon - nsCAutoString favicon; - rv = aRow->GetUTF8String(kGetInfoIndex_FaviconURL, favicon); - NS_ENSURE_SUCCESS(rv, rv); - - if (IsQueryURI(url)) { - // special case "place:" URIs: turn them into containers - return QueryRowToResult(url, title, accessCount, time, favicon, aResult); - } else if (aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_URI) { - *aResult = new nsNavHistoryResultNode(url, title, accessCount, time, - favicon); - if (! *aResult) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(*aResult); - return NS_OK; - } - // now we know the result type is some kind of visit (regular or full) - - // session - PRInt64 session = aRow->AsInt64(kGetInfoIndex_SessionId); - - if (aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_VISIT) { - *aResult = new nsNavHistoryVisitResultNode(url, title, accessCount, time, - favicon, session); - if (! *aResult) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(*aResult); - return NS_OK; - } - - // now it had better be a full visit - NS_ASSERTION(aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_FULL_VISIT, - "Invalid result type in RowToResult"); - - NS_NOTREACHED("Full visits not supported yet."); - // visit ID - /*PRUint32 accessCount; - rv = aRow->GetInt32(kGetInfoIndex_VisitCount, &accessCount); - NS_ENSURE_SUCCESS(rv, rv);*/ - - // referring visit ID - /*PRUint32 accessCount; - rv = aRow->GetInt32(kGetInfoIndex_VisitCount, &accessCount); - NS_ENSURE_SUCCESS(rv, rv);*/ - - // transition - /*PRUint32 transition; - rv = aRow->GetInt32(kGetInfoIndex_VisitCount, &transition); - NS_ENSURE_SUCCESS(rv, rv); - - *aResult = new nsNavHistoryFullVisitResultNode(title, accessCount, time, - favicon, url, session, visitId, - referring, transition); - if (! *aResult) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(*aResult);*/ - return NS_OK; -} - - -// nsNavHistory::QueryRowToResult -// -// Called by RowToResult when the URI is a place: URI to generate the proper -// folder or query node. - -nsresult -nsNavHistory::QueryRowToResult(const nsACString& aURI, const nsACString& aTitle, - PRUint32 aAccessCount, PRTime aTime, - const nsACString& aFavicon, - nsNavHistoryResultNode** aNode) -{ - nsCOMArray queries; - nsCOMPtr options; - nsresult rv = QueryStringToQueryArray(aURI, &queries, - getter_AddRefs(options)); - if (NS_FAILED(rv)) { - // This was a query that did not parse, what do we do? We don't want to - // return failure since that will kill the whole query process. Instead - // make a query node with the query as a string. This way we have a valid - // node for the user to manipulate that will look like a query, but it will - // never populate since the query string is invalid. - *aNode = new nsNavHistoryQueryResultNode(aURI, aTitle, aFavicon); - if (! *aNode) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(*aNode); - } else { - PRInt64 folderId = GetSimpleBookmarksQueryFolder(queries); - if (folderId) { - // simple bookmarks folder, magically generate a bookmarks folder node - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY); - - // this addrefs for us - rv = bookmarks->ResultNodeForFolder(folderId, options, aNode); - NS_ENSURE_SUCCESS(rv, rv); - } else { - // regular query - *aNode = new nsNavHistoryQueryResultNode(aTitle, EmptyCString(), - queries, options); - if (! *aNode) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(*aNode); - } - } - return NS_OK; -} - - -// nsNavHistory::VisitIdToResultNode -// -// Used by the query results to create new nodes on the fly when -// notifications come in. This just creates a node for the given visit ID. - -nsresult -nsNavHistory::VisitIdToResultNode(PRInt64 visitId, - nsNavHistoryQueryOptions* aOptions, - nsNavHistoryResultNode** aResult) -{ - mozIStorageStatement* statement; // non-owning! - if (aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_VISIT || - aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_FULL_VISIT) { - // visit query - want exact visit time - statement = mDBVisitToVisitResult; - } else { - // URL results - want last visit time - statement = mDBVisitToURLResult; - } - - mozStorageStatementScoper scoper(statement); - nsresult rv = statement->BindInt64Parameter(0, visitId); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMore = PR_FALSE; - rv = statement->ExecuteStep(&hasMore); - NS_ENSURE_SUCCESS(rv, rv); - if (! hasMore) { - NS_NOTREACHED("Trying to get a result node for an invalid visit"); - return NS_ERROR_INVALID_ARG; - } - - return RowToResult(statement, aOptions, aResult); -} - - -// nsNavHistory::UriToResultNode -// -// Used by the query results to create new nodes on the fly when -// notifications come in. This creates a URL node for the given URL. - -nsresult -nsNavHistory::UriToResultNode(nsIURI* aUri, nsNavHistoryQueryOptions* aOptions, - nsNavHistoryResultNode** aResult) -{ - // this query must be asking for URL results, because we don't have enough - // information to construct a visit result node here - NS_ASSERTION(aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_URI, - "Can't make visits from URIs"); - mozStorageStatementScoper scoper(mDBUrlToUrlResult); - nsresult rv = BindStatementURI(mDBUrlToUrlResult, 0, aUri); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMore = PR_FALSE; - rv = mDBUrlToUrlResult->ExecuteStep(&hasMore); - NS_ENSURE_SUCCESS(rv, rv); - if (! hasMore) { - NS_NOTREACHED("Trying to get a result node for an invalid URL"); - return NS_ERROR_INVALID_ARG; - } - - return RowToResult(mDBUrlToUrlResult, aOptions, aResult); -} - - -// nsNavHistory::TitleForDomain -// -// This computes the title for a given domain. Normally, this is just the -// domain name, but we specially handle empty cases to give you a nice -// localized string. - -void -nsNavHistory::TitleForDomain(const nsCString& domain, nsACString& aTitle) -{ - if (! domain.IsEmpty()) { - aTitle = domain; - return; - } - - // use the localized one instead - nsXPIDLString value; - nsresult rv = mBundle->GetStringFromName( - NS_LITERAL_STRING("localhost").get(), getter_Copies(value)); - if (NS_SUCCEEDED(rv)) - CopyUTF16toUTF8(value, aTitle); - else - aTitle.Truncate(0); -} - - -// nsNavHistory::SetPageTitleInternal -// -// Called to set either the user-defined title (aIsUserTitle=true) or the -// "real" page title (aIsUserTitle=false) for the given URI. Used as a -// backend for SetPageUserTitle and SetTitle -// -// Will fail for pages that are not in the DB. To clear the corresponding -// title, use aTitle.SetIsVoid(). Sending an empty string will save an -// empty string instead of clearing it. - -nsresult -nsNavHistory::SetPageTitleInternal(nsIURI* aURI, PRBool aIsUserTitle, - const nsAString& aTitle) -{ - nsresult rv; - - mozStorageTransaction transaction(mDBConn, PR_TRUE); - - // first, make sure the page exists, and fetch the old title (we need the one - // that isn't changing to send notifications) - nsAutoString title; - nsAutoString userTitle; - { // scope for statement - mozStorageStatementScoper infoScoper(mDBGetURLPageInfo); - rv = BindStatementURI(mDBGetURLPageInfo, 0, aURI); - NS_ENSURE_SUCCESS(rv, rv); - PRBool hasURL = PR_FALSE; - rv = mDBGetURLPageInfo->ExecuteStep(&hasURL); - NS_ENSURE_SUCCESS(rv, rv); - if (! hasURL) { - // we don't have the URL, give up - return NS_ERROR_NOT_AVAILABLE; - } - - // page title - rv = mDBGetURLPageInfo->GetString(kGetInfoIndex_Title, title); - NS_ENSURE_SUCCESS(rv, rv); - - // user title - rv = mDBGetURLPageInfo->GetString(kGetInfoIndex_UserTitle, userTitle); - NS_ENSURE_SUCCESS(rv, rv); - } - - // It is actually common to set the title to be the same thing it used to - // be. For example, going to any web page will always cause a title to be set, - // even though it will often be unchanged since the last visit. In these - // cases, we can avoid DB writing and (most significantly) observer overhead. - if (aIsUserTitle && aTitle.IsVoid() == userTitle.IsVoid() && - aTitle == userTitle) - return NS_OK; - if (! aIsUserTitle && aTitle.IsVoid() == title.IsVoid() && - aTitle == title) - return NS_OK; - - nsCOMPtr dbModStatement; - if (aIsUserTitle) { - userTitle = aTitle; - rv = mDBConn->CreateStatement( - NS_LITERAL_CSTRING("UPDATE moz_history SET user_title = ?1 WHERE url = ?2"), - getter_AddRefs(dbModStatement)); - } else { - title = aTitle; - rv = mDBConn->CreateStatement( - NS_LITERAL_CSTRING("UPDATE moz_history SET title = ?1 WHERE url = ?2"), - getter_AddRefs(dbModStatement)); - } - NS_ENSURE_SUCCESS(rv, rv); - - // title - if (aTitle.IsVoid()) - dbModStatement->BindNullParameter(0); - else - dbModStatement->BindStringParameter(0, StringHead(aTitle, HISTORY_TITLE_LENGTH_MAX)); - NS_ENSURE_SUCCESS(rv, rv); - - // url - rv = BindStatementURI(dbModStatement, 1, aURI); - NS_ENSURE_SUCCESS(rv, rv); - - rv = dbModStatement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - transaction.Commit(); - - // observers (have to check first if it's bookmarked) - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryObserver, - OnTitleChanged(aURI, title, userTitle, aIsUserTitle)) - - return NS_OK; - -} - - -// nsNavHistory::CreateLookupIndexes -// -// This creates some indexes on the history tables which are expensive to -// update when we're doing many insertions, as with history import. Instead, -// we defer creation of the index until import is finished. -// -// FIXME: We should check if the index exists (bug 327317) and then not -// try to create it. That way we can check for errors. Currently we ignore -// errors since the indeices may already exist. - -nsresult -nsNavHistory::CreateLookupIndexes() -{ - nsresult rv; - - // History table indexes - rv = mDBConn->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE INDEX moz_history_hostindex ON moz_history (rev_host)")); - //NS_ENSURE_SUCCESS(rv, rv); - rv = mDBConn->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE INDEX moz_history_visitcount ON moz_history (visit_count)")); - //NS_ENSURE_SUCCESS(rv, rv); - - // Visit table indexes - rv = mDBConn->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE INDEX moz_historyvisit_fromindex ON moz_historyvisit (from_visit)")); - //NS_ENSURE_SUCCESS(rv, rv); - rv = mDBConn->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE INDEX moz_historyvisit_dateindex ON moz_historyvisit (visit_date)")); - //NS_ENSURE_SUCCESS(rv, rv); - -#ifdef IN_MEMORY_LINKS - // In-memory links indexes - rv = mMemDBConn->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE INDEX moz_memhistory_index ON moz_memhistory (url)")); - NS_ENSURE_SUCCESS(rv, rv); -#endif - - return NS_OK; -} - -nsresult -nsNavHistory::AddPageWithVisit(nsIURI *aURI, - const nsString &aTitle, - const nsString &aUserTitle, - PRBool aHidden, PRBool aTyped, - PRInt32 aVisitCount, - PRInt32 aLastVisitTransition, - PRTime aLastVisitDate) -{ - PRBool canAdd = PR_FALSE; - nsresult rv = CanAddURI(aURI, &canAdd); - NS_ENSURE_SUCCESS(rv, rv); - if (!canAdd) { - return NS_OK; - } - - PRInt64 pageID; - rv = InternalAddNewPage(aURI, aTitle, aHidden, aTyped, aVisitCount, &pageID); - NS_ENSURE_SUCCESS(rv, rv); - - if (aLastVisitDate != -1) { - PRInt64 visitID; - rv = InternalAddVisit(pageID, 0, 0, - aLastVisitDate, aLastVisitTransition, &visitID); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -nsresult -nsNavHistory::RemoveDuplicateURIs() -{ - nsCOMPtr statement; - nsresult rv = mDBConn->CreateStatement( - NS_LITERAL_CSTRING("SELECT id, url FROM moz_history ORDER BY url"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - nsTArray duplicates; - nsCAutoString lastURI; - PRBool hasMore; - while (NS_SUCCEEDED(statement->ExecuteStep(&hasMore)) && hasMore) { - nsCAutoString uri; - statement->GetUTF8String(1, uri); - if (uri.Equals(lastURI)) { - duplicates.AppendElement(statement->AsInt64(0)); - } else { - lastURI = uri; - } - } - - // Now remove all of the duplicates from the history and visit tables. - rv = mDBConn->CreateStatement( - NS_LITERAL_CSTRING("DELETE FROM moz_history WHERE id = ?1"), - getter_AddRefs(statement)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr visitDelete; - rv = mDBConn->CreateStatement( - NS_LITERAL_CSTRING("DELETE FROM moz_historyvisit WHERE page_id = ?1"), - getter_AddRefs(visitDelete)); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRUint32 i = 0; i < duplicates.Length(); ++i) { - PRInt64 id = duplicates[i]; - { - mozStorageStatementScoper scope(statement); - rv = statement->BindInt64Parameter(0, id); - NS_ENSURE_SUCCESS(rv, rv); - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - } - { - mozStorageStatementScoper scope(visitDelete); - rv = visitDelete->BindInt64Parameter(0, id); - NS_ENSURE_SUCCESS(rv, rv); - rv = visitDelete->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - } - } - return NS_OK; -} - -// Local function ************************************************************** - - -// GetReversedHostname -// -// This extracts the hostname from the URI and reverses it in the -// form that we use (always ending with a "."). So -// "http://microsoft.com/" becomes "moc.tfosorcim." -// -// The idea behind this is that we can create an index over the items in -// the reversed host name column, and then query for as much or as little -// of the host name as we feel like. -// -// For example, the query "host >= 'gro.allizom.' AND host < 'gro.allizom/' -// Matches all host names ending in '.mozilla.org', including -// 'developer.mozilla.org' and just 'mozilla.org' (since we define all -// reversed host names to end in a period, even 'mozilla.org' matches). -// The important thing is that this operation uses the index. Any substring -// calls in a select statement (even if it's for the beginning of a string) -// will bypass any indices and will be slow). - -nsresult -GetReversedHostname(nsIURI* aURI, nsAString& aRevHost) -{ - nsCString forward8; - nsresult rv = aURI->GetHost(forward8); - if (NS_FAILED(rv)) { - return rv; - } - - // can't do reversing in UTF8, better use 16-bit chars - nsAutoString forward = NS_ConvertUTF8toUTF16(forward8); - GetReversedHostname(forward, aRevHost); - return NS_OK; -} - - -// GetReversedHostname -// -// Same as previous but for strings - -void -GetReversedHostname(const nsString& aForward, nsAString& aRevHost) -{ - ReverseString(aForward, aRevHost); - aRevHost.Append(PRUnichar('.')); -} - - -// IsNumericHostName -// -// For host-based groupings, numeric IPs should not be collapsed by the -// last part of the domain name, but should stand alone. This code determines -// if this is the case so we don't collapse "10.1.2.3" to "3". It would be -// nice to use the URL parsing code, but it doesn't give us this information, -// this is usually done by the OS in response to DNS lookups. -// -// This implementation is not perfect, we just check for all numbers and -// digits, and three periods. You could come up with crazy internal host -// names that would fool this logic, but I bet there are no real examples. - -PRBool IsNumericHostName(const nsCString& aHost) -{ - PRInt32 periodCount = 0; - for (PRUint32 i = 0; i < aHost.Length(); i ++) { - PRUnichar cur = aHost[i]; - if (cur == '.') - periodCount ++; - else if (cur < '0' || cur > '9') - return PR_FALSE; - } - return (periodCount == 3); -} - - -// GetSimpleBookmarksQueryFolder -// -// Determines if this set of queries is a simple bookmarks query for a -// folder with no other constraints. In these common cases, we can more -// efficiently compute the results. -// -// Returns the folder ID if it is a simple folder query, 0 if not. - -static PRInt64 -GetSimpleBookmarksQueryFolder(const nsCOMArray& aQueries) -{ - if (aQueries.Count() != 1) - return 0; - - nsNavHistoryQuery* query = aQueries[0]; - if (query->Folders().Length() != 1) - return 0; - - PRBool hasIt; - query->GetHasBeginTime(&hasIt); - if (hasIt) - return 0; - query->GetHasEndTime(&hasIt); - if (hasIt) - return 0; - query->GetHasDomain(&hasIt); - if (hasIt) - return 0; - query->GetHasUri(&hasIt); - if (hasIt) - return 0; - - // Note that we don't care about the onlyBookmarked flag, if you specify a bookmark - // folder, onlyBookmarked is inferred. - - return query->Folders()[0]; -} - - -// ParseSearchQuery -// -// This just breaks the query up into words. We don't do anything fancy, -// not even quoting. We do, however, strip quotes, because people might -// try to input quotes expecting them to do something and get no results -// back. - -inline PRBool isQueryWhitespace(PRUnichar ch) -{ - return ch == ' '; -} - -void ParseSearchQuery(const nsString& aQuery, nsStringArray* aTerms) -{ - PRInt32 lastBegin = -1; - for (PRUint32 i = 0; i < aQuery.Length(); i ++) { - if (isQueryWhitespace(aQuery[i]) || aQuery[i] == '"') { - if (lastBegin >= 0) { - // found the end of a word - aTerms->AppendString(Substring(aQuery, lastBegin, i - lastBegin)); - lastBegin = -1; - } - } else { - if (lastBegin < 0) { - // found the beginning of a word - lastBegin = i; - } - } - } - // last word - if (lastBegin >= 0) - aTerms->AppendString(Substring(aQuery, lastBegin)); -} - - -// GenerateTitleFromURI -// -// Given a URL, we try to get a reasonable title for this page. We try -// to use a filename out of the URI, then fall back on the path, then fall -// back on the whole hostname. - -nsresult // static -GenerateTitleFromURI(nsIURI* aURI, nsAString& aTitle) -{ - nsCAutoString name; - nsCOMPtr url(do_QueryInterface(aURI)); - if (url) - url->GetFileName(name); - if (name.IsEmpty()) { - // path - nsresult rv = aURI->GetPath(name); - if (NS_FAILED(rv) || (name.Length() == 1 && name[0] == '/')) { - // empty path name, use hostname - rv = aURI->GetHost(name); - NS_ENSURE_SUCCESS(rv, rv); - } - } - aTitle = NS_ConvertUTF8toUTF16(name); - return NS_OK; -} - - -// GetTLDCharCount -// -// Given a normal, forward host name ("bugzilla.mozilla.org") -// returns the number of 8-bit characters that the TLD occupies, NOT -// including the trailing dot: bugzilla.mozilla.org -> 3 -// theregister.co.uk -> 5 -// mysite.us -> 2 - -PRInt32 -GetTLDCharCount(const nsCString& aHost) -{ - nsCAutoString trailing; - GetSubstringFromNthDot(aHost, aHost.Length() - 1, 1, - PR_FALSE, trailing); - - switch (GetTLDType(trailing)) { - case 0: - // not a known TLD - return 0; - case 1: - // first-level TLD - return trailing.Length(); - case 2: { - // need to check second level and trim it too (if valid) - nsCAutoString trailingMore; - GetSubstringFromNthDot(aHost, aHost.Length() - 1, - 2, PR_FALSE, trailingMore); - if (GetTLDType(trailingMore)) - return trailingMore.Length(); - else - return trailing.Length(); - } - default: - NS_NOTREACHED("Invalid TLD type"); - return 0; - } -} - - -// GetTLDType -// -// Given the last part of a host name, tells you whether this is a known TLD. -// 0 -> not known -// 1 -> known 1st or second level TLD ("com", "co.uk") -// 2 -> end of a two-part TLD ("uk") -// -// If this returns 2, you should probably re-call the function including -// the next level of name. For example ("uk" -> 2, then you call with -// "co.uk" and know that the last two pars of this domain name are -// "toplevel". -// -// This should be moved somewhere else (like cookies) and made easier to -// update. - -PRInt32 -GetTLDType(const nsCString& aHostTail) -{ - //static nsDataHashtable tldTypes; - if (! gTldTypes) { - // need to populate table - gTldTypes = new nsDataHashtable(); - if (! gTldTypes) - return 0; - - gTldTypes->Init(256); - - gTldTypes->Put(NS_LITERAL_CSTRING("com"), 1); - gTldTypes->Put(NS_LITERAL_CSTRING("org"), 1); - gTldTypes->Put(NS_LITERAL_CSTRING("net"), 1); - gTldTypes->Put(NS_LITERAL_CSTRING("edu"), 1); - gTldTypes->Put(NS_LITERAL_CSTRING("gov"), 1); - gTldTypes->Put(NS_LITERAL_CSTRING("mil"), 1); - gTldTypes->Put(NS_LITERAL_CSTRING("uk"), 2); - gTldTypes->Put(NS_LITERAL_CSTRING("co.uk"), 1); - gTldTypes->Put(NS_LITERAL_CSTRING("kr"), 2); - gTldTypes->Put(NS_LITERAL_CSTRING("co.kr"), 1); - gTldTypes->Put(NS_LITERAL_CSTRING("hu"), 1); - gTldTypes->Put(NS_LITERAL_CSTRING("us"), 1); - - // FIXME: add the rest - } - - PRInt32 type = 0; - if (gTldTypes->Get(aHostTail, &type)) - return type; - else - return 0; -} - - -// GetSubstringFromNthDot -// -// Similar to GetSubstringToNthDot except searches backward -// GetSubstringFromNthDot("foo.bar", length, 1, PR_FALSE) -> "bar" -// -// It is legal to pass in a starting position < 0 so you can just -// use Length()-1 as the starting position even if the length is 0. - -void GetSubstringFromNthDot(const nsCString& aInput, PRInt32 aStartingSpot, - PRInt32 aN, PRBool aIncludeDot, nsACString& aSubstr) -{ - PRInt32 dotsFound = 0; - for (PRInt32 i = aStartingSpot; i >= 0; i --) { - if (aInput[i] == '.') { - dotsFound ++; - if (dotsFound == aN) { - if (aIncludeDot) - aSubstr = Substring(aInput, i, aInput.Length() - i); - else - aSubstr = Substring(aInput, i + 1, aInput.Length() - i - 1); - return; - } - } - } - aSubstr = aInput; // no dot found -} - - -// BindStatementURI -// -// Binds the specified URI as the parameter 'index' for the statment. -// URIs are always bound as UTF8 - -nsresult BindStatementURI(mozIStorageStatement* statement, PRInt32 index, - nsIURI* aURI) -{ - nsCAutoString utf8URISpec; - nsresult rv = aURI->GetSpec(utf8URISpec); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->BindUTF8StringParameter(index, - StringHead(utf8URISpec, HISTORY_URI_LENGTH_MAX)); - NS_ENSURE_SUCCESS(rv, rv); - return NS_OK; -} diff --git a/mozilla/browser/components/places/src/nsNavHistory.h b/mozilla/browser/components/places/src/nsNavHistory.h deleted file mode 100644 index 9e23202a3ac..00000000000 --- a/mozilla/browser/components/places/src/nsNavHistory.h +++ /dev/null @@ -1,610 +0,0 @@ -/* -*- 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 code. - * - * 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): - * Brett Wilson (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 ***** */ - -#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" -#include "nsDataHashtable.h" -#include "nsIAtom.h" -#include "nsINavHistoryService.h" -#include "nsIAutoCompleteSearch.h" -#include "nsIAutoCompleteResult.h" -#include "nsIAutoCompleteSimpleResult.h" -#include "nsIBrowserHistory.h" -#include "nsICollation.h" -#include "nsIDateTimeFormat.h" -#include "nsIGlobalHistory.h" -#include "nsIGlobalHistory3.h" -#include "nsIPrefService.h" -#include "nsIPrefBranch.h" -#include "nsIObserver.h" -#include "nsIObserverService.h" -#include "nsServiceManagerUtils.h" -#include "nsIStringBundle.h" -#include "nsITimer.h" -#include "nsITreeSelection.h" -#include "nsITreeView.h" -#include "nsString.h" -#include "nsVoidArray.h" -#include "nsWeakReference.h" -#include "nsTArray.h" -#include "nsINavBookmarksService.h" -#include "nsMaybeWeakPtr.h" - -#include "nsNavHistoryExpire.h" -#include "nsNavHistoryResult.h" -#include "nsNavHistoryQuery.h" - -// set to use more optimized (in-memory database) link coloring -//#define IN_MEMORY_LINKS - -// define to enable lazy link adding -#define LAZY_ADD - -#define QUERYUPDATE_TIME 0 -#define QUERYUPDATE_SIMPLE 1 -#define QUERYUPDATE_COMPLEX 2 -#define QUERYUPDATE_COMPLEX_WITH_BOOKMARKS 3 - -class AutoCompleteIntermediateResult; -class AutoCompleteResultComparator; -class mozIAnnotationService; -class nsNavHistory; -class nsNavBookmarks; -class QueryKeyValuePair; - -// nsNavHistory - -class nsNavHistory : public nsSupportsWeakReference, - public nsINavHistoryService, - public nsIObserver, - public nsIBrowserHistory, - public nsIGlobalHistory3, - public nsIAutoCompleteSearch -{ - friend class AutoCompleteIntermediateResultSet; - friend class AutoCompleteResultComparator; -public: - nsNavHistory(); - - NS_DECL_ISUPPORTS - - NS_DECL_NSINAVHISTORYSERVICE - NS_DECL_NSIGLOBALHISTORY2 - NS_DECL_NSIGLOBALHISTORY3 - NS_DECL_NSIBROWSERHISTORY - NS_DECL_NSIOBSERVER - NS_DECL_NSIAUTOCOMPLETESEARCH - - nsresult Init(); - - /** - * Used by other components in the places directory such as the annotation - * service to get a reference to this history object. Returns a pointer to - * the service if it exists. Otherwise creates one. Returns NULL on error. - */ - static nsNavHistory* GetHistoryService() - { - if (! gHistoryService) { - nsresult rv; - nsCOMPtr serv(do_GetService("@mozilla.org/browser/nav-history-service;1", &rv)); - NS_ENSURE_SUCCESS(rv, nsnull); - - // our constructor should have set the static variable. If it didn't, - // something is wrong. - NS_ASSERTION(gHistoryService, "History service creation failed"); - } - return gHistoryService; - } - - /** - * Call this function before doing any database reads. It will ensure that - * any data not flushed to the DB yet is flushed. - */ - void SyncDB() - { - #ifdef LAZY_ADD - CommitLazyMessages(); - #endif - } - -#ifdef LAZY_ADD - /** - * Adds a lazy message for adding a favicon. Used by the favicon service so - * that favicons are handled lazily just like page adds. - */ - nsresult AddLazyLoadFaviconMessage(nsIURI* aPage, nsIURI* aFavicon, - PRBool aForceReload); -#endif - - /** - * Returns the database ID for the given URI, or 0 if not found an autoCreate - * is false. - */ - nsresult GetUrlIdFor(nsIURI* aURI, PRInt64* aEntryID, - PRBool aAutoCreate); - - /** - * Returns a pointer to the storage connection used by history. This - * connection object is also used by the annotation service and bookmarks, so - * that things can be grouped into transactions across these components. - * - * NOT ADDREFed. - * - * This connection can only be used in the thread that created it the - * history service! - */ - mozIStorageConnection* GetStorageConnection() - { - return mDBConn; - } - -#ifdef IN_MEMORY_LINKS - mozIStorageConnection* GetMemoryStorageConnection() - { - return mMemDBConn; - } -#endif - - // see comment above StartDummyStatement - nsresult StartDummyStatement(); - nsresult StopDummyStatement(); - - /** - * These functions return non-owning references to the locale-specific - * objects for places components. Guaranteed to return non-NULL. - */ - nsIStringBundle* GetBundle() - { return mBundle; } - nsILocale* GetLocale() - { return mLocale; } - nsICollation* GetCollation() - { return mCollation; } - nsIDateTimeFormat* GetDateFormatter() - { return mDateFormatter; } - - // returns true if history has been disabled - PRBool IsHistoryDisabled() { return mExpireDays == 0; } - - // remember tree state - void SaveExpandItem(const nsAString& aTitle); - void SaveCollapseItem(const nsAString& aTitle); - - // get the statement for selecting a history row by URL - 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_UserTitle; - static const PRInt32 kGetInfoIndex_RevHost; - static const PRInt32 kGetInfoIndex_VisitCount; - - // select a history row by URL, with visit date info (extra work) - mozIStorageStatement* DBGetURLPageInfoFull() - { return mDBGetURLPageInfoFull; } - - // select a history row by id - mozIStorageStatement* DBGetIdPageInfo() { return mDBGetIdPageInfo; } - - // select a history row by id, with visit date info (extra work) - mozIStorageStatement* DBGetIdPageInfoFull() - { return mDBGetIdPageInfoFull; } - - // Constants for the columns returned by the above statement - // (in addition to the ones above). - static const PRInt32 kGetInfoIndex_VisitDate; - static const PRInt32 kGetInfoIndex_FaviconURL; - - // used in execute queries to get session ID info (only for visits) - static const PRInt32 kGetInfoIndex_SessionId; - - static nsIAtom* sMenuRootAtom; - static nsIAtom* sToolbarRootAtom; - static nsIAtom* sSessionStartAtom; - static nsIAtom* sSessionContinueAtom; - static nsIAtom* sContainerAtom; - - // this actually executes a query and gives you results, it is used by - // nsNavHistoryQueryResultNode - nsresult GetQueryResults(const nsCOMArray& aQueries, - nsNavHistoryQueryOptions *aOptions, - nsCOMArray* aResults); - - // Take a row of kGetInfoIndex_* columns and construct a ResultNode. - // The row must contain the full set of columns. - nsresult RowToResult(mozIStorageValueArray* aRow, - nsNavHistoryQueryOptions* aOptions, - nsNavHistoryResultNode** aResult); - nsresult QueryRowToResult(const nsACString& aURI, const nsACString& aTitle, - PRUint32 aAccessCount, PRTime aTime, - const nsACString& aFavicon, - nsNavHistoryResultNode** aNode); - - nsresult VisitIdToResultNode(PRInt64 visitId, - nsNavHistoryQueryOptions* aOptions, - nsNavHistoryResultNode** aResult); - nsresult UriToResultNode(nsIURI* aUri, - nsNavHistoryQueryOptions* aOptions, - nsNavHistoryResultNode** aResult); - - // used by other places components to send history notifications (for example, - // when the favicon has changed) - void SendPageChangedNotification(nsIURI* aURI, PRUint32 aWhat, - const nsAString& aValue) - { - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryObserver, - OnPageChanged(aURI, aWhat, aValue)); - } - - // current time optimization - PRTime GetNow(); - - // well-known annotations used by the history and bookmarks systems - static const char kAnnotationPreviousEncoding[]; - - // used by query result nodes to update: see comment on body of CanLiveUpdateQuery - static PRUint32 GetUpdateRequirements(const nsCOMArray& aQueries, - nsNavHistoryQueryOptions* aOptions, - PRBool* aHasSearchTerms); - PRBool EvaluateQueryForNode(const nsCOMArray& aQueries, - nsNavHistoryQueryOptions* aOptions, - nsNavHistoryResultNode* aNode); - - static nsresult AsciiHostNameFromHostString(const nsACString& aHostName, - nsACString& aAscii); - static void DomainNameFromHostName(const nsCString& aHostName, - nsACString& aDomainName); - static PRTime NormalizeTime(PRUint32 aRelative, PRTime aOffset); - nsresult RecursiveGroup(const nsCOMArray& aSource, - const PRUint32* aGroupingMode, PRUint32 aGroupCount, - nsCOMArray* aDest); - - // better alternative to QueryStringToQueries (in nsNavHistoryQuery.cpp) - nsresult QueryStringToQueryArray(const nsACString& aQueryString, - nsCOMArray* aQueries, - nsNavHistoryQueryOptions** aOptions); - - // Import-friendly version of SetPageDetails + AddVisit. - // This method adds a page to history along with a single last visit. - // It is an error to call this method if aURI might already be in history. - // The given aVisitCount should include the given last-visit date. - // aLastVisitDate can be -1 if there is no last visit date to record. - nsresult AddPageWithVisit(nsIURI *aURI, - const nsString &aTitle, - const nsString &aUserTitle, - PRBool aHidden, PRBool aTyped, - PRInt32 aVisitCount, - PRInt32 aLastVisitTransition, - PRTime aLastVisitDate); - - // Checks the database for any duplicate URLs. If any are found, - // all but the first are removed. This must be called after using - // AddPageWithVisit, to ensure that the database is in a consistent state. - nsresult RemoveDuplicateURIs(); - - private: - ~nsNavHistory(); - - // used by GetHistoryService - static nsNavHistory* gHistoryService; - -protected: - - // - // Constants - // - nsCOMPtr mPrefBranch; // MAY BE NULL when we are shutting down - nsDataHashtable gExpandedItems; - - // - // Database stuff - // - nsCOMPtr mDBService; - nsCOMPtr mDBConn; - - nsCOMPtr mDBGetURLPageInfo; // kGetInfoIndex_* results - nsCOMPtr mDBGetURLPageInfoFull; // kGetInfoIndex_* results - nsCOMPtr mDBGetIdPageInfo; // kGetInfoIndex_* results - nsCOMPtr mDBGetIdPageInfoFull; // kGetInfoIndex_* results - nsCOMPtr mDBFullAutoComplete; // kAutoCompleteIndex_* results, 1 arg (max # results) - static const PRInt32 kAutoCompleteIndex_URL; - static const PRInt32 kAutoCompleteIndex_Title; - static const PRInt32 kAutoCompleteIndex_UserTitle; - static const PRInt32 kAutoCompleteIndex_VisitCount; - static const PRInt32 kAutoCompleteIndex_Typed; - - nsCOMPtr mDBRecentVisitOfURL; // converts URL into most recent visit ID/session ID - nsCOMPtr mDBInsertVisit; // used by AddVisit - nsCOMPtr mDBGetPageVisitStats; // used by AddVisit - nsCOMPtr mDBUpdatePageVisitStats; // used by AddVisit - nsCOMPtr mDBAddNewPage; // used by InternalAddNewPage - - // these are used by VisitIdToResultNode for making new result nodes from IDs - nsCOMPtr mDBVisitToURLResult; // kGetInfoIndex_* results - nsCOMPtr mDBVisitToVisitResult; // kGetInfoIndex_* results - nsCOMPtr mDBUrlToUrlResult; // kGetInfoIndex_* results - - nsresult InitDB(PRBool *aDoImport); - nsresult InitStatements(); - -#ifdef IN_MEMORY_LINKS - // this is the cache DB in memory used for storing visited URLs - nsCOMPtr mMemDBConn; - nsCOMPtr mMemDBAddPage; - nsCOMPtr mMemDBGetPage; - - nsresult InitMemDB(); -#endif - - // this statement is kept open to persist the cache, see InitDB - nsCOMPtr mDummyDBConn; - nsCOMPtr mDBDummyStatement; - - nsresult AddURIInternal(nsIURI* aURI, PRTime aTime, PRBool aRedirect, - PRBool aToplevel, nsIURI* aReferrer); - - nsresult AddVisitChain(nsIURI* aURI, PRTime aTime, - PRBool aToplevel, PRBool aRedirect, - nsIURI* aReferrer, PRInt64* aVisitID, - PRInt64* aSessionID, PRInt64* aRedirectBookmark); - nsresult InternalAddNewPage(nsIURI* aURI, const nsAString& aTitle, - PRBool aHidden, PRBool aTyped, - PRInt32 aVisitCount, PRInt64* aPageID); - nsresult InternalAddVisit(PRInt64 aPageID, PRInt64 aReferringVisit, - PRInt64 aSessionID, PRTime aTime, - PRInt32 aTransitionType, PRInt64* aVisitID); - PRBool FindLastVisit(nsIURI* aURI, PRInt64* aVisitID, - PRInt64* aSessionID); - PRBool IsURIStringVisited(const nsACString& url); - nsresult LoadPrefs(); - - // Current time optimization - PRTime mLastNow; - PRBool mNowValid; - nsCOMPtr mExpireNowTimer; - static void expireNowTimerCallback(nsITimer* aTimer, void* aClosure); - - // expiration - friend class nsNavHistoryExpire; - nsNavHistoryExpire mExpire; - -#ifdef LAZY_ADD - // lazy add committing - struct LazyMessage { - enum MessageType { Type_Invalid, Type_AddURI, Type_Title, Type_Favicon }; - LazyMessage() - { - type = Type_Invalid; - isRedirect = PR_FALSE; - isToplevel = PR_FALSE; - time = 0; - alwaysLoadFavicon = PR_FALSE; - } - - // call this with common parms to initialize. Caller is responsible for - // setting other elements manually depending on type. - nsresult Init(MessageType aType, nsIURI* aURI) - { - type = aType; - nsresult rv = aURI->Clone(getter_AddRefs(uri)); - NS_ENSURE_SUCCESS(rv, rv); - return uri->GetSpec(uriSpec); - } - - // common elements - MessageType type; - nsCOMPtr uri; - nsCString uriSpec; // stringified version of URI, for quick isVisited - - // valid when type == Type_AddURI - nsCOMPtr referrer; - PRBool isRedirect; - PRBool isToplevel; - PRTime time; - - // valid when type == Type_Title - nsString title; - - // valid when type == LAZY_FAVICON - nsCOMPtr favicon; - PRBool alwaysLoadFavicon; - }; - nsTArray mLazyMessages; - nsCOMPtr mLazyTimer; - PRBool mLazyTimerSet; - PRUint32 mLazyTimerDeferments; // see StartLazyTimer - nsresult StartLazyTimer(); - nsresult AddLazyMessage(const LazyMessage& aMessage); - static void LazyTimerCallback(nsITimer* aTimer, void* aClosure); - void CommitLazyMessages(); -#endif - - nsresult QueryToSelectClause(nsNavHistoryQuery* aQuery, - PRInt32 aStartParameter, - nsCString* aClause, - PRInt32* aParamCount, - const nsACString& aCommonConditions); - nsresult BindQueryClauseParameters(mozIStorageStatement* statement, - PRInt32 aStartParameter, - nsNavHistoryQuery* aQuery, - PRInt32* aParamCount); - - nsresult ResultsAsList(mozIStorageStatement* statement, - nsNavHistoryQueryOptions* aOptions, - nsCOMArray* aResults); - - void TitleForDomain(const nsCString& domain, nsACString& aTitle); - nsresult SetPageTitleInternal(nsIURI* aURI, PRBool aIsUserTitle, - const nsAString& aTitle); - - nsresult GroupByHost(const nsCOMArray& aSource, - nsCOMArray* aDest, - PRBool aIsDomain); - - nsresult FilterResultSet(const nsCOMArray& aSet, - nsCOMArray* aFiltered, - const nsString& aSearch); - - // observers - nsMaybeWeakPtrArray mObservers; - PRInt32 mBatchesInProgress; - - // localization - nsCOMPtr mBundle; - nsCOMPtr mLocale; - nsCOMPtr mCollation; - nsCOMPtr mDateFormatter; - - // annotation service : MAY BE NULL! - //nsCOMPtr mAnnotationService; - - // recent events - typedef nsDataHashtable RecentEventHash; - RecentEventHash mRecentTyped; - RecentEventHash mRecentBookmark; - - PRBool CheckIsRecentEvent(RecentEventHash* hashTable, - const nsACString& url); - void ExpireNonrecentEvents(RecentEventHash* hashTable); - - // redirect tracking. See GetRedirectFor for a description of how this works. - struct RedirectInfo { - nsCString mSourceURI; - PRTime mTimeCreated; - PRUint32 mType; // one of TRANSITION_REDIRECT_[TEMPORARY,PERMANENT] - }; - typedef nsDataHashtable RedirectHash; - RedirectHash mRecentRedirects; - PR_STATIC_CALLBACK(PLDHashOperator) ExpireNonrecentRedirects( - nsCStringHashKey::KeyType aKey, RedirectInfo& aData, void* aUserArg); - PRBool GetRedirectFor(const nsACString& aDestination, nsACString& aSource, - PRTime* aTime, PRUint32* aRedirectType); - - // session tracking - PRInt64 mLastSessionID; - PRInt64 GetNewSessionID() { mLastSessionID ++; return mLastSessionID; } - - // - // AutoComplete stuff - // - struct AutoCompletePrefix - { - AutoCompletePrefix(const nsAString& aPrefix, PRBool aSecondLevel) : - prefix(aPrefix), secondLevel(aSecondLevel) {} - - // The prefix, for example, "http://" or "https://www." - nsString prefix; - - // Set when this prefix contains a spec AND a host. For example, - // "http://www." is a second level prefix, but "http://" is not. This - // flag is used to exclude matches. For example, if I type "http://w" - // I probably want it to autocomplete to sites beginning with w and - // NOT every "www" site I've ever visited. - PRBool secondLevel; - }; - nsTArray mAutoCompletePrefixes; - - nsCOMPtr mDBAutoCompleteQuery; - nsresult InitAutoComplete(); - nsresult CreateAutoCompleteQuery(); - PRInt32 mAutoCompleteMaxCount; - PRInt32 mExpireDays; - PRBool mAutoCompleteOnlyTyped; - - // Used to describe what prefixes shouldn't be cut from - // history urls when doing an autocomplete url comparison. - struct AutoCompleteExclude { - // these are indices into mIgnoreSchemes and mIgnoreHostnames, or -1 - PRInt32 schemePrefix; - PRInt32 hostnamePrefix; - - // this is the offset of the character immediately after the prefix - PRInt32 postPrefixOffset; - }; - - nsresult AutoCompleteTypedSearch(nsIAutoCompleteSimpleResult* result); - nsresult AutoCompleteFullHistorySearch(const nsAString& aSearchString, - nsIAutoCompleteSimpleResult* result); - nsresult AutoCompleteQueryOnePrefix(const nsString& aSearchString, - const nsTArray& aExcludePrefixes, - PRInt32 aPriorityDelta, - nsTArray* aResult); - PRInt32 AutoCompleteGetPrefixLength(const nsString& aSpec); - - // in nsNavHistoryQuery.cpp - nsresult TokensToQueries(const nsTArray& aTokens, - nsCOMArray* aQueries, - nsNavHistoryQueryOptions* aOptions); - - // creates supplemental indexes that we'd like to not bother with - // updating during import. - nsresult CreateLookupIndexes(); -}; - -/** - * Shared between the places components, this function binds the given URI as - * UTF8 to the given parameter for the statement. - */ -nsresult BindStatementURI(mozIStorageStatement* statement, PRInt32 index, - nsIURI* aURI); - -#define PLACES_URI_PREFIX "place:" - -/* Returns true if the given URI represents a history query. */ -inline PRBool IsQueryURI(const nsCString &uri) -{ - return StringBeginsWith(uri, NS_LITERAL_CSTRING(PLACES_URI_PREFIX)); -} - -/* Extracts the query string from a query URI. */ -inline const nsDependentCSubstring QueryURIToQuery(const nsCString &uri) -{ - NS_ASSERTION(IsQueryURI(uri), "should only be called for query URIs"); - return Substring(uri, NS_LITERAL_CSTRING(PLACES_URI_PREFIX).Length()); -} - -#endif // nsNavHistory_h_ diff --git a/mozilla/browser/components/places/src/nsNavHistoryAutoComplete.cpp b/mozilla/browser/components/places/src/nsNavHistoryAutoComplete.cpp deleted file mode 100644 index 9d55eea8919..00000000000 --- a/mozilla/browser/components/places/src/nsNavHistoryAutoComplete.cpp +++ /dev/null @@ -1,711 +0,0 @@ -/* -*- 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 code. - * - * 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): - * Brett Wilson - * Joe Hewitt - * Blake Ross - * - * 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 ***** */ - - -/** - * Autocomplte algorithm: - * - * Scoring - * ------- - * Generally ordering is by visit count. We given boosts to items that have - * been bookmarked or typed into the address bar (as opposed to clicked links). - * The penalties below for schemes and prefix matches also apply. We also - * prefer paths (URLs ending in '/') and try to have shorter ones generally - * appear first. The results are then presented in descending numeric order - * using this score. - * - * Database queries - * ---------------- - * It is tempting to do a select for "url LIKE user_input" but this is a very - * slow query since it is brute-force over all URLs in histroy. We can, - * however, do very efficient queries using < and > for strings. These - * operators use the index over the URL column. - * - * Therefore, we try to prepend any prefixes that should be considered and - * query for them individually. Therefore, we execute several very efficient - * queries, score the results, and sort it. - * - * To limit the amount of searching we do, we ask the database to order the - * results based on visit count for us and give us on the top N results. - * These results will be in approximate order of score. As long as each query - * has more results than the user is likely to see, they will not notice the - * effects of this. - * - * First see if any specs match that from the beginning - * ---------------------------------------------------- - * - If it matches the beginning of a known prefix prefix, exclude that prefix - * when querying. We would therefore exclude "http://" and "https://" if you type - * "ht". But match any other schemes that begin with "ht" (probably none). - * - * - Penalize all results. Most people don't type the scheme and don't want - * matches like this. This will make "file://" links go below - * "http://www.fido.com/". If one is typing the scheme "file:" for example, by - * the time you type the colon it won't match anything else (like "http://file:") - * and the penalty won't have any effect on the ordering (it will be applied to - * all results). - * - * Try different known prefixes - * ---------------------------- - * - Prepend each prefix, running a query. If the concatenated string is itself a - * prefix of another known prefix (ie input is "w" and we prepend "http://", it - * will be a prefix of "http://www."), select out that known prefix. In this - * case we'll query for everything starting with "http://w" except things - * starting with "http://www." - * - * - For each selected out prefix above, run a query but apply prefix match - * penalty to the results. This way you'll still get "http://www." results - * if you type "w", but they will generally be lower than "http://wookie.net/" - * For your favorite few sites with many visits, you might still get matches - * for "www" first, which is probably what you want for your favorite sites. - */ - -#include "nsNavHistory.h" -#include "nsNetUtil.h" - -#include "mozIStorageService.h" -#include "mozIStorageConnection.h" -#include "mozIStorageValueArray.h" -#include "mozIStorageStatement.h" -#include "mozIStorageFunction.h" -#include "mozStorageCID.h" -#include "mozStorageHelper.h" - -#define NS_AUTOCOMPLETESIMPLERESULT_CONTRACTID \ - "@mozilla.org/autocomplete/simple-result;1" - -// Size of visit count boost to give to URLs which are sites or paths -#define AUTOCOMPLETE_NONPAGE_VISIT_COUNT_BOOST 5 - -// Boost to give to URLs which have been typed -#define AUTOCOMPLETE_TYPED_BOOST 5 - -// Boost to give to URLs which are bookmarked -#define AUTOCOMPLETE_BOOKMARKED_BOOST 5 - -// Penalty to add to sites that match a prefix. For example, if I type "w" -// we will complte on "http://www.w..." like normal. We we will also complete -// on "http://w" which will match almost every site, but will have this penalty -// applied so they will come later. We want a pretty big penalty so that you'll -// only get "www" beating domain names that start with w for your very favorite -// sites. -#define AUTOCOMPLETE_MATCHES_PREFIX_PENALTY (-50) - -// Penalty applied to matches that don't have prefixes applied. See -// discussion above. -#define AUTOCOMPLETE_MATCHES_SCHEME_PENALTY (-20) - -// Number of results we will consider for each prefix. Each prefix lookup is -// done separately. Typically, we will only match one prefix, so this should be -// a sufficient number to give "enough" autocomplete matches per prefix. The -// total number of results that could ever be returned is this times the number -// of prefixes. This should be as small as is reasonable to make it faster. -#define AUTOCOMPLETE_MAX_PER_PREFIX 50 - -// This is the maximum results we'll return for a "typed" search (usually -// happens in response to clicking the down arrow next to the URL). -#define AUTOCOMPLETE_MAX_PER_TYPED 100 - -// This is the maximum number of visits that an item can have for us to -// try to remove the path and put a virtual item with just the hostname as the -// first entry. The virtual item helps the case where you've visited a site deep -// down and want to visit the main site. This limit makes sure we don't take -// the first autocomplete spot for a page you are more likely to go to. -#define AUTOCOMPLETE_MAX_TRUNCATION_VISIT 6 - -PRInt32 ComputeAutoCompletePriority(const nsAString& aUrl, PRInt32 aVisitCount, - PRBool aWasTyped, PRBool aIsBookmarked); -nsresult NormalizeAutocompleteInput(const nsAString& aInput, - nsString& aOutput); - -// nsIAutoCompleteSearch ******************************************************* - - -// AutoCompleteIntermediateResult/Set -// -// This class holds intermediate autocomplete results so that they can be -// sorted. This list is then handed off to a result using FillResult. The -// major reason for this is so that we can use nsArray's sorting functions, -// not use COM, yet have well-defined lifetimes for the objects. This uses -// a void array, but makes sure to delete the objects on desctruction. - -struct AutoCompleteIntermediateResult -{ - AutoCompleteIntermediateResult(const nsString& aUrl, const nsString& aTitle, - PRInt32 aVisitCount, PRInt32 aPriority) : - url(aUrl), title(aTitle), visitCount(aVisitCount), priority(aPriority) {} - nsString url; - nsString title; - PRInt32 visitCount; - PRInt32 priority; -}; - - -// AutoCompleteResultComparator - -class AutoCompleteResultComparator -{ -public: - AutoCompleteResultComparator(nsNavHistory* history) : mHistory(history) {} - - PRBool Equals(const AutoCompleteIntermediateResult& a, - const AutoCompleteIntermediateResult& b) const { - // Don't need an equals, this call will be optimized out when it - // is used by nsQuickSortComparator above - return PR_FALSE; - } - PRBool LessThan(const AutoCompleteIntermediateResult& match1, - const AutoCompleteIntermediateResult& match2) const { - // we actually compare GREATER than here, since we want the array to be in - // most relevant (highest priority value) first - - // In most cases the priorities will be different and we just use them - if (match1.priority != match2.priority) - { - return match1.priority > match2.priority; - } - else - { - // secondary sorting gives priority to site names and paths (ending in a /) - PRBool isPath1 = PR_FALSE, isPath2 = PR_FALSE; - if (!match1.url.IsEmpty()) - isPath1 = (match1.url.Last() == PRUnichar('/')); - if (!match2.url.IsEmpty()) - isPath2 = (match2.url.Last() == PRUnichar('/')); - - if (isPath1 && !isPath2) return PR_FALSE; // match1->url is a website/path, match2->url isn't - if (!isPath1 && isPath2) return PR_TRUE; // match1->url isn't a website/path, match2->url is - - // find the prefixes so we can sort by the stuff after the prefixes - PRInt32 prefix1 = mHistory->AutoCompleteGetPrefixLength(match1.url); - PRInt32 prefix2 = mHistory->AutoCompleteGetPrefixLength(match2.url); - - // Compare non-prefixed urls using the current locale string compare. This will sort - // things alphabetically (ignoring common prefixes). For example, "http://www.abc.com/" - // will come before "ftp://ftp.xyz.com" - PRInt32 ret = 0; - mHistory->mCollation->CompareString( - nsICollation::kCollationCaseInSensitive, - Substring(match1.url, prefix1), Substring(match2.url, prefix2), - &ret); - if (ret != 0) - return ret > 0; - - // sort http://xyz.com before http://www.xyz.com - return prefix1 > prefix2; - } - return PR_FALSE; - } -protected: - nsNavHistory* mHistory; -}; - - -// nsNavHistory::InitAutoComplete - -nsresult -nsNavHistory::InitAutoComplete() -{ - nsresult rv = CreateAutoCompleteQuery(); - NS_ENSURE_SUCCESS(rv, rv); - - AutoCompletePrefix* ok; - - // These are the prefixes we check for implicitly. Prefixes with a - // host portion (like "www.") get their second level flag set. - ok = mAutoCompletePrefixes.AppendElement(AutoCompletePrefix(NS_LITERAL_STRING("http://"), PR_FALSE)); - NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); - ok = mAutoCompletePrefixes.AppendElement(AutoCompletePrefix(NS_LITERAL_STRING("http://www."), PR_TRUE)); - NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); - ok = mAutoCompletePrefixes.AppendElement(AutoCompletePrefix(NS_LITERAL_STRING("ftp://"), PR_FALSE)); - NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); - ok = mAutoCompletePrefixes.AppendElement(AutoCompletePrefix(NS_LITERAL_STRING("ftp://ftp."), PR_TRUE)); - NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); - ok = mAutoCompletePrefixes.AppendElement(AutoCompletePrefix(NS_LITERAL_STRING("https://"), PR_FALSE)); - NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); - ok = mAutoCompletePrefixes.AppendElement(AutoCompletePrefix(NS_LITERAL_STRING("https://www."), PR_TRUE)); - NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); - - return NS_OK; -} - - -// nsNavHistory::CreateAutoCompleteQuery -// -// The auto complete query we use depends on options, so we have it in -// a separate function so it can be re-created when the option changes. - -nsresult -nsNavHistory::CreateAutoCompleteQuery() -{ - nsCString sql; - if (mAutoCompleteOnlyTyped) { - sql = NS_LITERAL_CSTRING( - "SELECT url, title, visit_count, typed, " - "(SELECT item_child FROM moz_bookmarks WHERE item_child = id) " - "FROM moz_history " - "WHERE url >= ?1 AND url < ?2 " - "AND typed = 1 " - "ORDER BY visit_count DESC " - "LIMIT "); - } else { - sql = NS_LITERAL_CSTRING( - "SELECT url, title, visit_count, typed, " - "(SELECT item_child FROM moz_bookmarks WHERE item_child = id) " - "FROM moz_history " - "WHERE url >= ?1 AND url < ?2 " - "AND (hidden <> 1 OR typed = 1) " - "ORDER BY visit_count DESC " - "LIMIT "); - } - sql.AppendInt(AUTOCOMPLETE_MAX_PER_PREFIX); - nsresult rv = mDBConn->CreateStatement(sql, - getter_AddRefs(mDBAutoCompleteQuery)); - return rv; -} - - -// nsNavHistory::StartSearch -// - -NS_IMETHODIMP -nsNavHistory::StartSearch(const nsAString & aSearchString, - const nsAString & aSearchParam, - nsIAutoCompleteResult *aPreviousResult, - nsIAutoCompleteObserver *aListener) -{ - nsresult rv; - - NS_ENSURE_ARG_POINTER(aListener); - - nsCOMPtr result = - do_CreateInstance(NS_AUTOCOMPLETESIMPLERESULT_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - result->SetSearchString(aSearchString); - - // Performance: We can improve performance for refinements of a previous - // result by filtering the old result with the new string. However, since - // our results are not a full match of history, we'll need to requery if - // any of the subresults returned the maximum number of elements (i.e. we - // didn't load all of them). - // - // Timing measurements show that the performance of this is actually very - // good for specific queries. Thus, the times where we can do the - // optimization (when there are few results) are exactly the times when - // we don't have to. As a result, we keep it this much simpler way. - if (aSearchString.IsEmpty()) { - rv = AutoCompleteTypedSearch(result); - } else { - rv = AutoCompleteFullHistorySearch(aSearchString, result); - } - NS_ENSURE_SUCCESS(rv, rv); - - // Determine the result of the search - PRUint32 count; - result->GetMatchCount(&count); - if (count > 0) { - result->SetSearchResult(nsIAutoCompleteResult::RESULT_SUCCESS); - result->SetDefaultIndex(0); - } else { - result->SetSearchResult(nsIAutoCompleteResult::RESULT_NOMATCH); - result->SetDefaultIndex(-1); - } - - aListener->OnSearchResult(this, result); - return NS_OK; -} - - -// nsNavHistory::StopSearch - -NS_IMETHODIMP -nsNavHistory::StopSearch() -{ - return NS_OK; -} - - -// nsNavHistory::AutoCompleteTypedSearch -// -// Called when there is no search string. This happens when you press -// down arrow from the URL bar: the most recent things you typed are listed. -// -// Ordering here is simpler because there are no boosts for typing, and there -// is no URL information to use. The ordering just comes out of the DB by -// visit count (primary) and time since last visited (secondary). - -nsresult nsNavHistory::AutoCompleteTypedSearch( - nsIAutoCompleteSimpleResult* result) -{ - // need to get more than the required minimum number since some will be dupes - nsCOMPtr dbSelectStatement; - nsCString sql = NS_LITERAL_CSTRING( - "SELECT url, title " - "FROM moz_historyvisit v JOIN moz_history h ON v.page_id = h.id " - "WHERE h.typed = 1 ORDER BY visit_date DESC LIMIT "); - sql.AppendInt(AUTOCOMPLETE_MAX_PER_TYPED * 3); - nsresult rv = mDBConn->CreateStatement(sql, getter_AddRefs(dbSelectStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - // prevent duplicates - nsDataHashtable urls; - if (! urls.Init(500)) - return NS_ERROR_OUT_OF_MEMORY; - - PRInt32 dummy; - PRInt32 count = 0; - PRBool hasMore = PR_FALSE; - while (count < AUTOCOMPLETE_MAX_PER_TYPED && - NS_SUCCEEDED(dbSelectStatement->ExecuteStep(&hasMore)) && hasMore) { - nsAutoString entryURL, entryTitle; - dbSelectStatement->GetString(0, entryURL); - dbSelectStatement->GetString(1, entryTitle); - - if (! urls.Get(entryURL, &dummy)) { - // new item - rv = result->AppendMatch(entryURL, entryTitle); - NS_ENSURE_SUCCESS(rv, rv); - - urls.Put(entryURL, 1); - count ++; - } - } - - return NS_OK; -} - - -// nsNavHistory::AutoCompleteFullHistorySearch -// -// A brute-force search of the entire history. This matches the given input -// with every possible history entry, and sorts them by likelihood. -// -// This may be slow for people on slow computers with large histories. - -nsresult -nsNavHistory::AutoCompleteFullHistorySearch(const nsAString& aSearchString, - nsIAutoCompleteSimpleResult* aResult) -{ - nsString searchString; - nsresult rv = NormalizeAutocompleteInput(aSearchString, searchString); - if (NS_FAILED(rv)) - return NS_OK; // no matches for invalid input - - nsTArray matches; - - // Try a query using this search string and every prefix. Keep track of - // known prefixes that the input matches for exclusion later - PRUint32 i; - const nsTArray emptyArray; - nsTArray firstLevelMatches; - nsTArray secondLevelMatches; - for (i = 0; i < mAutoCompletePrefixes.Length(); i ++) { - if (StringBeginsWith(mAutoCompletePrefixes[i].prefix, searchString)) { - if (mAutoCompletePrefixes[i].secondLevel) - secondLevelMatches.AppendElement(i); - else - firstLevelMatches.AppendElement(i); - } - - // current string to search for - nsString cur = mAutoCompletePrefixes[i].prefix + searchString; - - // see if the concatenated string itself matches any prefixes - nsTArray curPrefixMatches; - for (PRUint32 prefix = 0; prefix < mAutoCompletePrefixes.Length(); prefix ++) { - if (StringBeginsWith(mAutoCompletePrefixes[prefix].prefix, cur)) - curPrefixMatches.AppendElement(prefix); - } - - // search for the current string, excluding those matching prefixes - AutoCompleteQueryOnePrefix(cur, curPrefixMatches, 0, &matches); - - // search for each of those matching prefixes, applying the prefix penalty - for (PRUint32 match = 0; match < curPrefixMatches.Length(); match ++) { - AutoCompleteQueryOnePrefix(mAutoCompletePrefixes[curPrefixMatches[match]].prefix, - emptyArray, AUTOCOMPLETE_MATCHES_PREFIX_PENALTY, - &matches); - } - } - - // Now try searching with no prefix - if (firstLevelMatches.Length() > 0) { - // This will get all matches that DON'T match any prefix. For example, if - // the user types "http://w" we will match "http://westinghouse.com" but - // not "http://www.something". - AutoCompleteQueryOnePrefix(searchString, - firstLevelMatches, 0, &matches); - } else if (secondLevelMatches.Length() > 0) { - // if there are no first level matches (i.e. "http://") then we fall back on - // second level matches. Here, we assume that a first level match implies - // a second level match as well, so we only have to check when there are no - // first level matches. - AutoCompleteQueryOnePrefix(searchString, - secondLevelMatches, 0, &matches); - - // now we try to fill in matches of the prefix. For example, if you type - // "http://w" we will still match against "http://www." but with a penalty. - // We only do this for second level prefixes. - for (PRUint32 match = 0; match < secondLevelMatches.Length(); match ++) { - AutoCompleteQueryOnePrefix(mAutoCompletePrefixes[secondLevelMatches[match]].prefix, - emptyArray, AUTOCOMPLETE_MATCHES_SCHEME_PENALTY, - &matches); - } - } else { - // Input matched no prefix, try to query for all URLs beinning with this - // exact input. - AutoCompleteQueryOnePrefix(searchString, emptyArray, - AUTOCOMPLETE_MATCHES_SCHEME_PENALTY, &matches); - } - - // sort according to priorities - AutoCompleteResultComparator comparator(this); - matches.Sort(comparator); - - // fill into result - nsAutoString zerothEntry; - if (matches.Length() > 0 && - matches[0].visitCount <= AUTOCOMPLETE_MAX_TRUNCATION_VISIT) { - // Here, we try to make sure that the first match is always a host name - // we take the previous first match and extract its host name and add it - // before. If the first item has been visited a lot, don't do that because - // they probably want to go there instead - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), NS_ConvertUTF16toUTF8(matches[0].url)); - NS_ENSURE_SUCCESS(rv, rv); - uri->SetPath(NS_LITERAL_CSTRING("/")); - - nsCAutoString spec; - uri->GetSpec(spec); - zerothEntry = NS_ConvertUTF8toUTF16(spec); - - if (! zerothEntry.Equals(matches[0].url)) - aResult->AppendMatch(zerothEntry, EmptyString()); - rv = aResult->AppendMatch(matches[0].url, matches[0].title); - NS_ENSURE_SUCCESS(rv, rv); - } else if (matches.Length() > 0) { - // otherwise, just append the first match - rv = aResult->AppendMatch(matches[0].url, matches[0].title); - NS_ENSURE_SUCCESS(rv, rv); - } - for (i = 1; i < matches.Length(); i ++) { - // only add ones that are NOT the same as the previous one. It's possible - // to get duplicates from the queries. - if (! matches[i].url.Equals(matches[i-1].url) && - ! zerothEntry.Equals(matches[i].url)) { - rv = aResult->AppendMatch(matches[i].url, matches[i].title); - NS_ENSURE_SUCCESS(rv, rv); - } - } - return NS_OK; -} - - -// nsNavHistory::AutoCompleteQueryOnePrefix -// -// The values in aExcludePrefixes are indices into mAutoCompletePrefixes -// of prefixes to exclude during this query. For example, if I type -// "ht" this function will be called to match everything starting with -// "ht" EXCEPT "http://" and "https://". - -nsresult -nsNavHistory::AutoCompleteQueryOnePrefix(const nsString& aSearchString, - const nsTArray& aExcludePrefixes, - PRInt32 aPriorityDelta, - nsTArray* aResult) -{ - // All URL queries are in UTF-8. Compute the beginning (inclusive) and - // ending (exclusive) of the range of URLs to include when compared - // using strcmp (which is what sqlite does). - nsCAutoString beginQuery = NS_ConvertUTF16toUTF8(aSearchString); - if (beginQuery.IsEmpty()) - return NS_OK; - nsCAutoString endQuery = beginQuery; - unsigned char maxChar[6] = { 0xfd, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf }; - endQuery.Append(NS_REINTERPRET_CAST(const char*, maxChar), 6); - - nsTArray ranges; - if (aExcludePrefixes.Length() > 0) { - // we've been requested to include holes in our range, sort these ranges - ranges.AppendElement(beginQuery); - for (PRUint32 i = 0; i < aExcludePrefixes.Length(); i ++) { - nsCAutoString thisPrefix = NS_ConvertUTF16toUTF8( - mAutoCompletePrefixes[aExcludePrefixes[i]].prefix); - ranges.AppendElement(thisPrefix); - thisPrefix.Append(NS_REINTERPRET_CAST(const char*, maxChar), 6); - ranges.AppendElement(thisPrefix); - } - ranges.AppendElement(endQuery); - ranges.Sort(); - } else { - // simple range with no holes - ranges.AppendElement(beginQuery); - ranges.AppendElement(endQuery); - } - - NS_ASSERTION(ranges.Length() % 2 == 0, "Ranges should be pairs!"); - - // The nested select expands to nonzero when the item is bookmarked. - // It might be nice to also select hidden bookmarks (unvisited) but that - // made this statement more complicated and should be an unusual case. - nsresult rv; - for (PRUint32 range = 0; range < ranges.Length() - 1; range += 2) { - mozStorageStatementScoper scoper(mDBAutoCompleteQuery); - - rv = mDBAutoCompleteQuery->BindUTF8StringParameter(0, ranges[range]); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBAutoCompleteQuery->BindUTF8StringParameter(1, ranges[range + 1]); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMore; - nsAutoString url, title; - while (NS_SUCCEEDED(mDBAutoCompleteQuery->ExecuteStep(&hasMore)) && hasMore) { - mDBAutoCompleteQuery->GetString(0, url); - mDBAutoCompleteQuery->GetString(1, title); - PRInt32 visitCount = mDBAutoCompleteQuery->AsInt32(2); - PRInt32 priority = ComputeAutoCompletePriority(url, visitCount, - mDBAutoCompleteQuery->AsInt32(3) > 0, - mDBAutoCompleteQuery->AsInt32(4) > 0) + aPriorityDelta; - aResult->AppendElement(AutoCompleteIntermediateResult( - url, title, visitCount, priority)); - } - } - return NS_OK; -} - - -// nsNavHistory::AutoCompleteGetPrefixLength - -PRInt32 -nsNavHistory::AutoCompleteGetPrefixLength(const nsString& aSpec) -{ - for (PRUint32 i = 0; i < mAutoCompletePrefixes.Length(); ++i) { - if (StringBeginsWith(aSpec, mAutoCompletePrefixes[i].prefix)) - return mAutoCompletePrefixes[i].prefix.Length(); - } - return 0; // no prefix -} - - -// ComputeAutoCompletePriority -// -// Favor websites and webpaths more than webpages by boosting -// their visit counts. This assumes that URLs have been normalized, -// appending a trailing '/'. -// -// We use addition to boost the visit count rather than multiplication -// since we want URLs with large visit counts to remain pretty much -// in raw visit count order - we assume the user has visited these urls -// often for a reason and there shouldn't be a problem with putting them -// high in the autocomplete list regardless of whether they are sites/ -// paths or pages. However for URLs visited only a few times, sites -// & paths should be presented before pages since they are generally -// more likely to be visited again. - -PRInt32 -ComputeAutoCompletePriority(const nsAString& aUrl, PRInt32 aVisitCount, - PRBool aWasTyped, PRBool aIsBookmarked) -{ - PRInt32 aPriority = aVisitCount; - - if (!aUrl.IsEmpty()) { - // url is a site/path if it has a trailing slash - if (aUrl.Last() == PRUnichar('/')) - aPriority += AUTOCOMPLETE_NONPAGE_VISIT_COUNT_BOOST; - } - - if (aWasTyped) - aPriority += AUTOCOMPLETE_TYPED_BOOST; - if (aIsBookmarked) - aPriority += AUTOCOMPLETE_BOOKMARKED_BOOST; - - return aPriority; -} - - -// NormalizeAutocompleteInput - -nsresult NormalizeAutocompleteInput(const nsAString& aInput, - nsString& aOutput) -{ - nsresult rv; - - if (aInput.IsEmpty()) { - aOutput.Truncate(); - return NS_OK; - } - nsCAutoString input = NS_ConvertUTF16toUTF8(aInput); - - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), input); - PRBool isSchemeAdded = PR_FALSE; - if (NS_FAILED(rv)) { - // it may have failed because there is no scheme, prepend one - isSchemeAdded = PR_TRUE; - input = NS_LITERAL_CSTRING("http://") + input; - - rv = NS_NewURI(getter_AddRefs(uri), input); - if (NS_FAILED(rv)) - return rv; // still not valid, can't autocomplete this URL - } - - nsCAutoString spec; - rv = uri->GetSpec(spec); - NS_ENSURE_SUCCESS(rv, rv); - if (spec.IsEmpty()) - return NS_OK; // should never happen but we assume it's not empty below, so check - - aOutput = NS_ConvertUTF8toUTF16(spec); - - // trim the "http://" scheme if we added it - if (isSchemeAdded) { - NS_ASSERTION(aOutput.Length() > 7, "String impossibly short"); - aOutput = Substring(aOutput, 7); - } - - // it may have appended a slash, get rid of it - // example: input was "http://www.mozil" the URI spec will be - // "http://www.mozil/" which is obviously wrong to complete against. - // However, it won't always append a slash, for example, for the input - // "http://www.mozilla.org/supp" - if (input[input.Length() - 1] != '/' && aOutput[aOutput.Length() - 1] == '/') - aOutput.Truncate(aOutput.Length() - 1); - - return NS_OK; -} diff --git a/mozilla/browser/components/places/src/nsNavHistoryExpire.cpp b/mozilla/browser/components/places/src/nsNavHistoryExpire.cpp deleted file mode 100644 index fdcb0d4402f..00000000000 --- a/mozilla/browser/components/places/src/nsNavHistoryExpire.cpp +++ /dev/null @@ -1,643 +0,0 @@ -//* -*- 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 Mozilla History System - * - * The Initial Developer of the Original Code is - * Google Inc. - * Portions created by the Initial Developer are Copyright (C) 2006 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Brett Wilson (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 ***** */ - -/** - * This class handles expiration of history for nsNavHistory. There is a 1-1 - * mapping between nsNavHistory class and a nsNavHistoryExpire class, the - * code is separated for better understandability. - */ - -#include "nsNavHistory.h" -#include "mozStorageHelper.h" -#include "nsNetUtil.h" - -struct nsNavHistoryExpireRecord { - nsNavHistoryExpireRecord(mozIStorageStatement* statement); - - PRInt64 visitID; - PRInt64 pageID; - PRTime visitDate; - nsCString uri; - PRInt64 faviconID; - PRBool hidden; - PRBool bookmarked; - PRBool erased; // set to true if/when the history entry is erased -}; - -// Number of things we'll expire at once. Runtime of expiration is approximately -// linear with the number of things we expire at once. This number was picked so -// we expire "several" things at once, but still run quickly. Just doing 3 -// expirations at once isn't much faster than 6 due to constant overhead of -// running the query. -#define EXPIRATION_COUNT_PER_RUN 6 - -// The time in ms to wait after AddURI to try expiration of pages. Short is -// actually better. If expiration takes an unusually long period of time, it -// will interfere with video playback in the browser, for example. Such a blip -// is not likely to be noticable when the page has just appeared. -#define PARTIAL_EXPIRATION_TIMEOUT 3500 - -// The time in ms to wait after the initial expiration run for additional ones -#define SUBSEQUENT_EXIPRATION_TIMEOUT 20000 - -// Number of expirations we'll do after the most recent page is loaded before -// stopping. We don't want to keep the computer chugging forever expiring -// annotations if the user stopped using the browser. -// -// This current value of one prevents history expiration while the page is -// being shown, because expiration may interfere with media playback. -#define MAX_SEQUENTIAL_RUNS 1 - - -// nsNavHistoryExpire::nsNavHistoryExpire -// -// Warning: don't do anything with aHistory in the constructor, since -// this is a member of the nsNavHistory, it is still being constructed -// when this is called. - -nsNavHistoryExpire::nsNavHistoryExpire(nsNavHistory* aHistory) : - mHistory(aHistory), - mSequentialRuns(0), - mTimerSet(PR_FALSE), - mAnyEmptyRuns(PR_FALSE), - mNextExpirationTime(0), - mAddCount(0), - mExpiredItems(0), - mExpireRuns(0) -{ - -} - - -// nsNavHistoryExpire::~nsNavHistoryExpire - -nsNavHistoryExpire::~nsNavHistoryExpire() -{ - -} - - -// nsNavHistoryExpire::OnAddURI -// -// Called by history when a URI is added to history. This starts the timer -// for when we are going to expire. -// -// The current time is passed in by the history service as an optimization. -// The AddURI function has already computed the proper time, and getting the -// time again from the OS is nontrivial. - -void -nsNavHistoryExpire::OnAddURI(PRTime aNow) -{ - mAddCount ++; - mSequentialRuns = 0; - - if (mTimer && mTimerSet) { - mTimer->Cancel(); - mTimerSet = PR_FALSE; - } - - if (mNextExpirationTime != 0 && aNow < mNextExpirationTime) - return; // we know there's nothing to expire yet - - StartTimer(PARTIAL_EXPIRATION_TIMEOUT); -} - - -// nsNavHistoryExpire::OnQuit -// -// Here we check for some edge cases and fix them - -void -nsNavHistoryExpire::OnQuit() -{ - mozIStorageConnection* connection = mHistory->GetStorageConnection(); - if (! connection) { - NS_NOTREACHED("No connection"); - return; - } - - // Need to cancel any pending timers so we don't try to expire during shutdown - if (mTimer) - mTimer->Cancel(); - - // Handle degenerate runs: - ExpireForDegenerateRuns(); - - // vacuum up dangling items - ExpireHistoryParanoid(connection); - ExpireFaviconsParanoid(connection); - ExpireAnnotationsParanoid(connection); -} - - -// nsNavHistoryExpire::ClearHistory -// -// Performance: ExpireItems sends notifications. We may want to disable this -// for clear history cases. However, my initial tests show that the -// notifications are not a significant part of clear history time. - -nsresult -nsNavHistoryExpire::ClearHistory() -{ - PRBool keepGoing; - - mozIStorageConnection* connection = mHistory->GetStorageConnection(); - NS_ENSURE_TRUE(connection, NS_ERROR_OUT_OF_MEMORY); - - ExpireItems(0, &keepGoing); - - ExpireHistoryParanoid(connection); - ExpireFaviconsParanoid(connection); - ExpireAnnotationsParanoid(connection); - - ENUMERATE_WEAKARRAY(mHistory->mObservers, nsINavHistoryObserver, - OnClearHistory()) - - return NS_OK; -} - - -// nsNavHistoryExpire::OnExpirationChanged -// -// Called when the expiration length in days has changed. We clear any -// next expiration time, meaning that we'll try to expire stuff next time, -// and recompute the value if there's still nothing to expire. - -void -nsNavHistoryExpire::OnExpirationChanged() -{ - mNextExpirationTime = 0; -} - - -// nsNavHistoryExpire::DoPartialExpiration - -nsresult -nsNavHistoryExpire::DoPartialExpiration() -{ - mSequentialRuns ++; - - PRBool keepGoing; - ExpireItems(EXPIRATION_COUNT_PER_RUN, &keepGoing); - - if (keepGoing && mSequentialRuns < MAX_SEQUENTIAL_RUNS) - StartTimer(SUBSEQUENT_EXIPRATION_TIMEOUT); - return NS_OK; -} - - -// nsNavHistoryExpire::ExpireItems -// -// Here, we try to expire aNumToExpire items and their associated data, -// If we expired things and then stopped because we hit this limit, -// aKeepGoing will be set indicating we should keep expiring. If we ran -// out of things to expire, it will be unset indicating we should wait. -// -// As a special case, aNumToExpire can be 0 and we'll expire everything -// in history. - -nsresult -nsNavHistoryExpire::ExpireItems(PRUint32 aNumToExpire, PRBool* aKeepGoing) -{ - // mark how many times we've been able to run - mExpireRuns ++; - - mozIStorageConnection* connection = mHistory->GetStorageConnection(); - NS_ENSURE_TRUE(connection, NS_ERROR_OUT_OF_MEMORY); - - // This transaction is important for performance. It makes the DB flush - // everything to disk in one larger operation rather than many small ones. - // Note that this transaction always commits. - mozStorageTransaction transaction(connection, PR_TRUE); - - *aKeepGoing = PR_TRUE; - - PRInt64 expireTime; - if (aNumToExpire == 0) { - // special case: erase all history - expireTime = 0; - } else { - expireTime = PR_Now() - GetExpirationTimeAgo(); - } - - // find some visits to expire - nsTArray expiredVisits; - nsresult rv = FindVisits(expireTime, aNumToExpire, connection, - expiredVisits); - NS_ENSURE_SUCCESS(rv, rv); - - // if we didn't find the as many things to expire as we could have, then - // we should note the next time we need to expire. - if (expiredVisits.Length() < aNumToExpire) { - *aKeepGoing = PR_FALSE; - ComputeNextExpirationTime(connection); - - if (expiredVisits.Length() == 0) { - // Nothing to expire. Set the flag so we know we don't have to do any - // work on shutdown. - mAnyEmptyRuns = PR_TRUE; - return NS_OK; - } - } - mExpiredItems += expiredVisits.Length(); - - rv = EraseVisits(connection, expiredVisits); - NS_ENSURE_SUCCESS(rv, rv); - - rv = EraseHistory(connection, expiredVisits); - NS_ENSURE_SUCCESS(rv, rv); - - // send observer messages - nsCOMPtr uri; - for (PRUint32 i = 0; i < expiredVisits.Length(); i ++) { - rv = NS_NewURI(getter_AddRefs(uri), expiredVisits[i].uri); - if (NS_FAILED(rv)) continue; - - // FIXME bug 325241 provide a way to observe hidden elements - if (expiredVisits[i].hidden) continue; - - ENUMERATE_WEAKARRAY(mHistory->mObservers, nsINavHistoryObserver, - OnPageExpired(uri, expiredVisits[i].visitDate, - expiredVisits[i].erased)); - } - - // don't worry about errors here, it doesn't affect out ability to continue - EraseFavicons(connection, expiredVisits); - EraseAnnotations(connection, expiredVisits); - - return NS_OK; -} - - -// nsNavHistoryExpireRecord::nsNavHistoryExpireRecord -// -// Statement should be the one created in FindVisits. The parameters must -// agree. - -nsNavHistoryExpireRecord::nsNavHistoryExpireRecord( - mozIStorageStatement* statement) -{ - visitID = statement->AsInt64(0); - pageID = statement->AsInt64(1); - visitDate = statement->AsInt64(2); - statement->GetUTF8String(3, uri); - faviconID = statement->AsInt64(4); - hidden = (statement->AsInt32(5) > 0); - bookmarked = (statement->AsInt32(6) > 0); - erased = PR_FALSE; -} - - -// nsNavHistoryExpire::FindVisits -// -// aExpireThreshold is the time at which we will delete visits before. -// If it is zero, we will not use a threshold and will match everything. -// -// aNumToExpire is the maximum number of visits to find. If it is 0, then -// we will get all matching visits. - -nsresult -nsNavHistoryExpire::FindVisits(PRTime aExpireThreshold, PRUint32 aNumToExpire, - mozIStorageConnection* aConnection, - nsTArray& aRecords) -{ - nsresult rv; - - // get info for expiring visits, special case no threshold so there is no - // SQL parameter - nsCOMPtr selectStatement; - nsCString sql; - sql.AssignLiteral("SELECT " - "v.visit_id, v.page_id, v.visit_date, h.url, h.favicon, h.hidden, b.item_child " - "FROM moz_historyvisit v LEFT JOIN moz_history h ON v.page_id = h.id " - "LEFT OUTER JOIN moz_bookmarks b on v.page_id = b.item_child"); - if (aExpireThreshold != 0) - sql.AppendLiteral(" WHERE visit_date < ?1"); - rv = aConnection->CreateStatement(sql, getter_AddRefs(selectStatement)); - NS_ENSURE_SUCCESS(rv, rv); - if (aExpireThreshold != 0) { - rv = selectStatement->BindInt64Parameter(0, aExpireThreshold); - NS_ENSURE_SUCCESS(rv, rv); - } - - PRBool hasMore = PR_FALSE; - while (NS_SUCCEEDED(selectStatement->ExecuteStep(&hasMore)) && hasMore && - (aNumToExpire == 0 || aRecords.Length() < aNumToExpire)) { - nsNavHistoryExpireRecord record(selectStatement); - aRecords.AppendElement(record); - } - return NS_OK; -} - - -// nsNavHistoryExpire::EraseVisits - -nsresult -nsNavHistoryExpire::EraseVisits(mozIStorageConnection* aConnection, - const nsTArray& aRecords) -{ - nsCOMPtr deleteStatement; - nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "DELETE FROM moz_historyvisit WHERE visit_id = ?1"), - getter_AddRefs(deleteStatement)); - NS_ENSURE_SUCCESS(rv, rv); - PRUint32 i; - for (i = 0; i < aRecords.Length(); i ++) { - deleteStatement->BindInt64Parameter(0, aRecords[i].visitID); - rv = deleteStatement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - } - return NS_OK; -} - - -// nsNavHistoryExpire::EraseHistory -// -// This erases records in moz_history when there are no more visits. -// We need to be careful not to delete bookmarks and place:URIs. -// -// This will modify the input by setting the erased flag on each of the -// array elements according to whether the history item was erased or not. - -nsresult -nsNavHistoryExpire::EraseHistory(mozIStorageConnection* aConnection, - nsTArray& aRecords) -{ - nsCOMPtr deleteStatement; - nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "DELETE FROM moz_history WHERE id = ?1"), - getter_AddRefs(deleteStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr selectStatement; - rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT page_id FROM moz_historyvisit WHERE page_id = ?1"), - getter_AddRefs(selectStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRUint32 i = 0; i < aRecords.Length(); i ++) { - if (aRecords[i].bookmarked) - continue; // don't delete bookmarked entries - if (StringBeginsWith(aRecords[i].uri, NS_LITERAL_CSTRING("place:"))) - continue; // don't delete "place" URIs - - // check that there are no visits - rv = selectStatement->BindInt64Parameter(0, aRecords[i].pageID); - NS_ENSURE_SUCCESS(rv, rv); - PRBool hasVisit = PR_FALSE; - rv = selectStatement->ExecuteStep(&hasVisit); - selectStatement->Reset(); - if (hasVisit) continue; - - aRecords[i].erased = PR_TRUE; - rv = deleteStatement->BindInt64Parameter(0, aRecords[i].pageID); - rv = deleteStatement->Execute(); - } - return NS_OK; -} - - -// nsNavHistoryExpire::EraseFavicons - -nsresult -nsNavHistoryExpire::EraseFavicons(mozIStorageConnection* aConnection, - const nsTArray& aRecords) -{ - // see if this favicon still has an entry - nsCOMPtr selectStatement; - nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT id FROM moz_history where favicon = ?1"), - getter_AddRefs(selectStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - // delete a favicon - nsCOMPtr deleteStatement; - rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "DELETE FROM moz_favicon WHERE id = ?1"), - getter_AddRefs(deleteStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRUint32 i = 0; i < aRecords.Length(); i ++) { - if (! aRecords[i].erased) - continue; // main entry not expired - if (aRecords[i].faviconID == 0) - continue; // no favicon - selectStatement->BindInt64Parameter(0, aRecords[i].faviconID); - - // see if there are any history entries and skip if so - PRBool hasEntry; - if (NS_SUCCEEDED(selectStatement->ExecuteStep(&hasEntry)) && hasEntry) { - selectStatement->Reset(); - continue; // favicon still referenced - } - selectStatement->Reset(); - - // delete the favicon, ignoring errors. We could have the same favicon - // referenced twice in our list, and we'd try to delete it twice. - deleteStatement->BindInt64Parameter(0, aRecords[i].faviconID); - deleteStatement->Execute(); - } - return NS_OK; -} - - -// nsNavHistoryExpire::EraseAnnotations - -nsresult -nsNavHistoryExpire::EraseAnnotations(mozIStorageConnection* aConnection, - const nsTArray& aRecords) -{ - // FIXME bug 319455 expire annotations - return NS_OK; -} - - -// nsNavHistoryExpire::ExpireHistoryParanoid -// -// Deletes any dangling history entries that aren't associated with any -// visits or bookmarks. Also, special case "place:" URIs. - -nsresult -nsNavHistoryExpire::ExpireHistoryParanoid(mozIStorageConnection* aConnection) -{ - // delete history entries with no visits that are not bookmarked - // also never delete any "place:" URIs (see function header comment) - nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DELETE FROM moz_history WHERE id IN (SELECT id FROM moz_history h " - "LEFT OUTER JOIN moz_historyvisit v ON h.id = v.page_id " - "LEFT OUTER JOIN moz_bookmarks b ON h.id = b.item_child " - "WHERE v.visit_id IS NULL " - "AND b.item_child IS NULL " - "AND SUBSTR(url,0,6) <> 'place:')")); - NS_ENSURE_SUCCESS(rv, rv); - return NS_OK; -} - - -// nsNavHistoryExpire::ExpireFaviconsParanoid -// -// Deletes any dangling favicons that aren't associated with any pages. - -nsresult -nsNavHistoryExpire::ExpireFaviconsParanoid(mozIStorageConnection* aConnection) -{ - return aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( - "DELETE FROM moz_favicon WHERE id IN " - "(SELECT f.id FROM moz_favicon f " - "LEFT OUTER JOIN moz_history h ON f.id = h.favicon " - "WHERE h.favicon IS NULL)")); -} - - -// nsNavHistoryExpire::ExpireAnnotationsParanoid - -nsresult -nsNavHistoryExpire::ExpireAnnotationsParanoid(mozIStorageConnection* aConnection) -{ - // FIXME bug 319455 expire annotations - // Also remember to expire unused names in moz_anno_name - return NS_OK; -} - - -// nsNavHistoryExpire::ExpireForDegenerateRuns -// -// This checks for potentiall degenerate runs. For example, a tinderbox -// loads many web pages quickly and we'll never have a chance to expire. -// Particularly crazy users might also do this. If we detect this, then we -// want to force some expiration so history doesn't keep increasing. -// -// Returns true if we did anything. - -PRBool -nsNavHistoryExpire::ExpireForDegenerateRuns() -{ - // If there were any times that we didn't have anything to expire, this is - // not a degenerate run. - if (mAnyEmptyRuns) - return PR_FALSE; - - // If very few URIs were added this run, or we expired more items than we - // added, don't worry about it - if (mAddCount < 10 || mAddCount < mExpiredItems) - return PR_FALSE; - - // This run looks suspicious, try to expire up to the number of items - // we may have missed this session. - PRBool keepGoing; - ExpireItems(mAddCount - mExpiredItems, &keepGoing); - return PR_TRUE; -} - - -// nsNavHistoryExpire::ComputeNextExpirationTime -// -// This computes mNextExpirationTime. See that var in the header file. -// It is passed the number of microseconds that things expire in. - -void -nsNavHistoryExpire::ComputeNextExpirationTime( - mozIStorageConnection* aConnection) -{ - mNextExpirationTime = 0; - - nsCOMPtr statement; - nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( - "SELECT MIN(visit_date) FROM moz_historyvisit"), - getter_AddRefs(statement)); - NS_ASSERTION(NS_SUCCEEDED(rv), "Could not create statement"); - if (NS_FAILED(rv)) return; - - PRBool hasMore; - rv = statement->ExecuteStep(&hasMore); - if (NS_FAILED(rv) || ! hasMore) - return; // no items, we'll leave mNextExpirationTime = 0 and try to expire - // again next time - - PRTime minTime = statement->AsInt64(0); - mNextExpirationTime = minTime + GetExpirationTimeAgo(); -} - - -// nsNavHistoryExpire::StartTimer - -nsresult -nsNavHistoryExpire::StartTimer(PRUint32 aMilleseconds) -{ - if (! mTimer) - mTimer = do_CreateInstance("@mozilla.org/timer;1"); - NS_ENSURE_STATE(mTimer); // returns on error - nsresult rv = mTimer->InitWithFuncCallback(TimerCallback, this, - aMilleseconds, - nsITimer::TYPE_ONE_SHOT); - NS_ENSURE_SUCCESS(rv, rv); - return NS_OK; -} - - -// nsNavHistoryExpire::TimerCallback - -void // static -nsNavHistoryExpire::TimerCallback(nsITimer* aTimer, void* aClosure) -{ - nsNavHistoryExpire* that = NS_STATIC_CAST(nsNavHistoryExpire*, aClosure); - that->mTimerSet = PR_FALSE; - that->DoPartialExpiration(); -} - - -// nsNavHistoryExpire::GetExpirationTimeAgo - -PRTime -nsNavHistoryExpire::GetExpirationTimeAgo() -{ - PRInt64 expireDays = mHistory->mExpireDays; - - // Prevent Int64 overflow for people that type in huge numbers. - // This number is 2^63 / 24 / 60 / 60 / 1000000 (reversing the math below) - const PRInt64 maxDays = 106751991; - if (expireDays > maxDays) - expireDays = maxDays; - - // compute how long ago to expire from - const PRInt64 secsPerDay = 24*60*60; - const PRInt64 usecsPerSec = 1000000; - const PRInt64 usecsPerDay = secsPerDay * usecsPerSec; - return expireDays * usecsPerDay; -} diff --git a/mozilla/browser/components/places/src/nsNavHistoryExpire.h b/mozilla/browser/components/places/src/nsNavHistoryExpire.h deleted file mode 100644 index da5f18188a6..00000000000 --- a/mozilla/browser/components/places/src/nsNavHistoryExpire.h +++ /dev/null @@ -1,114 +0,0 @@ -//* -*- 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 Mozilla History System - * - * The Initial Developer of the Original Code is - * Google Inc. - * Portions created by the Initial Developer are Copyright (C) 2006 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Brett Wilson (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 ***** */ - -/** - * This class handles expiration of history for nsNavHistory. There is a 1-1 - * mapping between nsNavHistory class and a nsNavHistoryExpire class, the - * code is separated for better understandability. - */ - -class mozIStorageConnection; -class nsNavHistory; -class nsNavHistoryExpireRecord; - -class nsNavHistoryExpire -{ -public: - nsNavHistoryExpire(nsNavHistory* aHistory); - ~nsNavHistoryExpire(); - - void OnAddURI(PRTime aNow); - void OnQuit(); - nsresult ClearHistory(); - void OnExpirationChanged(); - -protected: - - nsNavHistory* mHistory; - - // Number of partial expirations since last AddURI call. - PRUint32 mSequentialRuns; - - nsCOMPtr mTimer; - PRBool mTimerSet; - - // Set when we try to expire something and find there is nothing to expire. - // This short-curcuits the shutdown logic by indicating that there probably - // isn't anything important we need to expire. - PRBool mAnyEmptyRuns; - - // When we have found nothing to expire, we compute the time the next item - // will expire. This is that time so we won't try to expire anything until - // then. It is 0 when we don't need to wait to expire stuff. - PRTime mNextExpirationTime; - void ComputeNextExpirationTime(mozIStorageConnection* aConnection); - - // global statistics - PRUint32 mAddCount; - PRUint32 mExpiredItems; - PRUint32 mExpireRuns; - - nsresult DoPartialExpiration(); - - nsresult ExpireItems(PRUint32 aNumToExpire, PRBool* aKeepGoing); - - // parts of ExpireItems - nsresult FindVisits(PRTime aExpireThreshold, PRUint32 aNumToExpire, - mozIStorageConnection* aConnection, - nsTArray& aRecords); - nsresult EraseVisits(mozIStorageConnection* aConnection, - const nsTArray& aRecords); - nsresult EraseHistory(mozIStorageConnection* aConnection, - nsTArray& aRecords); - nsresult EraseFavicons(mozIStorageConnection* aConnection, - const nsTArray& aRecords); - nsresult EraseAnnotations(mozIStorageConnection* aConnection, - const nsTArray& aRecords); - - // paranoid checks - nsresult ExpireHistoryParanoid(mozIStorageConnection* aConnection); - nsresult ExpireFaviconsParanoid(mozIStorageConnection* aConnection); - nsresult ExpireAnnotationsParanoid(mozIStorageConnection* aConnection); - - PRBool ExpireForDegenerateRuns(); - - nsresult StartTimer(PRUint32 aMilleseconds); - static void TimerCallback(nsITimer* aTimer, void* aClosure); - - PRTime GetExpirationTimeAgo(); -}; diff --git a/mozilla/browser/components/places/src/nsNavHistoryQuery.cpp b/mozilla/browser/components/places/src/nsNavHistoryQuery.cpp deleted file mode 100644 index db80f937d7a..00000000000 --- a/mozilla/browser/components/places/src/nsNavHistoryQuery.cpp +++ /dev/null @@ -1,1328 +0,0 @@ -//* -*- 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 code - * - * 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): - * Brett Wilson (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 ***** */ - -/** - * This file contains the definitions of nsNavHistoryQuery, - * nsNavHistoryQueryOptions, and those functions in nsINavHistory that directly - * support queries (specifically QueryStringToQueries and QueriesToQueryString). - */ - -#include "nsNavHistory.h" -#include "nsEscape.h" -#include "nsCOMArray.h" -#include "nsNetUtil.h" -#include "nsTArray.h" -#include "prprf.h" - -class QueryKeyValuePair -{ -public: - - // QueryKeyValuePair - // - // 01234567890 - // input : qwerty&key=value&qwerty - // ^ ^ ^ - // aKeyBegin | aPastEnd (may point to NULL terminator) - // aEquals - // - // Special case: if aKeyBegin == aEquals, then there is only one string - // and no equal sign, so we treat the entire thing as a key with no value - - QueryKeyValuePair(const nsCSubstring& aSource, PRInt32 aKeyBegin, - PRInt32 aEquals, PRInt32 aPastEnd) - { - if (aEquals == aKeyBegin) - aEquals = aPastEnd; - key = Substring(aSource, aKeyBegin, aEquals - aKeyBegin); - if (aPastEnd - aEquals > 0) - value = Substring(aSource, aEquals + 1, aPastEnd - aEquals - 1); - } - nsCString key; - nsCString value; -}; - -static nsresult TokenizeQueryString(const nsACString& aQuery, - nsTArray* aTokens); -static nsresult ParseQueryBooleanString(const nsCString& aString, - PRBool* aValue); - -// query getters -typedef NS_STDCALL_FUNCPROTO(nsresult, BoolQueryGetter, nsINavHistoryQuery, - GetOnlyBookmarked, (PRBool*)); -typedef NS_STDCALL_FUNCPROTO(nsresult, Uint32QueryGetter, nsINavHistoryQuery, - GetBeginTimeReference, (PRUint32*)); -typedef NS_STDCALL_FUNCPROTO(nsresult, Int64QueryGetter, nsINavHistoryQuery, - GetBeginTime, (PRInt64*)); -static void AppendBoolKeyValueIfTrue(nsACString& aString, - const nsCString& aName, - nsINavHistoryQuery* aQuery, - BoolQueryGetter getter); -static void AppendUint32KeyValueIfNonzero(nsACString& aString, - const nsCString& aName, - nsINavHistoryQuery* aQuery, - Uint32QueryGetter getter); -static void AppendInt64KeyValueIfNonzero(nsACString& aString, - const nsCString& aName, - nsINavHistoryQuery* aQuery, - Int64QueryGetter getter); - -// query setters -typedef NS_STDCALL_FUNCPROTO(nsresult, BoolQuerySetter, nsINavHistoryQuery, - SetOnlyBookmarked, (PRBool)); -typedef NS_STDCALL_FUNCPROTO(nsresult, Uint32QuerySetter, nsINavHistoryQuery, - SetBeginTimeReference, (PRUint32)); -typedef NS_STDCALL_FUNCPROTO(nsresult, Int64QuerySetter, nsINavHistoryQuery, - SetBeginTime, (PRInt64)); -static void SetQueryKeyBool(const nsCString& aValue, nsINavHistoryQuery* aQuery, - BoolQuerySetter setter); -static void SetQueryKeyUint32(const nsCString& aValue, nsINavHistoryQuery* aQuery, - Uint32QuerySetter setter); -static void SetQueryKeyInt64(const nsCString& aValue, nsINavHistoryQuery* aQuery, - Int64QuerySetter setter); - -// options setters -typedef NS_STDCALL_FUNCPROTO(nsresult, BoolOptionsSetter, - nsINavHistoryQueryOptions, - SetExpandQueries, (PRBool)); -typedef NS_STDCALL_FUNCPROTO(nsresult, Uint32OptionsSetter, - nsINavHistoryQueryOptions, - SetResultType, (PRUint32)); -static void SetOptionsKeyBool(const nsCString& aValue, - nsINavHistoryQueryOptions* aOptions, - BoolOptionsSetter setter); -static void SetOptionsKeyUint32(const nsCString& aValue, - nsINavHistoryQueryOptions* aOptions, - Uint32OptionsSetter setter); - -// Components of a query string. -// Note that query strings are also generated in nsNavBookmarks::GetFolderURI -// for performance reasons, so if you change these values, change that, too. -#define QUERYKEY_BEGIN_TIME "beginTime" -#define QUERYKEY_BEGIN_TIME_REFERENCE "beginTimeRef" -#define QUERYKEY_END_TIME "endTime" -#define QUERYKEY_END_TIME_REFERENCE "endTimeRef" -#define QUERYKEY_SEARCH_TERMS "terms" -#define QUERYKEY_ONLY_BOOKMARKED "onlyBookmarked" -#define QUERYKEY_DOMAIN_IS_HOST "domainIsHost" -#define QUERYKEY_DOMAIN "domain" -#define QUERYKEY_FOLDER "folder" -#define QUERYKEY_NOTANNOTATION "!annotation" -#define QUERYKEY_ANNOTATION "annotation" -#define QUERYKEY_URI "uri" -#define QUERYKEY_URIISPREFIX "uriIsPrefix" -#define QUERYKEY_SEPARATOR "OR" -#define QUERYKEY_GROUP "group" -#define QUERYKEY_SORT "sort" -#define QUERYKEY_RESULT_TYPE "type" -#define QUERYKEY_EXCLUDE_ITEMS "excludeItems" -#define QUERYKEY_EXCLUDE_QUERIES "excludeQueries" -#define QUERYKEY_EXCLUDE_READ_ONLY_FOLDERS "excludeReadOnlyFolders" -#define QUERYKEY_EXPAND_QUERIES "expandQueries" -#define QUERYKEY_FORCE_ORIGINAL_TITLE "originalTitle" -#define QUERYKEY_INCLUDE_HIDDEN "includeHidden" -#define QUERYKEY_MAX_RESULTS "maxResults" - -inline void AppendAmpersandIfNonempty(nsACString& aString) -{ - if (! aString.IsEmpty()) - aString.Append('&'); -} -inline void AppendInt32(nsACString& str, PRInt32 i) -{ - nsCAutoString tmp; - tmp.AppendInt(i); - str.Append(tmp); -} -inline void AppendInt64(nsACString& str, PRInt64 i) -{ - nsCString tmp; - tmp.AppendInt(i); - str.Append(tmp); -} - - -// nsNavHistory::QueryStringToQueries -// -// From C++ places code, you should use QueryStringToQueryArray, this is -// the harder-to-use XPCOM version. - -NS_IMETHODIMP -nsNavHistory::QueryStringToQueries(const nsACString& aQueryString, - nsINavHistoryQuery*** aQueries, - PRUint32* aResultCount, - nsINavHistoryQueryOptions** aOptions) -{ - *aQueries = nsnull; - *aResultCount = 0; - nsCOMPtr options; - nsCOMArray queries; - nsresult rv = QueryStringToQueryArray(aQueryString, &queries, - getter_AddRefs(options)); - NS_ENSURE_SUCCESS(rv, rv); - - *aResultCount = queries.Count(); - if (queries.Count() > 0) { - // convert COM array to raw - *aQueries = NS_STATIC_CAST(nsINavHistoryQuery**, - nsMemory::Alloc(sizeof(nsINavHistoryQuery*) * queries.Count())); - NS_ENSURE_TRUE(*aQueries, NS_ERROR_OUT_OF_MEMORY); - for (PRInt32 i = 0; i < queries.Count(); i ++) { - (*aQueries)[i] = queries[i]; - NS_ADDREF((*aQueries)[i]); - } - } - NS_ADDREF(*aOptions = options); - return NS_OK; -} - - -// nsNavHistory::QueryStringToQueryArray -// -// An internal version of QueryStringToQueries that fills a COM array for -// ease-of-use. - -nsresult -nsNavHistory::QueryStringToQueryArray(const nsACString& aQueryString, - nsCOMArray* aQueries, - nsNavHistoryQueryOptions** aOptions) -{ - nsresult rv; - aQueries->Clear(); - *aOptions = nsnull; - - nsRefPtr options(new nsNavHistoryQueryOptions()); - if (! options) - return NS_ERROR_OUT_OF_MEMORY; - - nsTArray tokens; - rv = TokenizeQueryString(aQueryString, &tokens); - NS_ENSURE_SUCCESS(rv, rv); - - if (tokens.Length() > 0) { - rv = TokensToQueries(tokens, aQueries, options); - if (NS_FAILED(rv)) { - NS_WARNING("Unable to parse the query string: "); - NS_WARNING(PromiseFlatCString(aQueryString).get()); - } - NS_ENSURE_SUCCESS(rv, rv); - } - // when there are no tokens, leave the query array empty - - NS_ADDREF(*aOptions = options); - return NS_OK; -} - - -// nsNavHistory::QueriesToQueryString - -NS_IMETHODIMP -nsNavHistory::QueriesToQueryString(nsINavHistoryQuery **aQueries, - PRUint32 aQueryCount, - nsINavHistoryQueryOptions* aOptions, - nsACString& aQueryString) -{ - nsCOMPtr options = do_QueryInterface(aOptions); - NS_ENSURE_TRUE(options, NS_ERROR_INVALID_ARG); - - aQueryString.AssignLiteral("place:"); - for (PRUint32 queryIndex = 0; queryIndex < aQueryCount; queryIndex ++) { - nsINavHistoryQuery* query = aQueries[queryIndex]; - if (queryIndex > 0) { - AppendAmpersandIfNonempty(aQueryString); - aQueryString += NS_LITERAL_CSTRING(QUERYKEY_SEPARATOR); - } - - PRBool hasIt; - - // begin time - query->GetHasBeginTime(&hasIt); - if (hasIt) { - AppendInt64KeyValueIfNonzero(aQueryString, - NS_LITERAL_CSTRING(QUERYKEY_BEGIN_TIME), - query, &nsINavHistoryQuery::GetBeginTime); - AppendUint32KeyValueIfNonzero(aQueryString, - NS_LITERAL_CSTRING(QUERYKEY_BEGIN_TIME_REFERENCE), - query, &nsINavHistoryQuery::GetBeginTimeReference); - } - - // end time - query->GetHasEndTime(&hasIt); - if (hasIt) { - AppendInt64KeyValueIfNonzero(aQueryString, - NS_LITERAL_CSTRING(QUERYKEY_END_TIME), - query, &nsINavHistoryQuery::GetEndTime); - AppendUint32KeyValueIfNonzero(aQueryString, - NS_LITERAL_CSTRING(QUERYKEY_END_TIME_REFERENCE), - query, &nsINavHistoryQuery::GetEndTimeReference); - } - - // search terms - query->GetHasSearchTerms(&hasIt); - if (hasIt) { - nsAutoString searchTerms; - query->GetSearchTerms(searchTerms); - nsCString escapedTerms; - if (! NS_Escape(NS_ConvertUTF16toUTF8(searchTerms), escapedTerms, - url_XAlphas)) - return NS_ERROR_OUT_OF_MEMORY; - - AppendAmpersandIfNonempty(aQueryString); - aQueryString += NS_LITERAL_CSTRING(QUERYKEY_SEARCH_TERMS "="); - aQueryString += escapedTerms; - } - - // only bookmarked - AppendBoolKeyValueIfTrue(aQueryString, - NS_LITERAL_CSTRING(QUERYKEY_ONLY_BOOKMARKED), - query, &nsINavHistoryQuery::GetOnlyBookmarked); - - // domain (+ is host), only call if hasDomain, which means non-IsVoid - // this means we may get an empty string for the domain in the result, - // which is valid - query->GetHasDomain(&hasIt); - if (hasIt) { - AppendBoolKeyValueIfTrue(aQueryString, - NS_LITERAL_CSTRING(QUERYKEY_DOMAIN_IS_HOST), - query, &nsINavHistoryQuery::GetDomainIsHost); - nsCAutoString domain; - nsresult rv = query->GetDomain(domain); - NS_ASSERTION(NS_SUCCEEDED(rv), "Failure getting value"); - nsCString escapedDomain; - PRBool success = NS_Escape(domain, escapedDomain, url_XAlphas); - NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY); - - AppendAmpersandIfNonempty(aQueryString); - aQueryString.Append(NS_LITERAL_CSTRING(QUERYKEY_DOMAIN "=")); - aQueryString.Append(escapedDomain); - } - - // uri - query->GetHasUri(&hasIt); - if (hasIt) { - AppendBoolKeyValueIfTrue(aQueryString, - NS_LITERAL_CSTRING(QUERYKEY_URIISPREFIX), - query, &nsINavHistoryQuery::GetUriIsPrefix); - nsCOMPtr uri; - query->GetUri(getter_AddRefs(uri)); - NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); // hasURI should tell is if invalid - nsCAutoString uriSpec; - nsresult rv = uri->GetSpec(uriSpec); - NS_ENSURE_SUCCESS(rv, rv); - nsCAutoString escaped; - PRBool success = NS_Escape(uriSpec, escaped, url_XAlphas); - NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY); - - AppendAmpersandIfNonempty(aQueryString); - aQueryString.Append(NS_LITERAL_CSTRING(QUERYKEY_URI "=")); - aQueryString.Append(escaped); - } - - // annotation - query->GetHasAnnotation(&hasIt); - if (hasIt) { - AppendAmpersandIfNonempty(aQueryString); - PRBool annotationIsNot; - query->GetAnnotationIsNot(&annotationIsNot); - if (annotationIsNot) - aQueryString.AppendLiteral(QUERYKEY_NOTANNOTATION "="); - else - aQueryString.AppendLiteral(QUERYKEY_ANNOTATION "="); - nsCAutoString annot; - query->GetAnnotation(annot); - nsCAutoString escaped; - PRBool success = NS_Escape(annot, escaped, url_XAlphas); - NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY); - aQueryString.Append(escaped); - } - - // folders - PRInt64 *folders = nsnull; - PRUint32 folderCount = 0; - query->GetFolders(&folderCount, &folders); - for (PRUint32 i = 0; i < folderCount; ++i) { - AppendAmpersandIfNonempty(aQueryString); - aQueryString += NS_LITERAL_CSTRING(QUERYKEY_FOLDER "="); - AppendInt64(aQueryString, folders[0]); - } - } - - // grouping - PRUint32 groupCount; - const PRUint32* groups = options->GroupingMode(&groupCount); - for (PRUint32 i = 0; i < groupCount; i ++) { - AppendAmpersandIfNonempty(aQueryString); - aQueryString += NS_LITERAL_CSTRING(QUERYKEY_GROUP "="); - AppendInt32(aQueryString, groups[i]); - } - - // sorting - if (options->SortingMode() != nsINavHistoryQueryOptions::SORT_BY_NONE) { - AppendAmpersandIfNonempty(aQueryString); - aQueryString += NS_LITERAL_CSTRING(QUERYKEY_SORT "="); - AppendInt32(aQueryString, options->SortingMode()); - } - - // result type - if (options->ResultType() != nsINavHistoryQueryOptions::RESULTS_AS_URI) { - AppendAmpersandIfNonempty(aQueryString); - aQueryString += NS_LITERAL_CSTRING(QUERYKEY_RESULT_TYPE "="); - AppendInt32(aQueryString, options->ResultType()); - } - - // exclude items - if (options->ExcludeItems()) { - AppendAmpersandIfNonempty(aQueryString); - aQueryString += NS_LITERAL_CSTRING(QUERYKEY_EXCLUDE_ITEMS "=1"); - } - - // exclude queries - if (options->ExcludeQueries()) { - AppendAmpersandIfNonempty(aQueryString); - aQueryString += NS_LITERAL_CSTRING(QUERYKEY_EXCLUDE_QUERIES "=1"); - } - - // exclude read only folders - if (options->ExcludeReadOnlyFolders()) { - AppendAmpersandIfNonempty(aQueryString); - aQueryString += NS_LITERAL_CSTRING(QUERYKEY_EXCLUDE_READ_ONLY_FOLDERS "=1"); - } - - // expand queries - if (options->ExpandQueries()) { - AppendAmpersandIfNonempty(aQueryString); - aQueryString += NS_LITERAL_CSTRING(QUERYKEY_EXPAND_QUERIES "=1"); - } - - // title mode - if (options->ForceOriginalTitle()) { - AppendAmpersandIfNonempty(aQueryString); - aQueryString += NS_LITERAL_CSTRING(QUERYKEY_FORCE_ORIGINAL_TITLE "=1"); - } - - // include hidden - if (options->IncludeHidden()) { - AppendAmpersandIfNonempty(aQueryString); - aQueryString += NS_LITERAL_CSTRING(QUERYKEY_INCLUDE_HIDDEN "=1"); - } - - // max results - if (options->MaxResults()) { - AppendAmpersandIfNonempty(aQueryString); - aQueryString += NS_LITERAL_CSTRING(QUERYKEY_MAX_RESULTS "="); - AppendInt32(aQueryString, options->MaxResults()); - } - - return NS_OK; -} - - -// TokenizeQueryString - -nsresult -TokenizeQueryString(const nsACString& aQuery, - nsTArray* aTokens) -{ - // Strip off the "place:" prefix - const PRUint32 prefixlen = 6; // = strlen("place:"); - nsCString query; - if (aQuery.Length() > prefixlen && - Substring(aQuery, 0, prefixlen).EqualsLiteral("place:")) - query = Substring(aQuery, prefixlen); - else - query = aQuery; - - PRInt32 keyFirstIndex = 0; - PRInt32 equalsIndex = 0; - for (PRUint32 i = 0; i < query.Length(); i ++) { - if (query[i] == '&') { - // new clause, save last one - if (i - keyFirstIndex > 1) { - if (! aTokens->AppendElement(QueryKeyValuePair(query, keyFirstIndex, - equalsIndex, i))) - return NS_ERROR_OUT_OF_MEMORY; - } - keyFirstIndex = equalsIndex = i + 1; - } else if (query[i] == '=') { - equalsIndex = i; - } - } - - // handle last pair, if any - if (query.Length() - keyFirstIndex > 1) { - if (! aTokens->AppendElement(QueryKeyValuePair(query, keyFirstIndex, - equalsIndex, query.Length()))) - return NS_ERROR_OUT_OF_MEMORY; - } - return NS_OK; -} - -// nsNavHistory::TokensToQueries - -nsresult -nsNavHistory::TokensToQueries(const nsTArray& aTokens, - nsCOMArray* aQueries, - nsNavHistoryQueryOptions* aOptions) -{ - nsresult rv; - if (aTokens.Length() == 0) - return NS_OK; // nothing to do - - nsTArray groups; - nsTArray folders; - - nsCOMPtr query(new nsNavHistoryQuery()); - if (! query) - return NS_ERROR_OUT_OF_MEMORY; - if (! aQueries->AppendObject(query)) - return NS_ERROR_OUT_OF_MEMORY; - for (PRUint32 i = 0; i < aTokens.Length(); i ++) { - const QueryKeyValuePair& kvp = aTokens[i]; - - // begin time - if (kvp.key.EqualsLiteral(QUERYKEY_BEGIN_TIME)) { - SetQueryKeyInt64(kvp.value, query, &nsINavHistoryQuery::SetBeginTime); - - // begin time reference - } else if (kvp.key.EqualsLiteral(QUERYKEY_BEGIN_TIME_REFERENCE)) { - SetQueryKeyUint32(kvp.value, query, &nsINavHistoryQuery::SetBeginTimeReference); - - // end time - } else if (kvp.key.EqualsLiteral(QUERYKEY_END_TIME)) { - SetQueryKeyInt64(kvp.value, query, &nsINavHistoryQuery::SetEndTime); - - // end time reference - } else if (kvp.key.EqualsLiteral(QUERYKEY_END_TIME_REFERENCE)) { - SetQueryKeyUint32(kvp.value, query, &nsINavHistoryQuery::SetEndTimeReference); - - // search terms - } else if (kvp.key.EqualsLiteral(QUERYKEY_SEARCH_TERMS)) { - nsCString unescapedTerms = kvp.value; - NS_UnescapeURL(unescapedTerms); // modifies input - rv = query->SetSearchTerms(NS_ConvertUTF8toUTF16(unescapedTerms)); - NS_ENSURE_SUCCESS(rv, rv); - - // onlyBookmarked flag - } else if (kvp.key.EqualsLiteral(QUERYKEY_ONLY_BOOKMARKED)) { - SetQueryKeyBool(kvp.value, query, &nsINavHistoryQuery::SetOnlyBookmarked); - - // domainIsHost flag - } else if (kvp.key.EqualsLiteral(QUERYKEY_DOMAIN_IS_HOST)) { - SetQueryKeyBool(kvp.value, query, &nsINavHistoryQuery::SetDomainIsHost); - - // domain string - } else if (kvp.key.EqualsLiteral(QUERYKEY_DOMAIN)) { - nsCAutoString unescapedDomain(kvp.value); - NS_UnescapeURL(unescapedDomain); // modifies input - rv = query->SetDomain(unescapedDomain); - NS_ENSURE_SUCCESS(rv, rv); - - // folders: FIXME use folder name??? - } else if (kvp.key.EqualsLiteral(QUERYKEY_FOLDER)) { - PRInt64 folder; - if (PR_sscanf(kvp.value.get(), "%lld", &folder) == 1) { - NS_ENSURE_TRUE(folders.AppendElement(folder), NS_ERROR_OUT_OF_MEMORY); - } else { - NS_WARNING("folders value in query is invalid, ignoring"); - } - - // uri - } else if (kvp.key.EqualsLiteral(QUERYKEY_URI)) { - nsCAutoString unescapedUri(kvp.value); - NS_UnescapeURL(unescapedUri); // modifies input - nsCOMPtr uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), unescapedUri); - if (NS_FAILED(rv)) { - NS_WARNING("Unable to parse URI"); - } - rv = query->SetUri(uri); - NS_ENSURE_SUCCESS(rv, rv); - - // URI is prefix - } else if (kvp.key.EqualsLiteral(QUERYKEY_URIISPREFIX)) { - SetQueryKeyBool(kvp.value, query, &nsINavHistoryQuery::SetUriIsPrefix); - - // not annotation - } else if (kvp.key.EqualsLiteral(QUERYKEY_NOTANNOTATION)) { - nsCAutoString unescaped(kvp.value); - NS_UnescapeURL(unescaped); // modifies input - query->SetAnnotationIsNot(PR_TRUE); - query->SetAnnotation(unescaped); - - // annotation - } else if (kvp.key.EqualsLiteral(QUERYKEY_ANNOTATION)) { - nsCAutoString unescaped(kvp.value); - NS_UnescapeURL(unescaped); // modifies input - query->SetAnnotationIsNot(PR_FALSE); - query->SetAnnotation(unescaped); - - // new query component - } else if (kvp.key.EqualsLiteral(QUERYKEY_SEPARATOR)) { - - if (folders.Length() != 0) { - query->SetFolders(folders.Elements(), folders.Length()); - folders.Clear(); - } - - query = new nsNavHistoryQuery(); - if (! query) - return NS_ERROR_OUT_OF_MEMORY; - if (! aQueries->AppendObject(query)) - return NS_ERROR_OUT_OF_MEMORY; - - // grouping - } else if (kvp.key.EqualsLiteral(QUERYKEY_GROUP)) { - PRUint32 grouping = kvp.value.ToInteger((PRInt32*)&rv); - if (NS_SUCCEEDED(rv)) { - groups.AppendElement(grouping); - } else { - NS_WARNING("Bad number for grouping in query"); - } - - // sorting mode - } else if (kvp.key.EqualsLiteral(QUERYKEY_SORT)) { - SetOptionsKeyUint32(kvp.value, aOptions, - &nsINavHistoryQueryOptions::SetSortingMode); - - // result type - } else if (kvp.key.EqualsLiteral(QUERYKEY_RESULT_TYPE)) { - SetOptionsKeyUint32(kvp.value, aOptions, - &nsINavHistoryQueryOptions::SetResultType); - - // exclude items - } else if (kvp.key.EqualsLiteral(QUERYKEY_EXCLUDE_ITEMS)) { - SetOptionsKeyBool(kvp.value, aOptions, - &nsINavHistoryQueryOptions::SetExcludeItems); - - // exclude queries - } else if (kvp.key.EqualsLiteral(QUERYKEY_EXCLUDE_QUERIES)) { - SetOptionsKeyBool(kvp.value, aOptions, - &nsINavHistoryQueryOptions::SetExcludeQueries); - - // exclude read only folders - } else if (kvp.key.EqualsLiteral(QUERYKEY_EXCLUDE_READ_ONLY_FOLDERS)) { - SetOptionsKeyBool(kvp.value, aOptions, - &nsINavHistoryQueryOptions::SetExcludeReadOnlyFolders); - - // expand queries - } else if (kvp.key.EqualsLiteral(QUERYKEY_EXPAND_QUERIES)) { - SetOptionsKeyBool(kvp.value, aOptions, - &nsINavHistoryQueryOptions::SetExpandQueries); - - // force original title - } else if (kvp.key.EqualsLiteral(QUERYKEY_FORCE_ORIGINAL_TITLE)) { - SetOptionsKeyBool(kvp.value, aOptions, - &nsINavHistoryQueryOptions::SetForceOriginalTitle); - - // include hidden - } else if (kvp.key.EqualsLiteral(QUERYKEY_INCLUDE_HIDDEN)) { - SetOptionsKeyBool(kvp.value, aOptions, - &nsINavHistoryQueryOptions::SetIncludeHidden); - - // max results - } else if (kvp.key.EqualsLiteral(QUERYKEY_MAX_RESULTS)) { - SetOptionsKeyUint32(kvp.value, aOptions, - &nsINavHistoryQueryOptions::SetMaxResults); - - // unknown key - } else { - aQueries->Clear(); - return NS_ERROR_INVALID_ARG; - } - } - - if (folders.Length() != 0) - query->SetFolders(folders.Elements(), folders.Length()); - aOptions->SetGroupingMode(groups.Elements(), groups.Length()); - - return NS_OK; -} - - -// ParseQueryBooleanString -// -// Converts a 0/1 or true/false string into a bool - -nsresult -ParseQueryBooleanString(const nsCString& aString, PRBool* aValue) -{ - if (aString.EqualsLiteral("1") || aString.EqualsLiteral("true")) { - *aValue = PR_TRUE; - return NS_OK; - } else if (aString.EqualsLiteral("0") || aString.EqualsLiteral("false")) { - *aValue = PR_FALSE; - return NS_OK; - } - return NS_ERROR_INVALID_ARG; -} - - -// nsINavHistoryQuery ********************************************************** - -NS_IMPL_ISUPPORTS2(nsNavHistoryQuery, nsNavHistoryQuery, nsINavHistoryQuery) - -// nsINavHistoryQuery::nsNavHistoryQuery -// -// This must initialize the object such that the default values will cause -// all history to be returned if this query is used. Then the caller can -// just set the things it's interested in. - -nsNavHistoryQuery::nsNavHistoryQuery() - : mBeginTime(0), mBeginTimeReference(TIME_RELATIVE_EPOCH), - mEndTime(0), mEndTimeReference(TIME_RELATIVE_EPOCH), - mOnlyBookmarked(PR_FALSE), mDomainIsHost(PR_FALSE), - mUriIsPrefix(PR_FALSE), mAnnotationIsNot(PR_FALSE) -{ - // differentiate not set (IsVoid) from empty string (local files) - mDomain.SetIsVoid(PR_TRUE); -} - -/* attribute PRTime beginTime; */ -NS_IMETHODIMP nsNavHistoryQuery::GetBeginTime(PRTime *aBeginTime) -{ - *aBeginTime = mBeginTime; - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryQuery::SetBeginTime(PRTime aBeginTime) -{ - mBeginTime = aBeginTime; - return NS_OK; -} - -/* attribute long beginTimeReference; */ -NS_IMETHODIMP nsNavHistoryQuery::GetBeginTimeReference(PRUint32* _retval) -{ - *_retval = mBeginTimeReference; - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryQuery::SetBeginTimeReference(PRUint32 aReference) -{ - if (aReference > TIME_RELATIVE_NOW) - return NS_ERROR_INVALID_ARG; - mBeginTimeReference = aReference; - return NS_OK; -} - -/* readonly attribute boolean hasBeginTime; */ -NS_IMETHODIMP nsNavHistoryQuery::GetHasBeginTime(PRBool* _retval) -{ - *_retval = ! (mBeginTimeReference == TIME_RELATIVE_EPOCH && mBeginTime == 0); - return NS_OK; -} - -/* readonly attribute PRTime absoluteBeginTime; */ -NS_IMETHODIMP nsNavHistoryQuery::GetAbsoluteBeginTime(PRTime* _retval) -{ - *_retval = nsNavHistory::NormalizeTime(mBeginTimeReference, mBeginTime); - return NS_OK; -} - -/* attribute PRTime endTime; */ -NS_IMETHODIMP nsNavHistoryQuery::GetEndTime(PRTime *aEndTime) -{ - *aEndTime = mEndTime; - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryQuery::SetEndTime(PRTime aEndTime) -{ - mEndTime = aEndTime; - return NS_OK; -} - -/* attribute long endTimeReference; */ -NS_IMETHODIMP nsNavHistoryQuery::GetEndTimeReference(PRUint32* _retval) -{ - *_retval = mEndTimeReference; - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryQuery::SetEndTimeReference(PRUint32 aReference) -{ - if (aReference > TIME_RELATIVE_NOW) - return NS_ERROR_INVALID_ARG; - mEndTimeReference = aReference; - return NS_OK; -} - -/* readonly attribute boolean hasEndTime; */ -NS_IMETHODIMP nsNavHistoryQuery::GetHasEndTime(PRBool* _retval) -{ - *_retval = ! (mEndTimeReference == TIME_RELATIVE_EPOCH && mEndTime == 0); - return NS_OK; -} - -/* readonly attribute PRTime absoluteEndTime; */ -NS_IMETHODIMP nsNavHistoryQuery::GetAbsoluteEndTime(PRTime* _retval) -{ - *_retval = nsNavHistory::NormalizeTime(mEndTimeReference, mEndTime); - return NS_OK; -} - -/* attribute string searchTerms; */ -NS_IMETHODIMP nsNavHistoryQuery::GetSearchTerms(nsAString& aSearchTerms) -{ - aSearchTerms = mSearchTerms; - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryQuery::SetSearchTerms(const nsAString& aSearchTerms) -{ - mSearchTerms = aSearchTerms; - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryQuery::GetHasSearchTerms(PRBool* _retval) -{ - *_retval = (! mSearchTerms.IsEmpty()); - return NS_OK; -} - -/* attribute boolean onlyBookmarked; */ -NS_IMETHODIMP nsNavHistoryQuery::GetOnlyBookmarked(PRBool *aOnlyBookmarked) -{ - *aOnlyBookmarked = mOnlyBookmarked; - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryQuery::SetOnlyBookmarked(PRBool aOnlyBookmarked) -{ - mOnlyBookmarked = aOnlyBookmarked; - return NS_OK; -} - -/* attribute boolean domainIsHost; */ -NS_IMETHODIMP nsNavHistoryQuery::GetDomainIsHost(PRBool *aDomainIsHost) -{ - *aDomainIsHost = mDomainIsHost; - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryQuery::SetDomainIsHost(PRBool aDomainIsHost) -{ - mDomainIsHost = aDomainIsHost; - return NS_OK; -} - -/* attribute AUTF8String domain; */ -NS_IMETHODIMP nsNavHistoryQuery::GetDomain(nsACString& aDomain) -{ - aDomain = mDomain; - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryQuery::SetDomain(const nsACString& aDomain) -{ - mDomain = aDomain; - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryQuery::GetHasDomain(PRBool* _retval) -{ - // note that empty but not void is still a valid query (local files) - *_retval = (! mDomain.IsVoid()); - return NS_OK; -} - -/* attribute boolean uriIsPrefix; */ -NS_IMETHODIMP nsNavHistoryQuery::GetUriIsPrefix(PRBool* aIsPrefix) -{ - *aIsPrefix = mUriIsPrefix; - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryQuery::SetUriIsPrefix(PRBool aIsPrefix) -{ - mUriIsPrefix = aIsPrefix; - return NS_OK; -} - -/* attribute nsIURI uri; */ -NS_IMETHODIMP nsNavHistoryQuery::GetUri(nsIURI** aUri) -{ - *aUri = mUri; - NS_ADDREF(*aUri); - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryQuery::SetUri(nsIURI* aUri) -{ - mUri = aUri; - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryQuery::GetHasUri(PRBool* aHasUri) -{ - *aHasUri = (mUri != nsnull); - return NS_OK; -} - -/* attribute boolean annotationIsNot; */ -NS_IMETHODIMP nsNavHistoryQuery::GetAnnotationIsNot(PRBool* aIsNot) -{ - *aIsNot = mAnnotationIsNot; - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryQuery::SetAnnotationIsNot(PRBool aIsNot) -{ - mAnnotationIsNot = aIsNot; - return NS_OK; -} - -/* attribute AUTF8String annotation; */ -NS_IMETHODIMP nsNavHistoryQuery::GetAnnotation(nsACString& aAnnotation) -{ - aAnnotation = mAnnotation; - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryQuery::SetAnnotation(const nsACString& aAnnotation) -{ - mAnnotation = aAnnotation; - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryQuery::GetHasAnnotation(PRBool* aHasIt) -{ - *aHasIt = ! mAnnotation.IsEmpty(); - return NS_OK; -} - -NS_IMETHODIMP nsNavHistoryQuery::GetFolders(PRUint32 *aCount, - PRInt64 **aFolders) -{ - PRUint32 count = mFolders.Length(); - PRInt64 *folders = nsnull; - if (count > 0) { - folders = NS_STATIC_CAST(PRInt64*, - nsMemory::Alloc(count * sizeof(PRInt64))); - NS_ENSURE_TRUE(folders, NS_ERROR_OUT_OF_MEMORY); - - for (PRUint32 i = 0; i < count; ++i) { - folders[i] = mFolders[i]; - } - } - *aCount = count; - *aFolders = folders; - return NS_OK; -} - -NS_IMETHODIMP nsNavHistoryQuery::GetFolderCount(PRUint32 *aCount) -{ - *aCount = mFolders.Length(); - return NS_OK; -} - -NS_IMETHODIMP nsNavHistoryQuery::SetFolders(const PRInt64 *aFolders, - PRUint32 aFolderCount) -{ - if (!mFolders.ReplaceElementsAt(0, mFolders.Length(), - aFolders, aFolderCount)) { - return NS_ERROR_OUT_OF_MEMORY; - } - - return NS_OK; -} - -NS_IMETHODIMP nsNavHistoryQuery::Clone(nsINavHistoryQuery** _retval) -{ - *_retval = nsnull; - - nsNavHistoryQuery *clone = new nsNavHistoryQuery(*this); - NS_ENSURE_TRUE(clone, NS_ERROR_OUT_OF_MEMORY); - - clone->mRefCnt = 0; // the clone doesn't inherit our refcount - NS_ADDREF(*_retval = clone); - return NS_OK; -} - - -// nsNavHistoryQueryOptions -NS_IMPL_ISUPPORTS2(nsNavHistoryQueryOptions, nsNavHistoryQueryOptions, nsINavHistoryQueryOptions) - -NS_IMETHODIMP -nsNavHistoryQueryOptions::GetGroupingMode(PRUint32 *aGroupCount, - PRUint32** aGroupingMode) -{ - if (mGroupCount == 0) { - *aGroupCount = 0; - *aGroupingMode = nsnull; - return NS_OK; - } - *aGroupingMode = NS_STATIC_CAST(PRUint32*, - nsMemory::Alloc(sizeof(PRUint32) * mGroupCount)); - if (! aGroupingMode) - return NS_ERROR_OUT_OF_MEMORY; - for(PRUint32 i = 0; i < mGroupCount; i ++) - (*aGroupingMode)[i] = mGroupings[i]; - *aGroupCount = mGroupCount; - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryOptions::SetGroupingMode(const PRUint32 *aGroupingMode, - PRUint32 aGroupCount) -{ - // check input - PRUint32 i; - for (i = 0; i < aGroupCount; i ++) { - if (aGroupingMode[i] > GROUP_BY_FOLDER) - return NS_ERROR_INVALID_ARG; - } - - if (mGroupings) { - delete[] mGroupings; - mGroupings = nsnull; - mGroupCount = 0; - } - if (! aGroupCount) - return NS_OK; - - mGroupings = new PRUint32[aGroupCount]; - NS_ENSURE_TRUE(mGroupings, NS_ERROR_OUT_OF_MEMORY); - - for (i = 0; i < aGroupCount; ++i) { - mGroupings[i] = aGroupingMode[i]; - } - - mGroupCount = aGroupCount; - return NS_OK; -} - -// sortingMode -NS_IMETHODIMP -nsNavHistoryQueryOptions::GetSortingMode(PRUint32* aMode) -{ - *aMode = mSort; - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryOptions::SetSortingMode(PRUint32 aMode) -{ - if (aMode > SORT_BY_VISITCOUNT_DESCENDING) - return NS_ERROR_INVALID_ARG; - mSort = aMode; - return NS_OK; -} - -// resultType -NS_IMETHODIMP -nsNavHistoryQueryOptions::GetResultType(PRUint32* aType) -{ - *aType = mResultType; - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryOptions::SetResultType(PRUint32 aType) -{ - if (aType > RESULTS_AS_FULL_VISIT) - return NS_ERROR_INVALID_ARG; - mResultType = aType; - return NS_OK; -} - -// excludeItems -NS_IMETHODIMP -nsNavHistoryQueryOptions::GetExcludeItems(PRBool* aExclude) -{ - *aExclude = mExcludeItems; - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryOptions::SetExcludeItems(PRBool aExclude) -{ - mExcludeItems = aExclude; - return NS_OK; -} - -// excludeQueries -NS_IMETHODIMP -nsNavHistoryQueryOptions::GetExcludeQueries(PRBool* aExclude) -{ - *aExclude = mExcludeQueries; - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryOptions::SetExcludeQueries(PRBool aExclude) -{ - mExcludeQueries = aExclude; - return NS_OK; -} - -// excludeReadOnlyFolders -NS_IMETHODIMP -nsNavHistoryQueryOptions::GetExcludeReadOnlyFolders(PRBool* aExclude) -{ - *aExclude = mExcludeReadOnlyFolders; - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryOptions::SetExcludeReadOnlyFolders(PRBool aExclude) -{ - mExcludeReadOnlyFolders = aExclude; - return NS_OK; -} - -// expandQueries -NS_IMETHODIMP -nsNavHistoryQueryOptions::GetExpandQueries(PRBool* aExpand) -{ - *aExpand = mExpandQueries; - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryOptions::SetExpandQueries(PRBool aExpand) -{ - mExpandQueries = aExpand; - return NS_OK; -} - -// forceOriginalTitle -NS_IMETHODIMP -nsNavHistoryQueryOptions::GetForceOriginalTitle(PRBool* aForce) -{ - *aForce = mForceOriginalTitle; - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryOptions::SetForceOriginalTitle(PRBool aForce) -{ - mForceOriginalTitle = aForce; - return NS_OK; -} - -// includeHidden -NS_IMETHODIMP -nsNavHistoryQueryOptions::GetIncludeHidden(PRBool* aIncludeHidden) -{ - *aIncludeHidden = mIncludeHidden; - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryOptions::SetIncludeHidden(PRBool aIncludeHidden) -{ - mIncludeHidden = aIncludeHidden; - return NS_OK; -} - -// maxResults -NS_IMETHODIMP -nsNavHistoryQueryOptions::GetMaxResults(PRUint32* aMaxResults) -{ - *aMaxResults = mMaxResults; - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryOptions::SetMaxResults(PRUint32 aMaxResults) -{ - mMaxResults = aMaxResults; - return NS_OK; -} - -NS_IMETHODIMP -nsNavHistoryQueryOptions::Clone(nsINavHistoryQueryOptions** aResult) -{ - nsNavHistoryQueryOptions *clone = nsnull; - nsresult rv = Clone(&clone); - *aResult = clone; - return rv; -} - -nsresult -nsNavHistoryQueryOptions::Clone(nsNavHistoryQueryOptions **aResult) -{ - *aResult = nsnull; - nsNavHistoryQueryOptions *result = new nsNavHistoryQueryOptions(); - if (! result) - return NS_ERROR_OUT_OF_MEMORY; - - nsRefPtr resultHolder(result); - result->mSort = mSort; - result->mResultType = mResultType; - result->mGroupCount = mGroupCount; - if (mGroupCount) { - result->mGroupings = new PRUint32[mGroupCount]; - if (! result->mGroupings) { - return NS_ERROR_OUT_OF_MEMORY; - } - for (PRUint32 i = 0; i < mGroupCount; i ++) - result->mGroupings[i] = mGroupings[i]; - } else { - result->mGroupCount = nsnull; - } - result->mExcludeItems = mExcludeItems; - result->mExcludeQueries = mExcludeQueries; - result->mExpandQueries = mExpandQueries; - - resultHolder.swap(*aResult); - return NS_OK; -} - - -// AppendBoolKeyValueIfTrue - -void // static -AppendBoolKeyValueIfTrue(nsACString& aString, const nsCString& aName, - nsINavHistoryQuery* aQuery, - BoolQueryGetter getter) -{ - PRBool value; - nsresult rv = (aQuery->*getter)(&value); - NS_ASSERTION(NS_SUCCEEDED(rv), "Failure getting boolean value"); - if (value) { - AppendAmpersandIfNonempty(aString); - aString += aName; - aString.AppendLiteral("=1"); - } -} - - -// AppendUint32KeyValueIfNonzero - -void // static -AppendUint32KeyValueIfNonzero(nsACString& aString, - const nsCString& aName, - nsINavHistoryQuery* aQuery, - Uint32QueryGetter getter) -{ - PRUint32 value; - nsresult rv = (aQuery->*getter)(&value); - NS_ASSERTION(NS_SUCCEEDED(rv), "Failure getting value"); - if (value) { - AppendAmpersandIfNonempty(aString); - aString += aName; - - // AppendInt requires a concrete string - nsCAutoString appendMe("="); - appendMe.AppendInt(value); - aString.Append(appendMe); - } -} - - -// AppendInt64KeyValueIfNonzero - -void // static -AppendInt64KeyValueIfNonzero(nsACString& aString, - const nsCString& aName, - nsINavHistoryQuery* aQuery, - Int64QueryGetter getter) -{ - PRInt64 value; - nsresult rv = (aQuery->*getter)(&value); - NS_ASSERTION(NS_SUCCEEDED(rv), "Failure getting value"); - if (value) { - AppendAmpersandIfNonempty(aString); - aString += aName; - nsCAutoString appendMe("="); - appendMe.AppendInt(value); - aString.Append(appendMe); - } -} - - -// SetQuery/OptionsKeyBool - -void // static -SetQueryKeyBool(const nsCString& aValue, nsINavHistoryQuery* aQuery, - BoolQuerySetter setter) -{ - PRBool value; - nsresult rv = ParseQueryBooleanString(aValue, &value); - if (NS_SUCCEEDED(rv)) { - rv = (aQuery->*setter)(value); - if (NS_FAILED(rv)) { - NS_WARNING("Error setting boolean key value"); - } - } else { - NS_WARNING("Invalid boolean key value in query string."); - } -} -void // static -SetOptionsKeyBool(const nsCString& aValue, nsINavHistoryQueryOptions* aOptions, - BoolOptionsSetter setter) -{ - PRBool value; - nsresult rv = ParseQueryBooleanString(aValue, &value); - if (NS_SUCCEEDED(rv)) { - rv = (aOptions->*setter)(value); - if (NS_FAILED(rv)) { - NS_WARNING("Error setting boolean key value"); - } - } else { - NS_WARNING("Invalid boolean key value in query string."); - } -} - - -// SetQuery/OptionsKeyUint32 - -void // static -SetQueryKeyUint32(const nsCString& aValue, nsINavHistoryQuery* aQuery, - Uint32QuerySetter setter) -{ - nsresult rv; - PRUint32 value = aValue.ToInteger(NS_REINTERPRET_CAST(PRInt32*, &rv)); - if (NS_SUCCEEDED(rv)) { - rv = (aQuery->*setter)(value); - if (NS_FAILED(rv)) { - NS_WARNING("Error setting Int32 key value"); - } - } else { - NS_WARNING("Invalid Int32 key value in query string."); - } -} -void // static -SetOptionsKeyUint32(const nsCString& aValue, nsINavHistoryQueryOptions* aOptions, - Uint32OptionsSetter setter) -{ - nsresult rv; - PRUint32 value = aValue.ToInteger(NS_REINTERPRET_CAST(PRInt32*, &rv)); - if (NS_SUCCEEDED(rv)) { - rv = (aOptions->*setter)(value); - if (NS_FAILED(rv)) { - NS_WARNING("Error setting Int32 key value"); - } - } else { - NS_WARNING("Invalid Int32 key value in query string."); - } -} - - -// SetQueryKeyInt64 - -void SetQueryKeyInt64(const nsCString& aValue, nsINavHistoryQuery* aQuery, - Int64QuerySetter setter) -{ - nsresult rv; - PRInt64 value; - if (PR_sscanf(aValue.get(), "%lld", &value) == 1) { - rv = (aQuery->*setter)(value); - if (NS_FAILED(rv)) { - NS_WARNING("Error setting Int64 key value"); - } - } else { - NS_WARNING("Invalid Int64 value in query string."); - } -} diff --git a/mozilla/browser/components/places/src/nsNavHistoryQuery.h b/mozilla/browser/components/places/src/nsNavHistoryQuery.h deleted file mode 100644 index 0b579beb47a..00000000000 --- a/mozilla/browser/components/places/src/nsNavHistoryQuery.h +++ /dev/null @@ -1,180 +0,0 @@ -/* -*- 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 code. - * - * 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): - * Brett Wilson (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 ***** */ - -/** - * The definitions of nsNavHistoryQuery and nsNavHistoryQueryOptions. This - * header file should only be included from nsNavHistory.h, include that if - * you want these classes. - */ - -#ifndef nsNavHistoryQuery_h_ -#define nsNavHistoryQuery_h_ - -// nsNavHistoryQuery -// -// This class encapsulates the parameters for basic history queries for -// building UI, trees, lists, etc. - -#define NS_NAVHISTORYQUERY_IID \ -{ 0xb10185e0, 0x86eb, 0x4612, { 0x95, 0x7c, 0x09, 0x34, 0xf2, 0xb1, 0xce, 0xd7 } } - -class nsNavHistoryQuery : public nsINavHistoryQuery -{ -public: - nsNavHistoryQuery(); - // note: we use a copy constructor in Clone(), the default is good enough - -#ifdef MOZILLA_1_8_BRANCH - NS_DEFINE_STATIC_IID_ACCESSOR(NS_NAVHISTORYQUERY_IID) -#else - NS_DECLARE_STATIC_IID_ACCESSOR(NS_NAVHISTORYQUERY_IID) -#endif - NS_DECL_ISUPPORTS - NS_DECL_NSINAVHISTORYQUERY - - PRTime BeginTime() { return mBeginTime; } - PRUint32 BeginTimeReference() { return mBeginTimeReference; } - PRTime EndTime() { return mEndTime; } - PRUint32 EndTimeReference() { return mEndTimeReference; } - const nsString& SearchTerms() { return mSearchTerms; } - PRBool OnlyBookmarked() { return mOnlyBookmarked; } - PRBool DomainIsHost() { return mDomainIsHost; } - const nsCString& Domain() { return mDomain; } - PRBool UriIsPrefix() { return mUriIsPrefix; } - nsIURI* Uri() { return mUri; } // NOT AddRef-ed! - PRBool AnnotationIsNot() { return mAnnotationIsNot; } - const nsCString& Annotation() { return mAnnotation; } - const nsTArray& Folders() const { return mFolders; } - -private: - ~nsNavHistoryQuery() {} - -protected: - - PRTime mBeginTime; - PRUint32 mBeginTimeReference; - PRTime mEndTime; - PRUint32 mEndTimeReference; - nsString mSearchTerms; - PRBool mOnlyBookmarked; - PRBool mDomainIsHost; - nsCString mDomain; // Default is IsVoid, empty string is valid query - PRBool mUriIsPrefix; - nsCOMPtr mUri; - PRBool mAnnotationIsNot; - nsCString mAnnotation; - nsTArray mFolders; -}; - -#ifndef MOZILLA_1_8_BRANCH -NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHistoryQuery, NS_NAVHISTORYQUERY_IID) -#endif - -// nsNavHistoryQueryOptions - -#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), - mExcludeItems(PR_FALSE), - mExcludeQueries(PR_FALSE), - mExcludeReadOnlyFolders(PR_FALSE), - mExpandQueries(PR_FALSE), - mForceOriginalTitle(PR_FALSE), - mIncludeHidden(PR_FALSE), - mMaxResults(0) - { } - -#ifdef MOZILLA_1_8_BRANCH - NS_DEFINE_STATIC_IID_ACCESSOR(NS_NAVHISTORYQUERYOPTIONS_IID) -#else - NS_DECLARE_STATIC_IID_ACCESSOR(NS_NAVHISTORYQUERYOPTIONS_IID) -#endif - - NS_DECL_ISUPPORTS - NS_DECL_NSINAVHISTORYQUERYOPTIONS - - PRUint32 SortingMode() const { return mSort; } - PRUint32 ResultType() const { return mResultType; } - const PRUint32* GroupingMode(PRUint32 *count) const { - *count = mGroupCount; return mGroupings; - } - PRBool ExcludeItems() const { return mExcludeItems; } - PRBool ExcludeQueries() const { return mExcludeQueries; } - PRBool ExcludeReadOnlyFolders() const { return mExcludeReadOnlyFolders; } - PRBool ExpandQueries() const { return mExpandQueries; } - PRBool ForceOriginalTitle() const { return mForceOriginalTitle; } - PRBool IncludeHidden() const { return mIncludeHidden; } - PRUint32 MaxResults() const { return mMaxResults; } - - nsresult Clone(nsNavHistoryQueryOptions **aResult); - -private: - nsNavHistoryQueryOptions(const nsNavHistoryQueryOptions& other) {} // no copy - - ~nsNavHistoryQueryOptions() { delete[] mGroupings; } - - // IF YOU ADD MORE ITEMS: - // * Add a new getter for C++ above if it makes sense - // * Add to the serialization code - // * Add to the deserialization code - // * Add to the nsNavHistoryQueryOptions::Clone() function - // * Add to the nsNavHistory.cpp:IsSimpleBookmarksQuery function if applicable - PRUint32 mSort; - PRUint32 mResultType; - PRUint32 mGroupCount; - PRUint32 *mGroupings; - PRBool mExcludeItems; - PRBool mExcludeQueries; - PRBool mExcludeReadOnlyFolders; - PRBool mExpandQueries; - PRBool mForceOriginalTitle; - PRBool mIncludeHidden; - PRUint32 mMaxResults; -}; - -#ifndef MOZILLA_1_8_BRANCH -NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHistoryQueryOptions, NS_NAVHISTORYQUERYOPTIONS_IID) -#endif - -#endif // nsNavHistoryQuery_h_ - diff --git a/mozilla/browser/components/places/src/nsNavHistoryResult.cpp b/mozilla/browser/components/places/src/nsNavHistoryResult.cpp deleted file mode 100755 index d2fcb164390..00000000000 --- a/mozilla/browser/components/places/src/nsNavHistoryResult.cpp +++ /dev/null @@ -1,5395 +0,0 @@ -//* -*- 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 Mozilla History System - * - * 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): - * Brett Wilson (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 -#include "nsNavHistory.h" -#include "nsNavBookmarks.h" - -#include "nsIArray.h" -#include "nsCOMPtr.h" -#include "nsDebug.h" -#include "nsFaviconService.h" -#include "nsHashPropertyBag.h" -#include "nsIComponentManager.h" -#include "nsIDateTimeFormat.h" -#include "nsIDOMElement.h" -#include "nsILocale.h" -#include "nsILocaleService.h" -#include "nsILocalFile.h" -#include "nsIRemoteContainer.h" -#include "nsIServiceManager.h" -#include "nsISupportsPrimitives.h" -#include "nsITreeColumns.h" -#include "nsIURI.h" -#include "nsIURL.h" -#include "nsIWritablePropertyBag.h" -#include "nsNetUtil.h" -#include "nsPrintfCString.h" -#include "nsPromiseFlatString.h" -#include "nsString.h" -#include "nsUnicharUtils.h" -#include "prtime.h" -#include "prprf.h" -#include "mozStorageHelper.h" - -#define ICONURI_QUERY "chrome://browser/skin/places/query.png" - -// What we want is: NS_INTERFACE_MAP_ENTRY(self) for static IID accessors, -// but some of our classes (like nsNavHistoryResult) have an ambiguous base -// class of nsISupports which prevents this from working (the default macro -// converts it to nsISupports, then addrefs it, then returns it). Therefore, we -// expand the macro here and change it so that it works. Yuck. -#define NS_INTERFACE_MAP_STATIC_AMBIGUOUS(_class) \ - if (aIID.Equals(NS_GET_IID(_class))) { \ - NS_ADDREF(this); \ - *aInstancePtr = this; \ - return NS_OK; \ - } else - -// emulate string comparison (used for sorting) for PRTime and int -inline PRInt32 ComparePRTime(PRTime a, PRTime b) -{ - if (LL_CMP(a, <, b)) - return -1; - else if (LL_CMP(a, >, b)) - return 1; - return 0; -} -inline PRInt32 CompareIntegers(PRUint32 a, PRUint32 b) -{ - return a - b; -} - - -// nsNavHistoryResultNode ****************************************************** - -NS_IMPL_ISUPPORTS2(nsNavHistoryResultNode, - nsNavHistoryResultNode, nsINavHistoryResultNode) - -nsNavHistoryResultNode::nsNavHistoryResultNode( - const nsACString& aURI, const nsACString& aTitle, PRUint32 aAccessCount, - PRTime aTime, const nsACString& aIconURI) : - mParent(nsnull), - mURI(aURI), - mTitle(aTitle), - mAccessCount(aAccessCount), - mTime(aTime), - mFaviconURI(aIconURI), - mBookmarkIndex(-1), - mIndentLevel(-1), - mViewIndex(-1) -{ -} - -NS_IMETHODIMP -nsNavHistoryResultNode::GetIcon(nsIURI** aURI) -{ - nsFaviconService* faviconService = nsFaviconService::GetFaviconService(); - NS_ENSURE_TRUE(faviconService, NS_ERROR_NO_INTERFACE); - if (mFaviconURI.IsEmpty()) { - *aURI = nsnull; - return NS_OK; - } - return faviconService->GetFaviconLinkForIconString(mFaviconURI, aURI); -} - -NS_IMETHODIMP -nsNavHistoryResultNode::GetParent(nsINavHistoryContainerResultNode** aParent) -{ - NS_IF_ADDREF(*aParent = mParent); - return NS_OK; -} - -NS_IMETHODIMP -nsNavHistoryResultNode::GetPropertyBag(nsIWritablePropertyBag** aBag) -{ - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - return result->PropertyBagFor(this, aBag); -} - - -// nsNavHistoryResultNode::OnRemoving -// -// This will zero out some values in case somebody still holds a reference - -void -nsNavHistoryResultNode::OnRemoving() -{ - mParent = nsnull; - mViewIndex = -1; -} - - -// nsNavHistoryResultNode::GetResult -// -// This will find the result for this node. We can ask the nearest container -// for this value (either ourselves or our parents should be a container, -// and all containers have result pointers). - -nsNavHistoryResult* -nsNavHistoryResultNode::GetResult() -{ - nsNavHistoryResultNode* node = this; - do { - if (node->IsContainer()) { - nsNavHistoryContainerResultNode* container = - NS_STATIC_CAST(nsNavHistoryContainerResultNode*, node); - NS_ASSERTION(container->mResult, "Containers must have valid results"); - return container->mResult; - } - node = node->mParent; - } while (node); - NS_NOTREACHED("No container node found in hierarchy!"); - return nsnull; -} - - -// nsNavHistoryResultNode::GetGeneratingOptions -// -// Searches up the tree for the closest node that has an options structure. -// This will tell us the options that were used to generate this node. -// -// Be careful, this function walks up the tree, so it can not be used when -// result nodes are created because they have no parent. Only call this -// function after the tree has been built. - -nsNavHistoryQueryOptions* -nsNavHistoryResultNode::GetGeneratingOptions() -{ - if (! mParent) { - // When we have no parent, it either means we haven't built the tree yet, - // in which case calling this function is a bug, or this node is the root - // of the tree. When we are the root of the tree, our own options are the - // generating options, and we know we are either a query of a folder node. - if (IsFolder()) - return GetAsFolder()->mOptions; - else if (IsQuery()) - return GetAsQuery()->mOptions; - NS_NOTREACHED("Can't find a generating node for this container, perhaps FillStats has not been called on this tree yet?"); - return nsnull; - } - - nsNavHistoryContainerResultNode* cur = mParent; - while (cur) { - if (cur->IsFolder()) - return cur->GetAsFolder()->mOptions; - else if (cur->IsQuery()) - return cur->GetAsQuery()->mOptions; - cur = cur->mParent; - } - // we should always find a folder or query node as an ancestor - NS_NOTREACHED("Can't find a generating node for this container, the tree seemes corrupted."); - return nsnull; -} - - -// nsNavHistoryVisitResultNode ************************************************* - -NS_IMPL_ISUPPORTS_INHERITED1(nsNavHistoryVisitResultNode, - nsNavHistoryResultNode, - nsINavHistoryVisitResultNode) - -nsNavHistoryVisitResultNode::nsNavHistoryVisitResultNode( - const nsACString& aURI, const nsACString& aTitle, PRUint32 aAccessCount, - PRTime aTime, const nsACString& aIconURI, PRInt64 aSession) : - nsNavHistoryResultNode(aURI, aTitle, aAccessCount, aTime, aIconURI), - mSessionId(aSession) -{ -} - - -// nsNavHistoryFullVisitResultNode ********************************************* - -NS_IMPL_ISUPPORTS_INHERITED1(nsNavHistoryFullVisitResultNode, - nsNavHistoryVisitResultNode, - nsINavHistoryFullVisitResultNode) - -nsNavHistoryFullVisitResultNode::nsNavHistoryFullVisitResultNode( - const nsACString& aURI, const nsACString& aTitle, PRUint32 aAccessCount, - PRTime aTime, const nsACString& aIconURI, PRInt64 aSession, - PRInt64 aVisitId, PRInt64 aReferringVisitId, PRInt32 aTransitionType) : - nsNavHistoryVisitResultNode(aURI, aTitle, aAccessCount, aTime, aIconURI, - aSession), - mVisitId(aVisitId), - mReferringVisitId(aReferringVisitId), - mTransitionType(aTransitionType) -{ -} - -// nsNavHistoryContainerResultNode ********************************************* - -NS_IMPL_ADDREF_INHERITED(nsNavHistoryContainerResultNode, nsNavHistoryResultNode) -NS_IMPL_RELEASE_INHERITED(nsNavHistoryContainerResultNode, nsNavHistoryResultNode) - -NS_INTERFACE_MAP_BEGIN(nsNavHistoryContainerResultNode) - NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsNavHistoryContainerResultNode) - NS_INTERFACE_MAP_ENTRY(nsINavHistoryContainerResultNode) -NS_INTERFACE_MAP_END_INHERITING(nsNavHistoryResultNode) - -nsNavHistoryContainerResultNode::nsNavHistoryContainerResultNode( - const nsACString& aURI, const nsACString& aTitle, - const nsACString& aIconURI, PRUint32 aContainerType, PRBool aReadOnly, - const nsACString& aRemoteContainerType) : - nsNavHistoryResultNode(aURI, aTitle, 0, 0, aIconURI), - mResult(nsnull), - mContainerType(aContainerType), - mExpanded(PR_FALSE), - mChildrenReadOnly(aReadOnly), - mRemoteContainerType(aRemoteContainerType) -{ -} - - -// nsNavHistoryContainerResultNode::OnRemoving -// -// Containers should notify their children that they are being removed -// when the container is being removed. - -void -nsNavHistoryContainerResultNode::OnRemoving() -{ - nsNavHistoryResultNode::OnRemoving(); - for (PRInt32 i = 0; i < mChildren.Count(); i ++) - mChildren[i]->OnRemoving(); - mChildren.Clear(); -} - - -// nsNavHistoryContainerResultNode::AreChildrenVisible -// -// Folders can't always depend on their mViewIndex value to determine if -// their children are visible because they can be root nodes. Root nodes -// are visible if a tree is attached to the result. - -PRBool -nsNavHistoryContainerResultNode::AreChildrenVisible() -{ - // can't see children when we're invisible - if (! mExpanded) - return PR_FALSE; - - // easy case, the node itself is visible - if (mViewIndex >= 0) - return PR_TRUE; - - nsNavHistoryResult* result = GetResult(); - if (! result) { - NS_NOTREACHED("Invalid result"); - return PR_FALSE; - } - if (result->mRootNode == this && result->mView) - return PR_TRUE; - return PR_FALSE; -} - - -// nsNavHistoryContainerResultNode::GetContainerOpen - -NS_IMETHODIMP -nsNavHistoryContainerResultNode::GetContainerOpen(PRBool *aContainerOpen) -{ - *aContainerOpen = mExpanded; - return NS_OK; -} - - -// nsNavHistoryContainerResultNode::SetContainerOpen - -NS_IMETHODIMP -nsNavHistoryContainerResultNode::SetContainerOpen(PRBool aContainerOpen) -{ - if (mExpanded && ! aContainerOpen) - CloseContainer(); - else if (! mExpanded && aContainerOpen) - OpenContainer(); - return NS_OK; -} - - -// nsNavHistoryContainerResultNode::OpenContainer -// -// This handles the generic container case. Other container types should -// override this to do their own handling. - -nsresult -nsNavHistoryContainerResultNode::OpenContainer() -{ - NS_ASSERTION(! mExpanded, "Container must be expanded to close it"); - mExpanded = PR_TRUE; - - /* Untested container API functions - if (! mRemoteContainerType.IsEmpty()) { - // remote container API may want to fill us - nsresult rv; - nsCOMPtr remote = do_GetService(mRemoteContainerType.get(), &rv); - if (NS_SUCCEEDED(rv)) { - remote->OnContainerOpening(this, GetGeneratingOptions()); - } else { - NS_WARNING("Unable to get remote container for "); - NS_WARNING(mRemoteContainerType.get()); - } - PRInt32 oldAccessCount = mAccessCount; - FillStats(); - ReverseUpdateStats(mAccessCount - oldAccessCount); - } - */ - - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - if (result->GetView()) - result->GetView()->ContainerOpened(this); - return NS_OK; -} - - -// nsNavHistoryContainerResultNode::CloseContainer -// -// Set aUpdateVisible to redraw the screen, this is the normal operation. -// This is set to false for the recursive calls since the root container -// that is being closed will handle recomputation of the visible elements -// for its entire subtree. - -nsresult -nsNavHistoryContainerResultNode::CloseContainer(PRBool aUpdateView) -{ - NS_ASSERTION(mExpanded, "Container must be expanded to close it"); - - // recursively close all child containers - for (PRInt32 i = 0; i < mChildren.Count(); i ++) { - if (mChildren[i]->IsContainer() && mChildren[i]->GetAsContainer()->mExpanded) - mChildren[i]->GetAsContainer()->CloseContainer(PR_FALSE); - } - - mExpanded = PR_FALSE; - - /* Untested remote container functions - nsresult rv; - if (! mRemoteContainerType.IsEmpty()) { - // notify remote containers that we are closing - nsCOMPtr remote = do_GetService(mRemoteContainerType.get(), &rv); - if (NS_SUCCEEDED(rv)) - remote->OnContainerClosed(this); - } - */ - - if (aUpdateView) { - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - if (result->GetView()) - result->GetView()->ContainerClosed(this); - } - return NS_OK; -} - - -// nsNavHistoryContainerResultNode::FillStats -// -// This builds up tree statistics from the bottom up. Call with a container -// and the indent level of that container. To init the full tree, call with -// the root container. The default indent level is -1, which is appropriate -// for the root level. -// -// CALL THIS AFTER FILLING ANY CONTAINER to update the parent and result -// node pointers, even if you don't care about visit counts and last visit -// dates. - -void -nsNavHistoryContainerResultNode::FillStats() -{ - mAccessCount = 0; - mTime = 0; - for (PRInt32 i = 0; i < mChildren.Count(); i ++) { - nsNavHistoryResultNode* node = mChildren[i]; - node->mParent = this; - node->mIndentLevel = mIndentLevel + 1; - if (node->IsContainer()) { - nsNavHistoryContainerResultNode* container = node->GetAsContainer(); - container->mResult = mResult; - container->FillStats(); - } - mAccessCount += node->mAccessCount; - if (node->mTime > mTime) - mTime = node->mTime; - } -} - - -// nsNavHistoryContainerResultNode::ReverseUpdateStats -// -// This is used when one container changes to do a minimal update of the -// tree structure. When something changes, you want to call FillStats if -// necessary and update this container completely. Then call this function -// which will walk up the tree and fill in the previous containers. -// -// Note that you have to tell us by how much our access count changed. Our -// access count should already be set to the new value; this is used to -// change the parents without having to re-count all their children. -// -// This does NOT update the last visit date downward. Therefore, if you are -// deleting a node that has the most recent last visit date, the parents -// will not get their last visit dates downshifted accordingly. This is a -// rather unusual case: we don't often delete things, and we usually don't -// even show the last visit date for folders. Updating would be slower -// because we would have to recompute it from scratch. - -void -nsNavHistoryContainerResultNode::ReverseUpdateStats(PRInt32 aAccessCountChange) -{ - if (mParent) { - mParent->mAccessCount += aAccessCountChange; - PRBool timeChanged = PR_FALSE; - if (mTime > mParent->mTime) { - timeChanged = PR_TRUE; - mParent->mTime = mTime; - } - - // check sorting, the stats may have caused this node to move if the - // sorting depended on something we are changing. - PRUint32 sortMode = mParent->GetSortType(); - PRBool resorted = PR_FALSE; - if (((sortMode == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING || - sortMode == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING) && - aAccessCountChange != 0) || - ((sortMode == nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING || - sortMode == nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING) && - timeChanged)) { - - SortComparator comparator = GetSortingComparator(sortMode); - int ourIndex = mParent->FindChild(this); - if (mParent->DoesChildNeedResorting(ourIndex, comparator)) { - // prevent us from being destroyed when removed from the parent - nsRefPtr ourLock = this; - nsNavHistoryContainerResultNode* ourParent = mParent; - - // Performance: moving items by removing and re-inserting is not very - // efficient because there may be a lot of unnecessary renumbering of - // items. I don't think the overhead is worth the extra complexity in - // this case. - ourParent->RemoveChildAt(ourIndex, PR_TRUE); - ourParent->InsertSortedChild(this, PR_TRUE); - resorted = PR_TRUE; - } - } - if (! resorted) { - // repaint visible rows - nsNavHistoryResult* result = GetResult(); - if (result && result->GetView()) { - result->GetView()->ItemChanged(NS_STATIC_CAST( - nsINavHistoryContainerResultNode*, mParent)); - } - } - - mParent->ReverseUpdateStats(aAccessCountChange); - } -} - - -// nsNavHistoryContainerResultNode::GetSortType -// -// This walks up the tree until we find a query result node or the root to -// get the sorting type. -// -// See nsNavHistoryQueryResultNode::GetSortType - -PRUint32 -nsNavHistoryContainerResultNode::GetSortType() -{ - if (mParent) - return mParent->GetSortType(); - else if (mResult) - return mResult->mSortingMode; - NS_NOTREACHED("We should always have a result"); - return nsINavHistoryQueryOptions::SORT_BY_NONE; -} - - -// nsNavHistoryContainerResultNode::GetSortingComparator -// -// Returns the sorting comparator function for the give sort type. -// RETURNS NULL if there is no comparator. - -nsNavHistoryContainerResultNode::SortComparator -nsNavHistoryContainerResultNode::GetSortingComparator(PRUint32 aSortType) -{ - switch (aSortType) - { - case nsINavHistoryQueryOptions::SORT_BY_NONE: - return &SortComparison_Bookmark; - case nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING: - return &SortComparison_TitleLess; - case nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING: - return &SortComparison_TitleGreater; - case nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING: - return &SortComparison_DateLess; - case nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING: - return &SortComparison_DateGreater; - case nsINavHistoryQueryOptions::SORT_BY_URI_ASCENDING: - return &SortComparison_URILess; - case nsINavHistoryQueryOptions::SORT_BY_URI_DESCENDING: - return &SortComparison_URIGreater; - case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING: - return &SortComparison_VisitCountLess; - case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING: - return &SortComparison_VisitCountGreater; - default: - NS_NOTREACHED("Bad sorting type"); - return nsnull; - } -} - - -// nsNavHistoryContainerResultNode::RecursiveSort -// -// This is used by Result::SetSortingMode and QueryResultNode::FillChildren to sort -// the child list. -// -// This does NOT update any visibility or tree information. The caller will -// have to completely rebuild the visible list after this. - -void -nsNavHistoryContainerResultNode::RecursiveSort( - nsICollation* aCollation, SortComparator aComparator) -{ - mChildren.Sort(aComparator, NS_STATIC_CAST(void*, aCollation)); - for (PRInt32 i = 0; i < mChildren.Count(); i ++) { - if (mChildren[i]->IsContainer()) - mChildren[i]->GetAsContainer()->RecursiveSort(aCollation, aComparator); - } -} - - -// nsNavHistoryContainerResultNode::FindInsertionPoint -// -// This returns the index that the given item would fall on if it were to -// be inserted using the given sorting. - -PRUint32 -nsNavHistoryContainerResultNode::FindInsertionPoint( - nsNavHistoryResultNode* aNode, SortComparator aComparator) -{ - if (mChildren.Count() == 0) - return 0; - - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, 0); - nsICollation* collation = history->GetCollation(); - - // The common case is the beginning or the end because this is used to insert - // new items that are added to history, which is usually sorted by date. - if (aComparator(aNode, mChildren[0], collation) <= 0) - return 0; - if (aComparator(aNode, mChildren[mChildren.Count() - 1], collation) >= 0) - return mChildren.Count(); - - PRUint32 beginRange = 0; // inclusive - PRUint32 endRange = mChildren.Count(); // exclusive - while (1) { - if (beginRange == endRange) - return endRange; - PRUint32 center = beginRange + (endRange - beginRange) / 2; - if (aComparator(aNode, mChildren[center], collation) <= 0) - endRange = center; // left side - else - beginRange = center + 1; // right site - } -} - - -// nsNavHistoryContainerResultNode::DoesChildNeedResorting -// -// This checks the child node at the given index to see if its sorting is -// correct. Returns true if not and it should be resorted. This is called -// when nodes are updated and we need to see whether we need to move it. - -PRBool -nsNavHistoryContainerResultNode::DoesChildNeedResorting(PRUint32 aIndex, - SortComparator aComparator) -{ - NS_ASSERTION(aIndex >= 0 && aIndex < PRUint32(mChildren.Count()), - "Input index out of range"); - if (mChildren.Count() == 1) - return PR_FALSE; - - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, 0); - nsICollation* collation = history->GetCollation(); - - if (aIndex > 0) { - // compare to previous item - if (aComparator(mChildren[aIndex - 1], mChildren[aIndex], collation) > 0) - return PR_TRUE; - } - if (aIndex < PRUint32(mChildren.Count()) - 1) { - // compare to next item - if (aComparator(mChildren[aIndex], mChildren[aIndex + 1], collation) > 0) - return PR_TRUE; - } - return PR_FALSE; -} - - -// nsNavHistoryContainerResultNode::SortComparison_Bookmark -// -// When there are bookmark indices, we should never have ties, so we don't -// need to worry about tiebreaking. When there are no bookmark indices, -// everything will be -1 and we don't worry about sorting. - -PRInt32 PR_CALLBACK nsNavHistoryContainerResultNode::SortComparison_Bookmark( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) -{ - return a->mBookmarkIndex - b->mBookmarkIndex; -} - - -// nsNavHistoryContainerResultNode::SortComparison_Title* -// -// These are a little more complicated because they do a localization -// conversion. If this is too slow, we can compute the sort keys once in -// advance, sort that array, and then reorder the real array accordingly. -// This would save some key generations. -// -// The collation object must be allocated before sorting on title! - -PRInt32 PR_CALLBACK nsNavHistoryContainerResultNode::SortComparison_TitleLess( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) -{ - PRUint32 aType, bType; - a->GetType(&aType); - b->GetType(&bType); - - if (aType != bType) { - return bType - aType; - } - - nsICollation* collation = NS_STATIC_CAST(nsICollation*, closure); - PRInt32 value = -1; // default to returning "true" on failure - collation->CompareString( - nsICollation::kCollationCaseInSensitive, - NS_ConvertUTF8toUTF16(a->mTitle), - NS_ConvertUTF8toUTF16(b->mTitle), &value); - if (value == 0) { - // resolve by URI - if (a->IsURI()) { - value = a->mURI.Compare(b->mURI.get()); - } - if (value == 0) { - // resolve by date - return ComparePRTime(a->mTime, b->mTime); - } - } - return value; -} -PRInt32 PR_CALLBACK nsNavHistoryContainerResultNode::SortComparison_TitleGreater( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) -{ - return -SortComparison_TitleLess(a, b, closure); -} - -// nsNavHistoryContainerResultNode::SortComparison_Date* -// -// Equal times will be very unusual, but it is important that there is some -// deterministic ordering of the results so they don't move around. - -PRInt32 PR_CALLBACK nsNavHistoryContainerResultNode::SortComparison_DateLess( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) -{ - PRInt32 value = ComparePRTime(a->mTime, b->mTime); - if (value == 0) { - nsICollation* collation = NS_STATIC_CAST(nsICollation*, closure); - collation->CompareString( - nsICollation::kCollationCaseInSensitive, - NS_ConvertUTF8toUTF16(a->mTitle), - NS_ConvertUTF8toUTF16(b->mTitle), &value); - } - return value; -} -PRInt32 PR_CALLBACK nsNavHistoryContainerResultNode::SortComparison_DateGreater( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) -{ - PRInt32 value = -ComparePRTime(a->mTime, b->mTime); - if (value == 0) { - nsICollation* collation = NS_STATIC_CAST(nsICollation*, closure); - collation->CompareString( - nsICollation::kCollationCaseInSensitive, - NS_ConvertUTF8toUTF16(a->mTitle), - NS_ConvertUTF8toUTF16(b->mTitle), &value); - value = -value; - } - return value; -} - - -// nsNavHistoryContainerResultNode::SortComparison_URI* -// -// Certain types of parent nodes are treated specially because URIs are not -// valid (like days or hosts). - -PRInt32 PR_CALLBACK nsNavHistoryContainerResultNode::SortComparison_URILess( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) -{ - PRUint32 aType, bType; - a->GetType(&aType); - b->GetType(&bType); - - if (aType != bType) { - // comparing different types - return bType - aType; - } - - PRInt32 value; - if (a->IsURI()) { - // normal URI or visit - value = a->mURI.Compare(b->mURI.get()); - } else { - // for everything else, use title (= host name) - nsICollation* collation = NS_STATIC_CAST(nsICollation*, closure); - collation->CompareString( - nsICollation::kCollationCaseInSensitive, - NS_ConvertUTF8toUTF16(a->mTitle), - NS_ConvertUTF8toUTF16(b->mTitle), &value); - } - - // resolve conflicts using date - if (value == 0) { - return ComparePRTime(a->mTime, b->mTime); - } - return value; -} -PRInt32 PR_CALLBACK nsNavHistoryContainerResultNode::SortComparison_URIGreater( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) -{ - return -SortComparison_URILess(a, b, closure); -} - - -// nsNavHistoryContainerResultNode::SortComparison_VisitCount* -// -// Fall back on dates for conflict resolution - -PRInt32 PR_CALLBACK nsNavHistoryContainerResultNode::SortComparison_VisitCountLess( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) -{ - PRInt32 value = CompareIntegers(a->mAccessCount, b->mAccessCount); - if (value == 0) - return ComparePRTime(a->mTime, b->mTime); - return value; -} -PRInt32 PR_CALLBACK nsNavHistoryContainerResultNode::SortComparison_VisitCountGreater( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure) -{ - PRInt32 value = -CompareIntegers(a->mAccessCount, b->mAccessCount); - if (value == 0) - return -ComparePRTime(a->mTime, b->mTime); - return value; -} - - -// nsNavHistoryContainerResultNode::FindChildURI -// -// Searches this folder for a node with the given URI. Returns null if not -// found. Does not addref the node! - -nsNavHistoryResultNode* -nsNavHistoryContainerResultNode::FindChildURI(const nsACString& aSpec, - PRUint32* aNodeIndex) -{ - for (PRInt32 i = 0; i < mChildren.Count(); i ++) { - if (mChildren[i]->IsURI()) { - if (aSpec.Equals(mChildren[i]->mURI)) { - *aNodeIndex = i; - return mChildren[i]; - } - } - } - return nsnull; -} - - -// nsNavHistoryContainerResultNode::FindChildFolder -// -// Searches this folder for the given subfolder. Returns null if not found. -// DOES NOT ADDREF. - -nsNavHistoryFolderResultNode* -nsNavHistoryContainerResultNode::FindChildFolder(PRInt64 aFolderId, - PRUint32* aNodeIndex) -{ - for (PRInt32 i = 0; i < mChildren.Count(); i ++) { - if (mChildren[i]->IsFolder()) { - nsNavHistoryFolderResultNode* folder = mChildren[i]->GetAsFolder(); - if (folder->mFolderId == aFolderId) { - *aNodeIndex = i; - return folder; - } - } - } - return nsnull; -} - - -// nsNavHistoryContainerResultNode::FindChildContainerByName -// -// Searches this container for a subfolder with the given name. This is used -// to find host and "day" nodes. Returns null if not found. DOES NOT ADDREF. - -nsNavHistoryContainerResultNode* -nsNavHistoryContainerResultNode::FindChildContainerByName( - const nsACString& aTitle, PRUint32* aNodeIndex) -{ - for (PRInt32 i = 0; i < mChildren.Count(); i ++) { - if (mChildren[i]->IsContainer()) { - nsNavHistoryContainerResultNode* container = - mChildren[i]->GetAsContainer(); - if (container->mTitle.Equals(aTitle)) { - *aNodeIndex = i; - return container; - } - } - } - return nsnull; -} - - -// nsNavHistoryContainerResultNode::InsertChildAt -// -// This does the work of adding a child to the container. This child can be -// a container, or a single item that may even be collapsed with the -// adjacent ones. -// -// Some inserts are "temporary" meaning that they are happening immediately -// after a temporary remove. We do this when movings elements when they -// change to keep them in the proper sorting position. In these cases, we -// don't need to recompute any statistics. - -nsresult -nsNavHistoryContainerResultNode::InsertChildAt(nsNavHistoryResultNode* aNode, - PRInt32 aIndex, - PRBool aIsTemporary) -{ - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - - aNode->mViewIndex = -1; - aNode->mParent = this; - aNode->mIndentLevel = mIndentLevel + 1; - if (! aIsTemporary && aNode->IsContainer()) { - // need to update all the new item's children - nsNavHistoryContainerResultNode* container = aNode->GetAsContainer(); - container->mResult = mResult; - container->FillStats(); - } - - if (! mChildren.InsertObjectAt(aNode, aIndex)) - return NS_ERROR_OUT_OF_MEMORY; - - // update our (the container) stats and refresh our row on the screen - if (! aIsTemporary) { - mAccessCount += aNode->mAccessCount; - if (mTime < aNode->mTime) - mTime = aNode->mTime; - if (result->GetView()) - result->GetView()->ItemChanged( - NS_STATIC_CAST(nsINavHistoryContainerResultNode*, this)); - ReverseUpdateStats(aNode->mAccessCount); - } - - // Update tree if we are visible. Note that we could be here and not expanded, - // like when there is a bookmark folder being updated because its parent is - // visible. - if (mExpanded && result->GetView()) - result->GetView()->ItemInserted(this, aNode, aIndex); - return NS_OK; -} - - -// nsNavHistoryContainerResultNode::InsertSortedChild -// -// This locates the proper place for insertion according to the current sort -// and calls InsertChildAt - -nsresult -nsNavHistoryContainerResultNode::InsertSortedChild( - nsNavHistoryResultNode* aNode, PRBool aIsTemporary) -{ - - if (mChildren.Count() == 0) - return InsertChildAt(aNode, 0, aIsTemporary); - - SortComparator comparator = GetSortingComparator(GetSortType()); - if (comparator) { - // When inserting a new node, it must have proper statistics because we use - // them to find the correct insertion point. The insert function will then - // recompute these statistics and fill in the proper parents and hierarchy - // level. Doing this twice shouldn't be a large performance penalty because - // when we are inserting new containers, they typically contain only one - // item (because we've browsed a new page). - if (! aIsTemporary && aNode->IsContainer()) - aNode->GetAsContainer()->FillStats(); - - return InsertChildAt(aNode, FindInsertionPoint(aNode, comparator), - aIsTemporary); - } - return InsertChildAt(aNode, mChildren.Count(), aIsTemporary); -} - - -// nsNavHistoryContainerResultNode::MergeResults -// -// This takes a fully grouped list of nodes and merges them into the -// current result set. Any containers that are added must already be -// sorted. -// -// This assumes that the items in 'aAddition' are new visits or -// replacement URIs. We do not update visits. -// -// PERFORMANCE: In the future, we can do more updates incrementally using -// this function. When a URI changes in a way we can't easily handle, -// construct a query with each query object specifying an exact match for -// the URI in question. Then remove all instances of that URI in the result -// and call this function. - -void -nsNavHistoryContainerResultNode::MergeResults( - nsCOMArray* aAddition) -{ - // Generally we will have very few (one) entries in the addition list, so - // just iterate through it. If we find we may have a lot, we may want to do - // some hashing to help with the merge. - for (PRUint32 i = 0; i < PRUint32(aAddition->Count()); i ++) { - nsNavHistoryResultNode* curAddition = (*aAddition)[i]; - if (curAddition->IsContainer()) { - PRUint32 containerIndex; - nsNavHistoryContainerResultNode* container = - FindChildContainerByName(curAddition->mTitle, &containerIndex); - if (container) { - // recursively merge with the existing container - container->MergeResults(&curAddition->GetAsContainer()->mChildren); - } else { - // need to add the new container to our result. - InsertSortedChild(curAddition); - } - } else { - if (curAddition->IsVisit()) { - // insert the new visit - InsertSortedChild(curAddition); - } else { - // add the URI, replacing a current one if any - PRUint32 oldIndex; - nsNavHistoryResultNode* oldNode = - FindChildURI(curAddition->mURI, &oldIndex); - if (oldNode) - ReplaceChildURIAt(oldIndex, curAddition); - else - InsertSortedChild(curAddition); - } - } - } -} - - -// nsNavHistoryContainerResultNode::ReplaceChildURIAt -// -// This is called to replace a leaf node. It will update tree stats -// and redraw the view if any. You can not use this to replace a container. -// -// This assumes that the node is being replaced with a newer version of -// itself and so its visit count will not go down. Also, this means that the -// collapsing of duplicates will not change. - -nsresult -nsNavHistoryContainerResultNode::ReplaceChildURIAt(PRUint32 aIndex, - nsNavHistoryResultNode* aNode) -{ - NS_ASSERTION(aIndex < PRUint32(mChildren.Count()), - "Invalid index for replacement"); - NS_ASSERTION(mChildren[aIndex]->IsURI(), - "Can not use ReplaceChildAt for a node of another type"); - NS_ASSERTION(mChildren[aIndex]->mURI.Equals(aNode->mURI), - "We must replace a URI with an updated one of the same"); - - aNode->mParent = this; - aNode->mIndentLevel = mIndentLevel + 1; - - // update tree stats if needed - PRUint32 accessCountChange = aNode->mAccessCount - mChildren[aIndex]->mAccessCount; - if (accessCountChange != 0 || mChildren[aIndex]->mTime != aNode->mTime) { - NS_ASSERTION(aNode->mAccessCount >= mChildren[aIndex]->mAccessCount, - "Replacing a node with one back in time or some nonmatching node"); - - mAccessCount += accessCountChange; - if (mTime < aNode->mTime) - mTime = aNode->mTime; - ReverseUpdateStats(accessCountChange); - } - - // Hold a reference so it doesn't go away as soon as we remove it from the - // array. This needs to be passed to the view. - nsCOMPtr oldItem = mChildren[aIndex]; - - // actually replace - if (! mChildren.ReplaceObjectAt(aNode, aIndex)) - return NS_ERROR_FAILURE; - - // update view - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - if (result->GetView()) - result->GetView()->ItemReplaced(this, oldItem, aNode, aIndex); - - mChildren[aIndex]->OnRemoving(); - return NS_OK; -} - - -// nsNavHistoryContainerResultNode::RemoveChildAt -// -// This does all the work of removing a child from this container, including -// updating the tree if necessary. Note that we do not need to be open for -// this to work. -// -// Some removes are "temporary" meaning that they'll just get inserted -// again. We do this for resorting. In these cases, we don't need to -// recompute any statistics, and we shouldn't notify those container that -// they are being removed. - -nsresult -nsNavHistoryContainerResultNode::RemoveChildAt(PRInt32 aIndex, - PRBool aIsTemporary) -{ - NS_ASSERTION(aIndex >= 0 && aIndex < mChildren.Count(), "Invalid index"); - - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - - // hold an owning reference to keep from expiring while we work with it - nsCOMPtr oldNode = mChildren[aIndex]; - - // stats - PRUint32 oldAccessCount = 0; - if (! aIsTemporary) { - oldAccessCount = mAccessCount; - mAccessCount -= mChildren[aIndex]->mAccessCount; - NS_ASSERTION(mAccessCount >= 0, "Invalid access count while updating!"); - } - - // remove from our list and notify the tree - mChildren.RemoveObjectAt(aIndex); - if (result->GetView()) - result->GetView()->ItemRemoved(this, oldNode, aIndex); - - if (! aIsTemporary) { - ReverseUpdateStats(mAccessCount - oldAccessCount); - oldNode->OnRemoving(); - } - return NS_OK; -} - - -// nsNavHistoryContainerResultNode::CanRemoteContainersChange -// -// Returns true if remote containers can manipulate the contents of this -// container. This is false for folders and queries, true for everything -// else. - -PRBool -nsNavHistoryContainerResultNode::CanRemoteContainersChange() -{ - return (mContainerType != nsNavHistoryResultNode::RESULT_TYPE_FOLDER && - mContainerType != nsNavHistoryResultNode::RESULT_TYPE_QUERY); -} - - -// nsNavHistoryContainerResultNode::RecursiveFindURIs -// -// This function searches for matches for the given URI. -// -// If aOnlyOne is set, it will terminate as soon as it finds a single match. -// This would be used when there are URI results so there will only ever be -// one copy of any URI. -// -// When aOnlyOne is false, it will check all elements. This is for visit -// style results that may have multiple copies of any given URI. - -void -nsNavHistoryContainerResultNode::RecursiveFindURIs(PRBool aOnlyOne, - nsNavHistoryContainerResultNode* aContainer, const nsCString& aSpec, - nsCOMArray* aMatches) -{ - for (PRInt32 child = 0; child < aContainer->mChildren.Count(); child ++) { - PRUint32 type; - aContainer->mChildren[child]->GetType(&type); - if (nsNavHistoryResultNode::IsTypeURI(type)) { - // compare URIs - nsNavHistoryResultNode* uriNode = aContainer->mChildren[child]; - if (uriNode->mURI.Equals(aSpec)) { - // found - aMatches->AppendObject(uriNode); - if (aOnlyOne) - return; - } - } else if (nsNavHistoryResultNode::IsTypeQuerySubcontainer(type)) { - // search into sub-containers - RecursiveFindURIs(aOnlyOne, aContainer->mChildren[child]->GetAsContainer(), - aSpec, aMatches); - if (aOnlyOne && aMatches->Count() > 0) - return; - } - } -} - - -// nsNavHistoryContainerResultNode::UpdateURIs -// -// If aUpdateSort is true, we will also update the sorting of this item. -// Normally you want this to be true, but it can be false if the thing you -// are changing can not affect sorting (like favicons). -// -// You should NOT change any child lists as part of the callback function. - -void -nsNavHistoryContainerResultNode::UpdateURIs(PRBool aRecursive, PRBool aOnlyOne, - PRBool aUpdateSort, const nsCString& aSpec, - void (*aCallback)(nsNavHistoryResultNode*,void*), void* aClosure) -{ - nsNavHistoryResult* result = GetResult(); - if (! result) { - NS_NOTREACHED("Must have a result for this query"); - return; - } - - // this needs to be owning since sometimes we remove and re-insert nodes - // in their parents and we don't want them to go away. - nsCOMArray matches; - - if (aRecursive) { - RecursiveFindURIs(aOnlyOne, this, aSpec, &matches); - } else if (aOnlyOne) { - PRUint32 nodeIndex; - nsNavHistoryResultNode* node = FindChildURI(aSpec, &nodeIndex); - if (node) - matches.AppendObject(node); - } else { - NS_NOTREACHED("UpdateURIs does not handle nonrecursive updates of multiple items."); - // this case easy to add if you need it, just find all the matching URIs - // at this level. However, this isn't currently used. History uses recursive, - // Bookmarks uses one level and knows that the match is unique. - return; - } - if (matches.Count() == 0) - return; - - SortComparator comparator = nsnull; - if (aUpdateSort) - comparator = GetSortingComparator(GetSortType()); - - // PERFORMANCE: This updates each container for each child in it that - // changes. In some cases, many elements have changed inside the same - // container. It would be better to compose a list of containers, and - // update each one only once for all the items that have changed in it. - for (PRInt32 i = 0; i < matches.Count(); i ++) - { - nsNavHistoryResultNode* node = matches[i]; - nsNavHistoryContainerResultNode* parent = node->mParent; - if (! parent) { - NS_NOTREACHED("All URI nodes being updated must have parents"); - continue; - } - PRUint32 oldAccessCount = node->mAccessCount; - PRTime oldTime = node->mTime; - aCallback(node, aClosure); - - if (oldAccessCount != node->mAccessCount || oldTime != node->mTime) { - // need to update/redraw the parent - parent->mAccessCount += node->mAccessCount - oldAccessCount; - if (node->mTime > parent->mTime) - parent->mTime = node->mTime; - if (result->GetView()) - result->GetView()->ItemChanged( - NS_STATIC_CAST(nsINavHistoryContainerResultNode*, parent)); - parent->ReverseUpdateStats(node->mAccessCount - oldAccessCount); - } - - if (aUpdateSort) { - PRInt32 childIndex = parent->FindChild(node); - if (childIndex >= 0 && parent->DoesChildNeedResorting(childIndex, comparator)) { - // child position changed - parent->RemoveChildAt(childIndex, PR_TRUE); - parent->InsertChildAt(node, parent->FindInsertionPoint(node, comparator), - PR_TRUE); - } else if (result->GetView()) { - result->GetView()->ItemChanged(node); - } - } else if (result->GetView()) { - result->GetView()->ItemChanged(node); - } - } -} - - -// nsNavHistoryContainerResultNode::ChangeTitles -// -// This is used to update the titles in the tree. This is called from both -// query and bookmark folder containers to update the tree. Bookmark folders -// should be sure to set recursive to false, since child folders will have -// their own callbacks registered. - -static void PR_CALLBACK setTitleCallback( - nsNavHistoryResultNode* aNode, void* aClosure) -{ - const nsACString* newTitle = NS_REINTERPRET_CAST(nsACString*, aClosure); - aNode->mTitle = *newTitle; -} -nsresult -nsNavHistoryContainerResultNode::ChangeTitles(nsIURI* aURI, - const nsACString& aNewTitle, - PRBool aRecursive, - PRBool aOnlyOne) -{ - // uri string - nsCAutoString uriString; - nsresult rv = aURI->GetSpec(uriString); - NS_ENSURE_SUCCESS(rv, rv); - - // The recursive function will update the result's tree nodes, but only if we - // give it a non-null pointer. So if there isn't a tree, just pass NULL so - // it doesn't bother trying to call the result. - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - - PRUint32 sortType = GetSortType(); - PRBool updateSorting = - (sortType == nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING || - sortType == nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING); - - UpdateURIs(aRecursive, aOnlyOne, updateSorting, uriString, - setTitleCallback, - NS_CONST_CAST(void*, NS_REINTERPRET_CAST(const void*, &aNewTitle))); - return NS_OK; -} - - -// nsNavHistoryContainerResultNode::GetHasChildren -// -// Complex containers (folders and queries) will override this. Here, we -// handle the case of simple containers (like host groups) where the children -// are always stored. - -NS_IMETHODIMP -nsNavHistoryContainerResultNode::GetHasChildren(PRBool *aHasChildren) -{ - *aHasChildren = (mChildren.Count() > 0); - return NS_OK; -} - - -// nsNavHistoryContainerResultNode::GetChildCount -// -// This function is only valid when the node is opened. - -NS_IMETHODIMP -nsNavHistoryContainerResultNode::GetChildCount(PRUint32* aChildCount) -{ - if (! mExpanded) - return NS_ERROR_NOT_AVAILABLE; - *aChildCount = mChildren.Count(); - return NS_OK; -} - - -// nsNavHistoryContainerResultNode::GetChild - -NS_IMETHODIMP -nsNavHistoryContainerResultNode::GetChild(PRUint32 aIndex, - nsINavHistoryResultNode** _retval) -{ - if (! mExpanded) - return NS_ERROR_NOT_AVAILABLE; - if (aIndex >= PRUint32(mChildren.Count())) - return NS_ERROR_INVALID_ARG; - NS_ADDREF(*_retval = mChildren[aIndex]); - return NS_OK; -} - - -// nsNavHistoryContainerResultNode::GetChildrenReadOnly -// -// Overridden for folders to query the bookmarks service directly. - -NS_IMETHODIMP -nsNavHistoryContainerResultNode::GetChildrenReadOnly(PRBool *aChildrenReadOnly) -{ - *aChildrenReadOnly = mChildrenReadOnly; - return NS_OK; -} - - -// nsNavHistoryContainerResultNode::GetRemoteContainerType - -NS_IMETHODIMP -nsNavHistoryContainerResultNode::GetRemoteContainerType( - nsACString& aRemoteContainerType) -{ - aRemoteContainerType = mRemoteContainerType; - return NS_OK; -} - - -// nsNavHistoryContainerResultNode::AppendURINode - -#if 0 // UNTESTED, commented out until it can be tested -NS_IMETHODIMP -nsNavHistoryContainerResultNode::AppendURINode( - const nsACString& aURI, const nsACString& aTitle, PRUint32 aAccessCount, - PRTime aTime, const nsACString& aIconURI, nsINavHistoryResultNode** _retval) -{ - *_retval = nsnull; - if (mRemoteContainerType.IsEmpty() || ! CanRemoteContainersChange()) - return NS_ERROR_INVALID_ARG; // we must be a remote container - - nsRefPtr result = - new nsNavHistoryResultNode(aURI, aTitle, aAccessCount, aTime, aIconURI); - NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); - - // append to our list - if (! mChildren.AppendObject(result)) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(*_retval = result); - return NS_OK; -} -#endif - - -// nsNavHistoryContainerResultNode::AppendVisitNode - -#if 0 // UNTESTED, commented out until it can be tested -NS_IMETHODIMP -nsNavHistoryContainerResultNode::AppendVisitNode( - const nsACString& aURI, const nsACString& aTitle, PRUint32 aAccessCount, - PRTime aTime, const nsACString& aIconURI, PRInt64 aSession, - nsINavHistoryVisitResultNode** _retval) -{ - *_retval = nsnull; - if (mRemoteContainerType.IsEmpty() || ! CanRemoteContainersChange()) - return NS_ERROR_INVALID_ARG; // we must be a remote container - - nsRefPtr result = - new nsNavHistoryVisitResultNode(aURI, aTitle, aAccessCount, aTime, - aIconURI, aSession); - NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); - - // append to our list - if (! mChildren.AppendObject(result)) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(*_retval = result); - return NS_OK; -} -#endif - - -// nsNavHistoryContainerResultNode::AppendFullVisitNode - -#if 0 // UNTESTED, commented out until it can be tested -NS_IMETHODIMP -nsNavHistoryContainerResultNode::AppendFullVisitNode( - const nsACString& aURI, const nsACString& aTitle, PRUint32 aAccessCount, - PRTime aTime, const nsACString& aIconURI, PRInt64 aSession, - PRInt64 aVisitId, PRInt64 aReferringVisitId, PRInt32 aTransitionType, - nsINavHistoryFullVisitResultNode** _retval) -{ - *_retval = nsnull; - if (mRemoteContainerType.IsEmpty() || ! CanRemoteContainersChange()) - return NS_ERROR_INVALID_ARG; // we must be a remote container - - nsRefPtr result = - new nsNavHistoryFullVisitResultNode(aURI, aTitle, aAccessCount, aTime, - aIconURI, aSession, aVisitId, - aReferringVisitId, aTransitionType); - NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); - - // append to our list - if (! mChildren.AppendObject(result)) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(*_retval = result); - return NS_OK; -} -#endif - - -// nsNavHistoryContainerResultNode::AppendContainerNode - -#if 0 // UNTESTED, commented out until it can be tested -NS_IMETHODIMP -nsNavHistoryContainerResultNode::AppendContainerNode( - const nsACString& aTitle, const nsACString& aIconURI, - PRUint32 aContainerType, const nsACString& aRemoteContainerType, - nsINavHistoryContainerResultNode** _retval) -{ - *_retval = nsnull; - if (mRemoteContainerType.IsEmpty() || ! CanRemoteContainersChange()) - return NS_ERROR_INVALID_ARG; // we must be a remote container - if (! IsTypeContainer(aContainerType) || IsTypeFolder(aContainerType) || - IsTypeQuery(aContainerType)) - return NS_ERROR_INVALID_ARG; // not proper container type - if (aContainerType == nsNavHistoryResultNode::RESULT_TYPE_REMOTE_CONTAINER && - aRemoteContainerType.IsEmpty()) - return NS_ERROR_INVALID_ARG; // remote containers must have r.c. type - if (aContainerType != nsNavHistoryResultNode::RESULT_TYPE_REMOTE_CONTAINER && - ! aRemoteContainerType.IsEmpty()) - return NS_ERROR_INVALID_ARG; // non-remote containers must NOT have r.c. type - - nsRefPtr result = - new nsNavHistoryContainerResultNode(EmptyCString(), aTitle, aIconURI, - aContainerType, PR_TRUE, - aRemoteContainerType); - NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); - - // append to our list - if (! mChildren.AppendObject(result)) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(*_retval = result); - return NS_OK; -} - - -// nsNavHistoryContainerResultNode::AppendQueryNode - -NS_IMETHODIMP -nsNavHistoryContainerResultNode::AppendQueryNode( - const nsACString& aQueryURI, const nsACString& aTitle, - const nsACString& aIconURI, nsINavHistoryQueryResultNode** _retval) -{ - *_retval = nsnull; - if (mRemoteContainerType.IsEmpty() || ! CanRemoteContainersChange()) - return NS_ERROR_INVALID_ARG; // we must be a remote container - - nsRefPtr result = - new nsNavHistoryQueryResultNode(aQueryURI, aTitle, aIconURI); - NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); - - // append to our list - if (! mChildren.AppendObject(result)) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(*_retval = result); - return NS_OK; -} -#endif - - -// nsNavHistoryContainerResultNode::AppendFolderNode - -#if 0 // UNTESTED, commented out until it can be tested -NS_IMETHODIMP -nsNavHistoryContainerResultNode::AppendFolderNode( - PRInt64 aFolderId, nsINavHistoryFolderResultNode** _retval) -{ - *_retval = nsnull; - if (mRemoteContainerType.IsEmpty() || ! CanRemoteContainersChange()) - return NS_ERROR_INVALID_ARG; // we must be a remote container - - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY); - - // create the node, it will be addrefed for us - nsRefPtr result; - nsresult rv = bookmarks->ResultNodeForFolder(aFolderId, - GetGeneratingOptions(), - getter_AddRefs(result)); - NS_ENSURE_SUCCESS(rv, rv); - - // append to our list - if (! mChildren.AppendObject(result)) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(*_retval = result->GetAsFolder()); - return NS_OK; -} -#endif - - -// nsNavHistoryContainerResultNode::ClearContents -// -// Used by the remote container API to clear this container - -#if 0 // UNTESTED, commented out until it can be tested -NS_IMETHODIMP -nsNavHistoryContainerResultNode::ClearContents() -{ - if (mRemoteContainerType.IsEmpty() || ! CanRemoteContainersChange()) - return NS_ERROR_INVALID_ARG; // we must be a remote container - - // we know if CanRemoteContainersChange() then we are a regular container - // and not a query or folder, so clearing doesn't need anything else to - // happen (like unregistering observers). Also, since this should only - // happen when the container is closed, we don't need to redraw the screen. - mChildren.Clear(); - - PRUint32 oldAccessCount = mAccessCount; - mAccessCount = 0; - mTime = 0; - ReverseUpdateStats(-PRInt32(oldAccessCount)); - return NS_OK; -} -#endif - - -// nsNavHistoryQueryResultNode ************************************************* -// -// HOW QUERY UPDATING WORKS -// -// Queries are different than bookmark folders in that we can not always -// do dynamic updates (easily) and updates are more expensive. Therefore, -// we do NOT query if we are not open and want to see if we have any children -// (for drawing a twisty) and always assume we will. -// -// When the container is opened, we execute the query and register the -// listeners. Like bookmark folders, we stay registered even when closed, -// and clear ourselves as soon as a message comes in. This lets us respond -// quickly if the user closes and reopens the container. -// -// We try to handle the most common notifications for the most common query -// types dynamically, that is, figuring out what should happen in response -// to a message without doing a requery. For complex changes or complex -// queries, we give up and requery. - -NS_IMPL_ISUPPORTS_INHERITED1(nsNavHistoryQueryResultNode, - nsNavHistoryContainerResultNode, - nsINavHistoryQueryResultNode) - -nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode( - const nsACString& aTitle, const nsACString& aIconURI, - const nsACString& aQueryURI) : - nsNavHistoryContainerResultNode(aQueryURI, aTitle, aIconURI, - nsNavHistoryResultNode::RESULT_TYPE_QUERY, - PR_TRUE, EmptyCString()), - mHasSearchTerms(PR_FALSE), - mContentsValid(PR_FALSE), - mBatchInProgress(PR_FALSE) -{ - // queries have special icons if not otherwise set - if (mFaviconURI.IsEmpty()) - mFaviconURI.AppendLiteral(ICONURI_QUERY); -} - -nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode( - const nsACString& aTitle, const nsACString& aIconURI, - const nsCOMArray& aQueries, - nsNavHistoryQueryOptions* aOptions) : - nsNavHistoryContainerResultNode(EmptyCString(), aTitle, aIconURI, - nsNavHistoryResultNode::RESULT_TYPE_QUERY, - PR_TRUE, EmptyCString()), - mQueries(aQueries), - mOptions(aOptions), - mContentsValid(PR_FALSE), - mBatchInProgress(PR_FALSE) -{ - NS_ASSERTION(aQueries.Count() > 0, "Must have at least one query"); - - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ASSERTION(history, "History service missing"); - mLiveUpdate = history->GetUpdateRequirements(mQueries, mOptions, - &mHasSearchTerms); - - // queries have special icons if not otherwise set - if (mFaviconURI.IsEmpty()) { - mFaviconURI.AppendLiteral(ICONURI_QUERY); - - // see if there's a favicon explicitly set on this query - nsFaviconService* faviconService = nsFaviconService::GetFaviconService(); - if (! faviconService) - return; - nsresult rv = VerifyQueriesSerialized(); - if (NS_FAILED(rv)) return; - - nsCOMPtr queryURI; - rv = NS_NewURI(getter_AddRefs(queryURI), mURI); - if (NS_FAILED(rv)) return; - nsCOMPtr favicon; - rv = faviconService->GetFaviconForPage(queryURI, getter_AddRefs(favicon)); - if (NS_FAILED(rv)) return; - favicon->GetSpec(mFaviconURI); - } -} - - -// nsNavHistoryQueryResultNode::CanExpand -// -// Whoever made us may want non-expanding queries. However, we always -// expand when we are the root node, or else asking for non-expanding -// queries would be useless. - -PRBool -nsNavHistoryQueryResultNode::CanExpand() -{ - nsNavHistoryQueryOptions* options = GetGeneratingOptions(); - if (options && options->ExpandQueries()) - return PR_TRUE; - if (mResult && mResult->mRootNode == this) - return PR_TRUE; - return PR_FALSE; -} - - -// nsNavHistoryQueryResultNode::OnRemoving -// -// Here we do not want to call ContainerResultNode::OnRemoving since our own -// ClearChildren will do the same thing and more (unregister the observers). -// The base ResultNode::OnRemoving will clear some regular node stats, so it -// is OK. - -void -nsNavHistoryQueryResultNode::OnRemoving() -{ - nsNavHistoryResultNode::OnRemoving(); - ClearChildren(PR_TRUE); -} - - -// nsNavHistoryQueryResultNode::OpenContainer -// -// Marks the container as open, rebuilding results if they are invalid. We -// may still have valid results if the container was previously open and -// nothing happened since closing it. -// -// We do not handle CloseContainer specially. The default one just marks the -// container as closed, but doesn't actually mark the results as invalid. -// The results will be invalidated by the next history or bookmark -// notification that comes in. This means if you open and close the item -// without anything happening in between, it will be fast (this actually -// happens when results are used as menus). - -nsresult -nsNavHistoryQueryResultNode::OpenContainer() -{ - NS_ASSERTION(! mExpanded, "Container must be expanded to close it"); - mExpanded = PR_TRUE; - if (! CanExpand()) - return NS_OK; - if (! mContentsValid) { - nsresult rv = FillChildren(); - NS_ENSURE_SUCCESS(rv, rv); - } - - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - if (result->GetView()) - result->GetView()->ContainerOpened( - NS_STATIC_CAST(nsNavHistoryContainerResultNode*, this)); - return NS_OK; -} - - -// nsNavHistoryQueryResultNode::GetHasChildren -// -// When we have valid results we can always give an exact answer. When we -// don't we just assume we'll have results, since actually doing the query -// might be hard. This is used to draw twisties on the tree, so precise -// results don't matter. - -NS_IMETHODIMP -nsNavHistoryQueryResultNode::GetHasChildren(PRBool* aHasChildren) -{ - if (! CanExpand()) { - *aHasChildren = PR_FALSE; - return NS_OK; - } - if (mContentsValid) { - *aHasChildren = (mChildren.Count() > 0); - return NS_OK; - } - *aHasChildren = PR_TRUE; - return NS_OK; -} - - -// nsNavHistoryQueryResultNode::GetUri -// -// This doesn't just return mURI because in the case of queries that may -// be lazily constructed from the query objects. - -NS_IMETHODIMP -nsNavHistoryQueryResultNode::GetUri(nsACString& aURI) -{ - nsresult rv = VerifyQueriesSerialized(); - NS_ENSURE_SUCCESS(rv, rv); - aURI = mURI; - return NS_OK; -} - - -// nsNavHistoryQueryResultNode::GetQueries - -NS_IMETHODIMP -nsNavHistoryQueryResultNode::GetQueries(PRUint32* queryCount, - nsINavHistoryQuery*** queries) -{ - nsresult rv = VerifyQueriesParsed(); - NS_ENSURE_SUCCESS(rv, rv); - NS_ASSERTION(mQueries.Count() > 0, "Must have >= 1 query"); - - *queries = NS_STATIC_CAST(nsINavHistoryQuery**, - nsMemory::Alloc(mQueries.Count() * sizeof(nsINavHistoryQuery*))); - NS_ENSURE_TRUE(*queries, NS_ERROR_OUT_OF_MEMORY); - - for (PRInt32 i = 0; i < mQueries.Count(); ++i) - NS_ADDREF((*queries)[i] = mQueries[i]); - *queryCount = mQueries.Count(); - return NS_OK; -} - - -// nsNavHistoryQueryResultNode::GetQueryOptions - -NS_IMETHODIMP -nsNavHistoryQueryResultNode::GetQueryOptions( - nsINavHistoryQueryOptions** aQueryOptions) -{ - nsresult rv = VerifyQueriesParsed(); - NS_ENSURE_SUCCESS(rv, rv); - NS_ASSERTION(mOptions, "Options invalid"); - - *aQueryOptions = mOptions; - NS_ADDREF(*aQueryOptions); - return NS_OK; -} - - -// nsNavHistoryQueryResultNode::VerifyQueriesParsed - -nsresult -nsNavHistoryQueryResultNode::VerifyQueriesParsed() -{ - if (mQueries.Count() > 0) { - NS_ASSERTION(mOptions, "If a result has queries, it also needs options"); - return NS_OK; - } - NS_ASSERTION(! mURI.IsEmpty(), - "Query nodes must have either a URI or query/options"); - - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); - - nsresult rv = history->QueryStringToQueryArray(mURI, &mQueries, - getter_AddRefs(mOptions)); - NS_ENSURE_SUCCESS(rv, rv); - - mLiveUpdate = history->GetUpdateRequirements(mQueries, mOptions, - &mHasSearchTerms); - return NS_OK; -} - - -// nsNavHistoryQueryResultNode::VerifyQueriesSerialized - -nsresult -nsNavHistoryQueryResultNode::VerifyQueriesSerialized() -{ - if (! mURI.IsEmpty()) { - return NS_OK; - } - NS_ASSERTION(mQueries.Count() > 0 && mOptions, - "Query nodes must have either a URI or query/options"); - - nsTArray flatQueries; - flatQueries.SetCapacity(mQueries.Count()); - for (PRInt32 i = 0; i < mQueries.Count(); i ++) - flatQueries.AppendElement(NS_STATIC_CAST(nsINavHistoryQuery*, - mQueries.ObjectAt(i))); - - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); - - nsresult rv = history->QueriesToQueryString(flatQueries.Elements(), - flatQueries.Length(), - mOptions, mURI); - NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(! mURI.IsEmpty(), NS_ERROR_FAILURE); - return NS_OK; -} - - -// nsNavHistoryQueryResultNode::FillChildren - -nsresult -nsNavHistoryQueryResultNode::FillChildren() -{ - NS_ASSERTION(! mContentsValid, - "Don't call FillChildren when contents are valid"); - NS_ASSERTION(mChildren.Count() == 0, - "We are trying to fill children when there already are some"); - - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); - - // get the results from the history service - nsresult rv = VerifyQueriesParsed(); - NS_ENSURE_SUCCESS(rv, rv); - rv = history->GetQueryResults(mQueries, mOptions, &mChildren); - NS_ENSURE_SUCCESS(rv, rv); - - // it is important to call FillStats to fill in the parents on all - // nodes and the result node pointers on the containers - FillStats(); - - // once we've computed all tree stats, we can sort, because containers will - // not have proper visit counts and dates - SortComparator comparator = GetSortingComparator(GetSortType()); - if (comparator) - RecursiveSort(history->GetCollation(), comparator); - - // register with the result for updates - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - result->AddEverythingObserver(this); - - mContentsValid = PR_TRUE; - return NS_OK; -} - - -// nsNavHistoryQueryResultNode::ClearChildren -// -// Call with unregister = false when we are going to update the children (for -// exmple, when the container is open). This will clear the list and notify -// all the children that they are going away. -// -// When the results are becoming invalid and we are not going to refresh -// them, set unregister = true, which will unregister the listener from the -// result if any. We use unregister = false when we are refreshing the list -// immediately so want to stay a notifier. - -void -nsNavHistoryQueryResultNode::ClearChildren(PRBool aUnregister) -{ - for (PRInt32 i = 0; i < mChildren.Count(); i ++) - mChildren[i]->OnRemoving(); - mChildren.Clear(); - - if (aUnregister && mContentsValid) { - nsNavHistoryResult* result = GetResult(); - if (result) - result->RemoveEverythingObserver(this); - } - mContentsValid = PR_FALSE; -} - - -// nsNavHistoryQueryResultNode::Refresh -// -// This is called to update the result when something has changed that we -// can not incrementally update. - -nsresult -nsNavHistoryQueryResultNode::Refresh() -{ - // Ignore refreshes when there is a batch, EndUpdateBatch will do a refresh - // to get all the changes. - if (mBatchInProgress) - return NS_OK; - - if (! mExpanded) { - // when we are not expanded, we don't update, just invalidate and unhook - ClearChildren(PR_TRUE); - return NS_OK; // no updates in tree state - } - - // ignore errors from FillChildren, since we will still want to refresh - // the tree (there just might not be anything in it on error) - ClearChildren(PR_FALSE); - FillChildren(); - - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - if (result->GetView()) - return result->GetView()->InvalidateContainer( - NS_STATIC_CAST(nsNavHistoryContainerResultNode*, this)); - return NS_OK; -} - - -// nsNavHistoryQueryResultNode::GetSortType -// -// Here, we override GetSortType to return the current sorting for this -// query. GetSortType is used when dynamically inserting query results so we -// can see which comparator we should use to find the proper insertion point -// (it shouldn't be called from folder containers which maintain their own -// sorting). -// -// Normally, the container just forwards it up the chain. This is what -// we want for host groups, for example. For queries, we often want to -// use the query's sorting mode. -// -// However, we only use this query node's sorting when it is not the root. -// When it is the root, we use the result's sorting mode, which is set -// according to the column headers in the tree view (if attached). This is -// because there are two cases: -// -// - You are looking at a bookmark hierarchy that contains an embedded -// result. We should always use the query's sort ordering since the result -// node's headers have nothing to do with us (and are disabled). -// -// - You are looking at a query in the tree. In this case, we want the -// result sorting to override ours (it should be initialized to the same -// sorting mode). -// -// It might seem like the column headers should set the sorting mode for the -// query in the result so we don't have to have this special case. But, the -// UI allows you to save the query in a different place or edit it, and just -// grabs the parameters and options from the query node. It would be weird -// to build a query, click a column header, and have the options you built -// up in the query builder be changed from under you. - -PRUint32 -nsNavHistoryQueryResultNode::GetSortType() -{ - if (mParent) { - // use our sorting, we are not the root - return mOptions->SortingMode(); - } else if (mResult) { - return mResult->mSortingMode; - } - NS_NOTREACHED("We should always have a result"); - return nsINavHistoryQueryOptions::SORT_BY_NONE; -} - - -// nsNavHistoryResultNode::OnBeginUpdateBatch - -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnBeginUpdateBatch() -{ - mBatchInProgress = PR_TRUE; - return NS_OK; -} - - -// nsNavHistoryResultNode::OnEndUpdateBatch -// -// Always requery when batches are done. These will typically involve large -// operations (currently delete) and it is likely more efficient to requery -// than to incrementally update as each message comes in. - -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnEndUpdateBatch() -{ - NS_ASSERTION(mBatchInProgress, "EndUpdateBatch without a begin"); - // must set to false before calling Refresh or Refresh will ignore us - mBatchInProgress = PR_FALSE; - return Refresh(); -} - - -// nsNavHistoryQueryResultNode::OnVisit -// -// Here we need to update all copies of the URI we have with the new visit -// count, and potentially add a new entry in our query. This is the most -// common update operation and it is important that it be as efficient as -// possible. - -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnVisit(nsIURI* aURI, PRInt64 aVisitId, - PRTime aTime, PRInt64 aSessionId, - PRInt64 aReferringId, - PRUint32 aTransitionType) -{ - // ignore everything during batches - if (mBatchInProgress) - return NS_OK; - - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); - - nsresult rv; - nsCOMPtr addition; - switch(mLiveUpdate) { - case QUERYUPDATE_TIME: { - // For these simple yet common cases we can check the time ourselves - // before doing the overhead of creating a new result node. - NS_ASSERTION(mQueries.Count() == 1, "Time updated queries can have only one object"); - nsCOMPtr query = do_QueryInterface(mQueries[0], &rv); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasIt; - query->GetHasBeginTime(&hasIt); - if (hasIt) { - PRTime beginTime = history->NormalizeTime(query->BeginTimeReference(), - query->BeginTime()); - if (aTime < beginTime) - return NS_OK; // before our time range - } - query->GetHasEndTime(&hasIt); - if (hasIt) { - PRTime endTime = history->NormalizeTime(query->EndTimeReference(), - query->EndTime()); - if (aTime > endTime) - return NS_OK; // after our time range - } - // now we know that our visit satisfies the time range, create a new node - rv = history->VisitIdToResultNode(aVisitId, mOptions, - getter_AddRefs(addition)); - NS_ENSURE_SUCCESS(rv, rv); - break; - } - case QUERYUPDATE_SIMPLE: { - // The history service can tell us whether the new item should appear - // in the result. We first have to construct a node for it to check. - rv = history->VisitIdToResultNode(aVisitId, mOptions, - getter_AddRefs(addition)); - NS_ENSURE_SUCCESS(rv, rv); - if (! history->EvaluateQueryForNode(mQueries, mOptions, addition)) - return NS_OK; // don't need to include in our query - break; - } - case QUERYUPDATE_COMPLEX: - case QUERYUPDATE_COMPLEX_WITH_BOOKMARKS: - // need to requery in complex cases - return Refresh(); - default: - NS_NOTREACHED("Invalid value for mLiveUpdate"); - return Refresh(); - } - - // NOTE: The dynamic updating never deletes any nodes. Sometimes it replaces - // URI nodes or adds visits, but never deletes old ones. - // - // The only time this might happen in the current implementation is if the - // title changes and it no longer matches a keyword search. This is not - // important enough to handle given that we don't do any other deleting. It - // is arguably more useful behavior anyway, since you're searching your - // history and the page used to match. - // - // When more queries are possible (show pages I've visited less than 5 times) - // this will be important to add. - - PRUint32 groupCount; - const PRUint32* groupings = mOptions->GroupingMode(&groupCount); - nsCOMArray grouped; - if (groupCount > 0) { - // feed this one node into the results grouper for this query to see where - // it should go in the results - nsCOMArray itemSource; - if (! itemSource.AppendObject(addition)) - return NS_ERROR_OUT_OF_MEMORY; - history->RecursiveGroup(itemSource, groupings, groupCount, &grouped); - } else { - // no grouping - if (! grouped.AppendObject(addition)) - return NS_ERROR_OUT_OF_MEMORY; - } - - MergeResults(&grouped); - return NS_OK; -} - - -// nsNavHistoryQueryResultNode::OnTitleChanged -// -// Find every node that matches this URI and rename it. We try to do -// incremental updates here, even when we are closed, because changing titles -// is easier than requerying if we are invalid. -// -// This actually gets called a lot. Typically, we will get an AddURI message -// when the user visits the page, and then the title will be set asynchronously -// when the title element of the page is parsed. - -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnTitleChanged(nsIURI* aURI, - const nsAString& aPageTitle, - const nsAString& aUserTitle, - PRBool aIsUserTitleChanged) -{ - if (mBatchInProgress) - return NS_OK; // ignore everything during batches - if (! mExpanded) { - // When we are not expanded, we don't update, just invalidate and unhook. - // It would still be pretty easy to traverse the results and update the - // titles, but when a title changes, its unlikely that it will be the only - // thing. Therefore, we just give up. - ClearChildren(PR_TRUE); - return NS_OK; // no updates in tree state - } - - // See if our queries have any keyword matching. In this case, we can't just - // replace the title on the items, but must redo the query. This could be - // handled more efficiently, but it is hard because keyword matching might - // match anything, including the title and other things. - if (mHasSearchTerms) { - return Refresh(); - } - - // compute what the new title should be - nsCAutoString newTitle; - if (mOptions->ForceOriginalTitle() || aUserTitle.IsVoid()) { - newTitle = NS_ConvertUTF16toUTF8(aPageTitle); - } else { - newTitle = NS_ConvertUTF16toUTF8(aUserTitle); - } - - PRBool onlyOneEntry = (mOptions->ResultType() == - nsINavHistoryQueryOptions::RESULTS_AS_URI); - return ChangeTitles(aURI, newTitle, PR_TRUE, onlyOneEntry); -} - - -// nsNavHistoryQueryResultNode::OnDeleteURI -// -// Here, we can always live update by just deleting all occurrences of -// the given URI. - -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnDeleteURI(nsIURI *aURI) -{ - PRBool onlyOneEntry = (mOptions->ResultType() == - nsINavHistoryQueryOptions::RESULTS_AS_URI); - nsCAutoString spec; - nsresult rv = aURI->GetSpec(spec); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMArray matches; - RecursiveFindURIs(onlyOneEntry, this, spec, &matches); - if (matches.Count() == 0) - return NS_OK; // not found - - for (PRInt32 i = 0; i < matches.Count(); i ++) { - nsNavHistoryResultNode* node = matches[i]; - nsNavHistoryContainerResultNode* parent = node->mParent; - NS_ASSERTION(parent, "URI nodes should always have parents"); - - PRInt32 childIndex = parent->FindChild(node); - NS_ASSERTION(childIndex >= 0, "Child not found in parent"); - parent->RemoveChildAt(childIndex); - - if (parent->mChildren.Count() == 0 && parent->IsQuerySubcontainer()) { - // when query subcontainers (like hosts) get empty we should remove them - // as well. Just append this to our list and it will get evaluated later - // in the loop. - matches.AppendObject(parent); - } - } - return NS_OK; -} - - -// nsNavHistoryQueryResultNode::OnClearHistory - -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnClearHistory() -{ - Refresh(); - return NS_OK; -} - - -// nsNavHistoryQueryResultNode::OnPageChanged -// - -static void PR_CALLBACK setFaviconCallback( - nsNavHistoryResultNode* aNode, void* aClosure) -{ - const nsCString* newFavicon = NS_STATIC_CAST(nsCString*, aClosure); - aNode->mFaviconURI = *newFavicon; -} -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnPageChanged(nsIURI *aURI, PRUint32 aWhat, - const nsAString &aValue) -{ - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - - nsCAutoString spec; - nsresult rv = aURI->GetSpec(spec); - NS_ENSURE_SUCCESS(rv, rv); - - switch (aWhat) { - case nsINavHistoryObserver::ATTRIBUTE_FAVICON: { - nsCString newFavicon = NS_ConvertUTF16toUTF8(aValue); - PRBool onlyOneEntry = (mOptions->ResultType() == - nsINavHistoryQueryOptions::RESULTS_AS_URI); - UpdateURIs(PR_TRUE, onlyOneEntry, PR_FALSE, spec, setFaviconCallback, - &newFavicon); - break; - } - default: - NS_WARNING("Unknown page changed notification"); - } - return NS_OK; -} - - -// nsNavHistoryQueryResultNode::OnPageExpired -// -// Do nothing. Perhaps we want to handle this case. If so, add the call to -// the result to enumerate the history observers. - -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnPageExpired(nsIURI* aURI, PRTime aVisitTime, - PRBool aWholeEntry) -{ - return NS_OK; -} - - -// nsNavHistoryQueryResultNode bookmark observers -// -// These are the bookmark observer functions for query nodes. They listen -// for bookmark events and refresh the results if we have any dependence on -// the bookmark system. - -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnItemAdded(nsIURI* aBookmark, PRInt64 aFolder, - PRInt32 aIndex) -{ - if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS) - return Refresh(); - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnItemRemoved(nsIURI* aBookmark, PRInt64 aFolder, - PRInt32 aIndex) -{ - if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS) - return Refresh(); - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnItemChanged(nsIURI* aBookmark, - const nsACString& aProperty, - const nsAString& aValue) -{ - NS_NOTREACHED("Everything observers should not get OnItemChanged, but should get the corresponding history notifications instead"); - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnItemVisited(nsIURI* aBookmark, PRInt64 aVisitId, - PRTime aTime) -{ - NS_NOTREACHED("Everything observers should not get OnItemVisited, but should get OnVisit instead"); - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnItemReplaced(PRInt64 aFolder, nsIURI *aOldItem, - nsIURI* aNewItem) -{ - if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS) - return Refresh(); - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnFolderAdded(PRInt64 aFolder, PRInt64 aParent, - PRInt32 aIndex) -{ - if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS) - return Refresh(); - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnFolderRemoved(PRInt64 aFolder, PRInt64 aParent, - PRInt32 aIndex) -{ - if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS) - return Refresh(); - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnFolderMoved(PRInt64 aFolder, PRInt64 aOldParent, - PRInt32 aOldIndex, PRInt64 aNewParent, - PRInt32 aNewIndex) -{ - if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS) - return Refresh(); - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnFolderChanged(PRInt64 aFolder, - const nsACString& property) -{ - if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS) - return Refresh(); - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnSeparatorAdded(PRInt64 aParent, PRInt32 aIndex) -{ - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryQueryResultNode::OnSeparatorRemoved(PRInt64 aParent, - PRInt32 aIndex) -{ - return NS_OK; -} - -// nsNavHistoryFolderResultNode ************************************************ -// -// HOW DYNAMIC FOLDER UPDATING WORKS -// -// When you create a result, it will automatically keep itself in sync with -// stuff that happens in the system. For folder nodes, this means changes -// to bookmarks. -// -// A folder will fill its children "when necessary." This means it is being -// opened or whether we need to see if it is empty for twisty drawing. It -// will then register its ID with the main result object that owns it. This -// result object will listen for all bookmark notifications and pass those -// notifications to folder nodes that have registered for that specific -// folder ID. -// -// When a bookmark folder is closed, it will not clear its children. Instead, -// it will keep them and also stay registered as a listener. This means that -// you can more quickly re-open the same folder without doing any work. This -// happens a lot for menus, and bookmarks don't change very often. -// -// When a message comes in and the folder is open, we will do the correct -// operations to keep ourselves in sync with the bookmark service. If the -// folder is closed, we just clear our list to mark it as invalid and -// unregister as a listener. This means we do not have to keep maintaining -// an up-to-date list for the entire bookmark menu structure in every place -// it is used. -// -// There is an additional wrinkle. If the result is attached to a treeview, -// and we invalidate but the folder itself is visible (its parent is open), -// we will immediately get asked if we are full. The folder will then query -// its children. Thus, the whole benefit of incremental updating is lost. -// Therefore, if we are in a treeview AND our parent is visible, we will -// keep incremental updating. - -NS_IMPL_ISUPPORTS_INHERITED2(nsNavHistoryFolderResultNode, - nsNavHistoryContainerResultNode, - nsINavHistoryQueryResultNode, - nsINavHistoryFolderResultNode) - -nsNavHistoryFolderResultNode::nsNavHistoryFolderResultNode( - const nsACString& aTitle, nsNavHistoryQueryOptions* aOptions, - PRInt64 aFolderId, const nsACString& aRemoteContainerType) : - nsNavHistoryContainerResultNode(EmptyCString(), aTitle, EmptyCString(), - nsNavHistoryResultNode::RESULT_TYPE_FOLDER, - PR_FALSE, aRemoteContainerType), - mContentsValid(PR_FALSE), - mOptions(aOptions), - mFolderId(aFolderId) -{ - // Get the favicon, if any, for this folder. Errors aren't too important - // here, so just give up if anything bad happens. - // - // PERFORMANCE: This is not very efficient, we may want to pass this as - // a parameter and integrate it into the query that generates the - // bookmark results. - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - if (! bookmarks) - return; - - // This is the folder URI used for the favicon. It IS NOT the folder URI - // that we will return from GetUri. That one will include the options for - // this specific query, while the folderURI is invariant w.r.t. options. - nsCOMPtr folderURI; - nsresult rv = bookmarks->GetFolderURI(aFolderId, getter_AddRefs(folderURI)); - if (NS_FAILED(rv)) - return; - - nsFaviconService* faviconService = nsFaviconService::GetFaviconService(); - if (! faviconService) - return; - nsCOMPtr favicon; - rv = faviconService->GetFaviconForPage(folderURI, getter_AddRefs(favicon)); - if (NS_FAILED(rv)) - return; // this will happen when there is no favicon (most common case) - - // we have a favicon, save it - favicon->GetSpec(mFaviconURI); -} - - -// nsNavHistoryFolderResultNode::OnRemoving -// -// Here we do not want to call ContainerResultNode::OnRemoving since our own -// ClearChildren will do the same thing and more (unregister the observers). -// The base ResultNode::OnRemoving will clear some regular node stats, so it -// is OK. - -void -nsNavHistoryFolderResultNode::OnRemoving() -{ - nsNavHistoryResultNode::OnRemoving(); - ClearChildren(PR_TRUE); -} - - -// nsNavHistoryFolderResultNode::OpenContainer -// -// See nsNavHistoryQueryResultNode::OpenContainer - -nsresult -nsNavHistoryFolderResultNode::OpenContainer() -{ - NS_ASSERTION(! mExpanded, "Container must be expanded to close it"); - nsresult rv; - - /* Untested container API functions - if (! mRemoteContainerType.IsEmpty()) { - // remote container API may want to change the bookmarks for this folder. - nsCOMPtr remote = do_GetService(mRemoteContainerType.get(), &rv); - if (NS_SUCCEEDED(rv)) { - remote->OnContainerOpening(NS_STATIC_CAST(nsINavHistoryFolderResultNode*, this), - mOptions); - } else { - NS_WARNING("Unable to get remote container for "); - NS_WARNING(mRemoteContainerType.get()); - } - } - */ - - if (! mContentsValid) { - rv = FillChildren(); - NS_ENSURE_SUCCESS(rv, rv); - } - mExpanded = PR_TRUE; - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - if (result->GetView()) - result->GetView()->ContainerOpened( - NS_STATIC_CAST(nsNavHistoryContainerResultNode*, this)); - return NS_OK; -} - - -// nsNavHistoryFolderResultNode::GetHasChildren -// -// See nsNavHistoryQueryResultNode::HasChildren. -// -// The semantics here are a little different. Querying the contents of a -// bookmark folder is relatively fast and it is common to have empty folders. -// Therefore, we always want to return the correct result so that twisties -// are drawn properly. - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::GetHasChildren(PRBool* aHasChildren) -{ - if (! mContentsValid) { - nsresult rv = FillChildren(); - NS_ENSURE_SUCCESS(rv, rv); - } - *aHasChildren = (mChildren.Count() > 0); - return NS_OK; -} - - -// nsNavHistoryFolderResultNode::GetChildrenReadOnly -// -// Here, we override the getter and ignore the value stored in our object. -// The bookmarks service can tell us whether this folder should be read-only -// or not. -// -// It would be nice to put this code in the folder constructor, but the -// database was complaining. I believe it is because most folders are -// created while enumerating the bookmarks table and having a statement -// open, and doing another statement might make it unhappy in some cases. - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::GetChildrenReadOnly(PRBool *aChildrenReadOnly) -{ - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_UNEXPECTED); - return bookmarks->GetFolderReadonly(mFolderId, aChildrenReadOnly); -} - - -// nsNavHistoryFolderResultNode::GetUri -// -// This lazily computes the URI for this specific folder query with -// the current options. - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::GetUri(nsACString& aURI) -{ - if (! mURI.IsEmpty()) { - aURI = mURI; - return NS_OK; - } - - PRUint32 queryCount; - nsINavHistoryQuery** queries; - nsresult rv = GetQueries(&queryCount, &queries); - NS_ENSURE_SUCCESS(rv, rv); - - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); - nsCAutoString queryString; - rv = history->QueriesToQueryString(queries, queryCount, mOptions, aURI); - nsMemory::Free(queries); - return rv; -} - - -// nsNavHistoryFolderResultNode::GetQueries -// -// This just returns the queries that give you this bookmarks folder - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::GetQueries(PRUint32* queryCount, - nsINavHistoryQuery*** queries) -{ - // get the query object - nsCOMPtr query; - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); - nsresult rv = history->GetNewQuery(getter_AddRefs(query)); - NS_ENSURE_SUCCESS(rv, rv); - - // query just has the folder ID set and nothing else - rv = query->SetFolders(&mFolderId, 1); - NS_ENSURE_SUCCESS(rv, rv); - - // make array of our 1 query - *queries = NS_STATIC_CAST(nsINavHistoryQuery**, - nsMemory::Alloc(sizeof(nsINavHistoryQuery*))); - if (! *queries) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF((*queries)[0] = query); - *queryCount = 1; - return NS_OK; -} - - -// nsNavHistoryFolderResultNode::GetQueryOptions -// -// Options for the query that gives you this bookmarks folder. This is just -// the options for the folder with the current folder ID set. - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::GetQueryOptions( - nsINavHistoryQueryOptions** aQueryOptions) -{ - NS_ASSERTION(mOptions, "Options invalid"); - - *aQueryOptions = mOptions; - NS_ADDREF(*aQueryOptions); - return NS_OK; -} - - -// nsNavHistoryFolderResultNode::FillChildren -// -// Call to fill the actual children of this folder. - -nsresult -nsNavHistoryFolderResultNode::FillChildren() -{ - NS_ASSERTION(! mContentsValid, - "Don't call FillChildren when contents are valid"); - NS_ASSERTION(mChildren.Count() == 0, - "We are trying to fill children when there already are some"); - - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY); - - // actually get the folder children from the bookmark service - nsresult rv = bookmarks->QueryFolderChildren(mFolderId, mOptions, &mChildren); - NS_ENSURE_SUCCESS(rv, rv); - - // PERFORMANCE: it may be better to also fill any child folders at this point - // so that we can draw tree twisties without doing a separate query later. If - // we don't end up drawing twisties a lot, it doesn't matter. If we do this, - // we should wrap everything in a transaction here on the bookmark service's - // connection. - - // it is important to call FillStats to fill in the parents on all - // nodes and the result node pointers on the containers - FillStats(); - - // register with the result for updates - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - result->AddBookmarkObserver(this, mFolderId); - - mContentsValid = PR_TRUE; - return NS_OK; -} - - -// nsNavHistoryFolderResultNode::ClearChildren -// -// See nsNavHistoryQueryResultNode::FillChildren - -void -nsNavHistoryFolderResultNode::ClearChildren(PRBool unregister) -{ - for (PRInt32 i = 0; i < mChildren.Count(); i ++) - mChildren[i]->OnRemoving(); - mChildren.Clear(); - - if (unregister && mContentsValid) { - nsNavHistoryResult* result = GetResult(); - if (result) - result->RemoveBookmarkObserver(this, mFolderId); - } - mContentsValid = PR_FALSE; -} - - -// nsNavHistoryFolderResultNode::Refresh -// -// This is called to update the result when something has changed that we -// can not incrementally update. - -nsresult -nsNavHistoryFolderResultNode::Refresh() -{ - if (! mExpanded) { - // when we are not expanded, we don't update, just invalidate and unhook - ClearChildren(PR_TRUE); - return NS_OK; // no updates in tree state - } - - // ignore errors from FillChildren, since we will still want to refresh - // the tree (there just might not be anything in it on error). Need to - // unregister as an observer because FillChildren will try to re-register us. - ClearChildren(PR_TRUE); - FillChildren(); - - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - if (result->GetView()) - return result->GetView()->InvalidateContainer( - NS_STATIC_CAST(nsNavHistoryContainerResultNode*, this)); - return NS_OK; -} - - -// nsNavHistoryFolderResultNode::StartIncrementalUpdate -// -// This implements the logic described above the constructor. This sees if -// we should do an incremental update and returns true if so. If not, it -// invalidates our children, unregisters us an observer, and returns false. - -PRBool -nsNavHistoryFolderResultNode::StartIncrementalUpdate() -{ - // if any items are excluded, we can not do incremental updates since the - // indices from the bookmark service will not be valid - if (! mOptions->ExcludeItems() && ! mOptions->ExcludeQueries() && - ! mOptions->ExcludeReadOnlyFolders()) { - - // easy case: we are visible, always do incremental update - if (mExpanded || AreChildrenVisible()) - return PR_TRUE; - - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, PR_FALSE); - - // when a tree is attached also do incremental updates if our parent is - // visible so that twisties are drawn correctly. - if (mParent && result->GetView()) - return PR_TRUE; - } - - // otherwise, we don't do incremental updates, invalidate and unregister - Refresh(); - return PR_FALSE; -} - - -// nsNavHistoryFolderResultNode::ReindexRange -// -// This function adds aDelta to all bookmark indices between the two -// endpoints, inclusive. It is used when items are added or removed from -// the bookmark folder. - -void -nsNavHistoryFolderResultNode::ReindexRange(PRInt32 aStartIndex, - PRInt32 aEndIndex, - PRInt32 aDelta) -{ - for (PRInt32 i = 0; i < mChildren.Count(); i ++) { - nsNavHistoryResultNode* node = mChildren[i]; - if (node->mBookmarkIndex >= aStartIndex && - node->mBookmarkIndex <= aEndIndex) - node->mBookmarkIndex += aDelta; - } -} - - -// nsNavHistoryFolderResultNode::OnBeginUpdateBatch (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::OnBeginUpdateBatch() -{ - return NS_OK; -} - - -// nsNavHistoryFolderResultNode::OnEndUpdateBatch (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::OnEndUpdateBatch() -{ - return NS_OK; -} - - -// nsNavHistoryFolderResultNode::OnItemAdded (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::OnItemAdded(nsIURI* aBookmark, PRInt64 aFolder, - PRInt32 aIndex) -{ - NS_ASSERTION(aFolder == mFolderId, "Got wrong bookmark update"); - if (mOptions->ExcludeItems()) { - // don't update items when we aren't displaying them, but we still need - // to adjust bookmark indices to account for the insertion - ReindexRange(aIndex, PR_INT32_MAX, 1); - return NS_OK; - } - - // here, try to do something reasonable if the bookmark service gives us - // a bogus index. - if (aIndex < 0) { - NS_NOTREACHED("Invalid index for item adding: <0"); - aIndex = 0; - } else if (aIndex > mChildren.Count()) { - NS_NOTREACHED("Invalid index for item adding: greater than count"); - aIndex = mChildren.Count(); - } - if (! StartIncrementalUpdate()) - return NS_OK; // folder was completely refreshed for us - - // adjust bookmark indices - ReindexRange(aIndex, PR_INT32_MAX, 1); - - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); - - nsNavHistoryResultNode* node; - nsresult rv = history->UriToResultNode(aBookmark, mOptions, &node); - NS_ENSURE_SUCCESS(rv, rv); - node->mBookmarkIndex = aIndex; - - if (GetSortType() == nsINavHistoryQueryOptions::SORT_BY_NONE) { - // insert at natural bookmarks position - return InsertChildAt(node, aIndex); - } - // insert at sorted position - return InsertSortedChild(node, PR_FALSE); -} - - -// nsNavHistoryFolderResultNode::OnItemRemoved (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::OnItemRemoved(nsIURI* aBookmark, PRInt64 aFolder, - PRInt32 aIndex) -{ - NS_ASSERTION(aFolder == mFolderId, "Got wrong bookmark update"); - if (mOptions->ExcludeItems()) { - // don't update items when we aren't displaying them, but we do need to - // adjust everybody's bookmark indices to account for the removal - ReindexRange(aIndex, PR_INT32_MAX, -1); - return NS_OK; - } - if (! StartIncrementalUpdate()) - return NS_OK; // folder was completely refreshed for us - - // shift all following indices down - ReindexRange(aIndex + 1, PR_INT32_MAX, -1); - - // don't trust the index from the bookmark service, find it ourselves. The - // sorting could be different, or the bookmark services indices and ours might - // be out of sync somehow. - PRUint32 nodeIndex; - nsNavHistoryResultNode* node = FindChildURI(aBookmark, &nodeIndex); - if (! node) - return NS_ERROR_FAILURE; // can't find it - - return RemoveChildAt(nodeIndex); -} - - -// nsNavHistoryFolderResultNode::OnItemChanged (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::OnItemChanged(nsIURI* aBookmark, - const nsACString& aProperty, - const nsAString& aValue) -{ - if (mOptions->ExcludeItems()) - return NS_OK; // don't update items when we aren't displaying them - if (! StartIncrementalUpdate()) - return NS_OK; - - // The changetitles function will handle all the updating and redrawing for - // us. We know there is only one match and that we don't need recursion since - // children with this item in it will be notified separately. - if (aProperty.EqualsLiteral("title")) { - return ChangeTitles(aBookmark, NS_ConvertUTF16toUTF8(aValue), - PR_FALSE, PR_TRUE); - } - - nsCAutoString spec; - nsresult rv = aBookmark->GetSpec(spec); - NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 nodeIndex; - nsNavHistoryResultNode* node = FindChildURI(aBookmark, &nodeIndex); - if (! node) - return NS_ERROR_FAILURE; - - if (aProperty.EqualsLiteral("favicon")) { - node->mFaviconURI = NS_ConvertUTF16toUTF8(aValue); - } else if (aProperty.EqualsLiteral("cleartime")) { - node->mTime = 0; - } else { - NS_NOTREACHED("Unknown bookmark property changing."); - } - - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - if (result->GetView()) - result->GetView()->ItemChanged(node); - return NS_OK; -} - - -// nsNavHistoryFolderResultNode::OnItemVisited (nsINavBookmarkObserver) -// -// Update visit count and last visit time and refresh. - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::OnItemVisited(nsIURI* aBookmark, PRInt64 aVisitId, - PRTime aTime) -{ - if (mOptions->ExcludeItems()) - return NS_OK; // don't update items when we aren't displaying them - if (! StartIncrementalUpdate()) - return NS_OK; - - PRUint32 nodeIndex; - nsNavHistoryResultNode* node = FindChildURI(aBookmark, &nodeIndex); - if (! node) - return NS_ERROR_FAILURE; - - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - - // update node - node->mTime = aTime; - node->mAccessCount ++; - - // update us - PRInt32 oldAccessCount = mAccessCount; - mAccessCount ++; - if (aTime > mTime) - mTime = aTime; - ReverseUpdateStats(mAccessCount - oldAccessCount); - - // update sorting if necessary - PRUint32 sortType = GetSortType(); - if (sortType == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING || - sortType == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING || - sortType == nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING || - sortType == nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING) { - PRInt32 childIndex = FindChild(node); - NS_ASSERTION(childIndex >= 0, "Could not find child we just got a reference to"); - if (childIndex >= 0) { - SortComparator comparator = GetSortingComparator(GetSortType()); - nsCOMPtr nodeLock(node); - RemoveChildAt(childIndex, PR_TRUE); - InsertChildAt(node, FindInsertionPoint(node, comparator), PR_TRUE); - } - } else if (result->GetView()) { - // no sorting changed, just redraw the row if visible - result->GetView()->ItemChanged(node); - } - return NS_OK; -} - - -// nsNavHistoryFolderResultNode::OnItemReplaced (nsINavBookmarkObserver) -// -// This is uncommon so we won't waste a lot of effort on this. Just do -// a remove and add. - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::OnItemReplaced(PRInt64 aFolder, nsIURI *aOldItem, - nsIURI* aNewItem) -{ - NS_ASSERTION(aFolder == mFolderId, "Got wrong bookmark update"); - if (mOptions->ExcludeItems()) - return NS_OK; // don't update items when we aren't displaying them - if (! StartIncrementalUpdate()) - return NS_OK; - - PRUint32 nodeIndex; - nsNavHistoryResultNode* oldNode = FindChildURI(aOldItem, &nodeIndex); - if (! oldNode) { - NS_NOTREACHED("Bad bookmark message"); - return NS_ERROR_FAILURE; - } - - nsresult rv = OnItemRemoved(aOldItem, aFolder, nodeIndex); - NS_ENSURE_SUCCESS(rv, rv); - return OnItemAdded(aNewItem, aFolder, nodeIndex); -} - - -// nsNavHistoryFolderResultNode::OnFolderAdded (nsINavBookmarkObserver) -// -// Create the new folder and add it - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::OnFolderAdded(PRInt64 aFolder, PRInt64 aParent, - PRInt32 aIndex) -{ - NS_ASSERTION(aParent == mFolderId, "Got wrong bookmark update"); - if (! StartIncrementalUpdate()) - return NS_OK; // folder was completely refreshed for us - - // adjust indices to account for insertion - ReindexRange(aIndex, PR_INT32_MAX, 1); - - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY); - - nsNavHistoryResultNode* node; - nsresult rv = bookmarks->ResultNodeForFolder(aFolder, mOptions, &node); - NS_ENSURE_SUCCESS(rv, rv); - node->mBookmarkIndex = aIndex; - - if (GetSortType() == nsINavHistoryQueryOptions::SORT_BY_NONE) { - // insert at natural bookmarks position - return InsertChildAt(node, aIndex); - } - // insert at sorted position - return InsertSortedChild(node, PR_FALSE); -} - - -// nsNavHistoryFolderResultNode::OnFolderRemoved (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::OnFolderRemoved(PRInt64 aFolder, PRInt64 aParent, - PRInt32 aIndex) -{ - // We only care about notifications when a child changes. When the deleted - // folder is us, our parent should also be registered and will remove us from - // its list. - if (mFolderId == aFolder) - return NS_OK; - - NS_ASSERTION(aParent == mFolderId, "Got wrong bookmark update"); - if (! StartIncrementalUpdate()) - return NS_OK; // we are completely refreshed - - // update indices - ReindexRange(aIndex + 1, PR_INT32_MAX, -1); - - PRUint32 index; - nsNavHistoryFolderResultNode* node = FindChildFolder(aFolder, &index); - if (! node) { - NS_NOTREACHED("Removing folder we don't have"); - return NS_ERROR_FAILURE; - } - return RemoveChildAt(index); -} - - -// nsNavHistoryFolderResultNode::OnFolderMoved (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::OnFolderMoved(PRInt64 aFolder, PRInt64 aOldParent, - PRInt32 aOldIndex, PRInt64 aNewParent, - PRInt32 aNewIndex) -{ - NS_ASSERTION(aOldParent == mFolderId || aNewParent == mFolderId, - "Got a bookmark message that doesn't belong to us"); - if (! StartIncrementalUpdate()) - return NS_OK; // entire container was refreshed for us - - if (aOldParent == aNewParent) { - // getting moved within the same folder, we don't want to do a remove and - // an add because that will lose your tree state. - - // adjust bookmark indices - ReindexRange(aOldIndex + 1, PR_INT32_MAX, -1); - ReindexRange(aNewIndex, PR_INT32_MAX, 1); - - PRUint32 index; - nsNavHistoryFolderResultNode* node = FindChildFolder(aFolder, &index); - if (! node) { - NS_NOTREACHED("Can't find folder that is moving!"); - return NS_ERROR_FAILURE; - } - NS_ASSERTION(index >= 0 && index < PRUint32(mChildren.Count()), - "Invalid index!"); - node->mBookmarkIndex = aNewIndex; - - // adjust position - PRInt32 sortType = GetSortType(); - SortComparator comparator = GetSortingComparator(sortType); - if (DoesChildNeedResorting(index, comparator)) { - // needs resorting, this will cause everything to be redrawn, so we - // don't need to do that explicitly later. - nsRefPtr lock(node); - RemoveChildAt(index, PR_TRUE); - InsertChildAt(node, FindInsertionPoint(node, comparator), PR_TRUE); - return NS_OK; - } - - } else { - // moving between two different folders, just do a remove and an add - if (aOldParent == mFolderId) - OnFolderRemoved(aFolder, aOldParent, aOldIndex); - if (aNewParent == mFolderId) - OnFolderAdded(aFolder, aNewParent, aNewIndex); - } - return NS_OK; -} - - -// nsNavHistoryFolderResultNode::OnFolderChanged (nsINavBookmarkObserver) -// -// Unlike some of the other notifications that are sent to the parent of -// the item that's changing, this one is sent to the folder that's -// changing. - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::OnFolderChanged(PRInt64 aFolder, - const nsACString& property) -{ - // Do NOT call StartIncrementalUpdate here. That call is not appropriate - // because it assumes a child has changed. Here, our folder itself is - // changing. Updating a folder is very easy and isn't affected by filtering, - // so we can always do incremental updates. - - if (property.EqualsLiteral("title")) { - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY); - - nsAutoString title; - bookmarks->GetFolderTitle(mFolderId, title); - mTitle = NS_ConvertUTF16toUTF8(title); - - PRInt32 sortType = GetSortType(); - if ((sortType == nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING || - sortType == nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING) && - mParent) { - PRInt32 ourIndex = mParent->FindChild(this); - SortComparator comparator = GetSortingComparator(sortType); - if (mParent->DoesChildNeedResorting(ourIndex, comparator)) { - // needs resorting, this will cause everything to be redrawn, so we - // don't need to do that explicitly later. - mParent->RemoveChildAt(ourIndex, PR_TRUE); - mParent->InsertChildAt(this, mParent->FindInsertionPoint(this, comparator), - PR_TRUE); - return NS_OK; - } - } - } else { - NS_NOTREACHED("Unknown folder change event"); - return NS_ERROR_FAILURE; - } - - // update folder if visible - nsNavHistoryResult* result = GetResult(); - NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); - if (result->GetView()) - result->GetView()->ItemChanged( - NS_STATIC_CAST(nsNavHistoryResultNode*, this)); - return NS_OK; -} - - -// nsNavHistoryFolderResultNode::OnSeparatorAdded (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::OnSeparatorAdded(PRInt64 aParent, PRInt32 aIndex) -{ - NS_ASSERTION(aParent == mFolderId, "Got wrong bookmark update"); - if (mOptions->ExcludeItems()) { - // don't update items when we aren't displaying them, except we need to - // update the indices - ReindexRange(aIndex, PR_INT32_MAX, 1); - return NS_OK; - } - if (! StartIncrementalUpdate()) - return NS_OK; // folder is completely refreshed for us - - // adjust indices - ReindexRange(aIndex, PR_INT32_MAX, 1); - - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY); - - nsNavHistoryResultNode* node = new nsNavHistorySeparatorResultNode(); - NS_ENSURE_TRUE(node, NS_ERROR_OUT_OF_MEMORY); - node->mBookmarkIndex = aIndex; - - return InsertChildAt(node, aIndex); -} - -// nsNavHistoryFolderResultNode::OnSeparatorRemoved (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryFolderResultNode::OnSeparatorRemoved(PRInt64 aParent, - PRInt32 aIndex) -{ - NS_ASSERTION(aParent == mFolderId, "Got wrong bookmark update"); - if (mOptions->ExcludeItems()) { - // don't update items when we aren't displaying them, except we need to - // update everybody's indices - ReindexRange(aIndex, PR_INT32_MAX, -1); - return NS_OK; - } - if (! StartIncrementalUpdate()) - return NS_OK; // folder is completely refreshed for us - - ReindexRange(aIndex, PR_INT32_MAX, -1); - - if (aIndex >= mChildren.Count()) { - NS_NOTREACHED("Removing separator at invalid index"); - return NS_OK; - } - - if (!mChildren[aIndex]->IsSeparator()) { - NS_NOTREACHED("OnSeparatorRemoved called for a non-separator node"); - return NS_OK; - } - - return RemoveChildAt(aIndex); -} - - -// nsNavHistorySeparatorResultNode -// -// Separator nodes do not hold any data - -nsNavHistorySeparatorResultNode::nsNavHistorySeparatorResultNode() - : nsNavHistoryResultNode(EmptyCString(), EmptyCString(), - 0, 0, EmptyCString()) -{ -} - -// nsNavHistoryResult ********************************************************** - -NS_IMPL_ADDREF(nsNavHistoryResult) -NS_IMPL_RELEASE(nsNavHistoryResult) - -NS_INTERFACE_MAP_BEGIN(nsNavHistoryResult) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsINavHistoryResult) - NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsNavHistoryResult) - NS_INTERFACE_MAP_ENTRY(nsINavHistoryResult) - NS_INTERFACE_MAP_ENTRY(nsINavBookmarkObserver) - NS_INTERFACE_MAP_ENTRY(nsINavHistoryObserver) - NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) -NS_INTERFACE_MAP_END - -nsNavHistoryResult::nsNavHistoryResult(nsNavHistoryContainerResultNode* aRoot) : - mRootNode(aRoot), - mIsHistoryObserver(PR_FALSE), - mIsBookmarksObserver(PR_FALSE) -{ - mRootNode->mResult = this; -} - -PR_STATIC_CALLBACK(PLDHashOperator) -RemoveBookmarkObserversCallback(nsTrimInt64HashKey::KeyType aKey, - nsNavHistoryResult::FolderObserverList*& aData, - void* userArg) -{ - delete aData; - return PL_DHASH_REMOVE; -} - -nsNavHistoryResult::~nsNavHistoryResult() -{ - // delete all bookmark observer arrays which are allocated on the heap - mBookmarkObservers.Enumerate(&RemoveBookmarkObserversCallback, nsnull); -} - - -// nsNavHistoryResult::Init -// -// Call AddRef before this, since we may do things like register ourselves. - -nsresult -nsNavHistoryResult::Init(nsINavHistoryQuery** aQueries, - PRUint32 aQueryCount, - nsNavHistoryQueryOptions *aOptions) -{ - nsresult rv; - NS_ASSERTION(aOptions, "Must have valid options"); - NS_ASSERTION(aQueries && aQueryCount > 0, "Must have >1 query in result"); - - // Fill saved source queries with copies of the original (the caller might - // change their original objects, and we always want to reflect the source - // parameters). - for (PRUint32 i = 0; i < aQueryCount; i ++) { - nsCOMPtr queryClone; - rv = aQueries[i]->Clone(getter_AddRefs(queryClone)); - NS_ENSURE_SUCCESS(rv, rv); - if (!mQueries.AppendObject(queryClone)) - return NS_ERROR_OUT_OF_MEMORY; - } - rv = aOptions->Clone(getter_AddRefs(mOptions)); - mSortingMode = aOptions->SortingMode(); - NS_ENSURE_SUCCESS(rv, rv); - - mPropertyBags.Init(); - if (! mBookmarkObservers.Init(128)) - return NS_ERROR_OUT_OF_MEMORY; - - NS_ASSERTION(mRootNode->mIndentLevel == -1, - "Root node's indent level initialized wrong"); - mRootNode->FillStats(); - - return NS_OK; -} - - -// nsNavHistoryResult::NewHistoryResult -// -// Constructs a new history result object. - -nsresult // static -nsNavHistoryResult::NewHistoryResult(nsINavHistoryQuery** aQueries, - PRUint32 aQueryCount, - nsNavHistoryQueryOptions* aOptions, - nsNavHistoryContainerResultNode* aRoot, - nsNavHistoryResult** result) -{ - *result = new nsNavHistoryResult(aRoot); - if (! *result) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(*result); // must happen before Init - nsresult rv = (*result)->Init(aQueries, aQueryCount, aOptions); - if (NS_FAILED(rv)) { - NS_RELEASE(*result); - *result = nsnull; - return rv; - } - return NS_OK; -} - - -// nsNavHistoryResult::PropertyBagFor -// -// Given a pointer to a result node, this will give you the property bag -// corresponding to it. Each node exposes a property bag to be used to -// store temporary data. It is designed primarily for those implementing -// container sources for storing data they need. -// -// Since we expect very few result nodes will ever have their property bags -// used, and since we can have a LOT of result nodes, we store the property -// bags separately in a hash table in the parent result. -// -// This function is called by a node when somebody wants the property bag. -// It will create a property bag if necessary and store it for later -// retrieval. - -nsresult -nsNavHistoryResult::PropertyBagFor(nsISupports* aObject, - nsIWritablePropertyBag** aBag) -{ - *aBag = nsnull; - if (mPropertyBags.Get(aObject, aBag) && *aBag) - return NS_OK; - - nsresult rv = NS_NewHashPropertyBag(aBag); - NS_ENSURE_SUCCESS(rv, rv); - if (! mPropertyBags.Put(aObject, *aBag)) { - NS_RELEASE(*aBag); - *aBag = nsnull; - return NS_ERROR_OUT_OF_MEMORY; - } - return NS_OK; -} - - - - -// nsNavHistoryResult::AddEverythingObserver - -void -nsNavHistoryResult::AddEverythingObserver(nsNavHistoryQueryResultNode* aNode) -{ - if (! mIsHistoryObserver) { - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ASSERTION(history, "Can't create history service"); - history->AddObserver(this, PR_TRUE); - mIsHistoryObserver = PR_TRUE; - } - if (mEverythingObservers.IndexOf(aNode) != mEverythingObservers.NoIndex) { - NS_NOTREACHED("Attempting to register an observer twice!"); - return; - } - mEverythingObservers.AppendElement(aNode); -} - - -// nsNavHistoryResult::AddBookmarkObserver -// -// Here, we also attach as a bookmark service observer if necessary - -void -nsNavHistoryResult::AddBookmarkObserver(nsNavHistoryFolderResultNode* aNode, - PRInt64 aFolder) -{ - if (! mIsBookmarksObserver) { - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - if (! bookmarks) { - NS_NOTREACHED("Can't create bookmark service"); - return; - } - bookmarks->AddObserver(this, PR_TRUE); - mIsBookmarksObserver = PR_TRUE; - } - FolderObserverList* list = BookmarkObserversForId(aFolder, PR_TRUE); - if (list->IndexOf(aNode) != list->NoIndex) { - NS_NOTREACHED("Attempting to register an observer twice!"); - return; - } - list->AppendElement(aNode); -} - - -// nsNavHistoryResult::RemoveEverythingObserver - -void -nsNavHistoryResult::RemoveEverythingObserver(nsNavHistoryQueryResultNode* aNode) -{ - mEverythingObservers.RemoveElement(aNode); -} - - -// nsNavHistoryResult::RemoveBookmarkObserver - -void -nsNavHistoryResult::RemoveBookmarkObserver(nsNavHistoryFolderResultNode* aNode, - PRInt64 aFolder) -{ - FolderObserverList* list = BookmarkObserversForId(aFolder, PR_FALSE); - if (! list) - return; // we don't even have an entry for that folder - list->RemoveElement(aNode); -} - - -// nsNavHistoryResult::BookmarkObserversForId - -nsNavHistoryResult::FolderObserverList* -nsNavHistoryResult::BookmarkObserversForId(PRInt64 aFolderId, PRBool aCreate) -{ - FolderObserverList* list; - if (mBookmarkObservers.Get(aFolderId, &list)) - return list; - if (! aCreate) - return nsnull; - - // need to create a new list - list = new FolderObserverList; - mBookmarkObservers.Put(aFolderId, list); - return list; -} - -// nsNavHistoryResult::GetSortingMode (nsINavHistoryResult) - -NS_IMETHODIMP -nsNavHistoryResult::GetSortingMode(PRUint32* aSortingMode) -{ - *aSortingMode = mSortingMode; - return NS_OK; -} - -// nsNavHistoryResult::SetSortingMode (nsINavHistoryResult) - -NS_IMETHODIMP -nsNavHistoryResult::SetSortingMode(PRUint32 aSortingMode) -{ - if (aSortingMode > nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING) - return NS_ERROR_INVALID_ARG; - if (! mRootNode) - return NS_ERROR_FAILURE; - - // keep everything in sync - NS_ASSERTION(mOptions, "Options should always be present for a root query"); - - mSortingMode = aSortingMode; - - // actually do sorting - nsNavHistoryContainerResultNode::SortComparator comparator = - nsNavHistoryContainerResultNode::GetSortingComparator(aSortingMode); - if (comparator) { - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); - mRootNode->RecursiveSort(history->GetCollation(), comparator); - } - - if (mView) { - mView->SortingChanged(aSortingMode); - mView->InvalidateAll(); - } - return NS_OK; -} - - -// nsNavHistoryResult::Viewer (nsINavHistoryResult) - -NS_IMETHODIMP -nsNavHistoryResult::GetViewer(nsINavHistoryResultViewer** aViewer) -{ - *aViewer = mView; - NS_IF_ADDREF(*aViewer); - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryResult::SetViewer(nsINavHistoryResultViewer* aViewer) -{ - mView = aViewer; - if (aViewer) - aViewer->SetResult(this); - return NS_OK; -} - - -// nsNavHistoryResult::GetRoot (nsINavHistoryResult) -// -// We have a pointer to a container, but it will either be a folder or -// query node, both of which QI to QueryResultNode (even though folder -// does not inherit from a concrete query). - -NS_IMETHODIMP -nsNavHistoryResult::GetRoot(nsINavHistoryQueryResultNode** aRoot) -{ - if (! mRootNode) { - NS_NOTREACHED("Root is null"); - *aRoot = nsnull; - return NS_ERROR_FAILURE; - } - return mRootNode->QueryInterface(NS_GET_IID(nsINavHistoryQueryResultNode), - NS_REINTERPRET_CAST(void**, aRoot)); -} - - -// nsINavBookmarkObserver implementation - -// Here, it is important that we create a COPY of the observer array. Some -// observers will requery themselves, which may cause the observer array to -// be modified or added to. -#define ENUMERATE_BOOKMARK_OBSERVERS_FOR_FOLDER(_folderId, _functionCall) \ - { \ - FolderObserverList* _fol = BookmarkObserversForId(_folderId, PR_FALSE); \ - if (_fol) { \ - FolderObserverList _listCopy(*_fol); \ - for (PRUint32 _fol_i = 0; _fol_i < _listCopy.Length(); _fol_i ++) { \ - if (_listCopy[_fol_i]) \ - _listCopy[_fol_i]->_functionCall; \ - } \ - } \ - } -#define ENUMERATE_HISTORY_OBSERVERS(_functionCall) \ - { \ - nsTArray observerCopy(mEverythingObservers); \ - for (PRUint32 _obs_i = 0; _obs_i < observerCopy.Length(); _obs_i ++) { \ - if (observerCopy[_obs_i]) \ - observerCopy[_obs_i]->_functionCall; \ - } \ - } - -// nsNavHistoryResult::OnBeginUpdateBatch (nsINavBookmark/HistoryObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnBeginUpdateBatch() -{ - ENUMERATE_HISTORY_OBSERVERS(OnBeginUpdateBatch()); - return NS_OK; -} - - -// nsNavHistoryResult::OnEndUpdateBatch (nsINavBookmark/HistoryObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnEndUpdateBatch() -{ - ENUMERATE_HISTORY_OBSERVERS(OnEndUpdateBatch()); - return NS_OK; -} - - -// nsNavHistoryResult::OnItemAdded (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnItemAdded(nsIURI *aBookmark, - PRInt64 aFolder, - PRInt32 aIndex) -{ - ENUMERATE_BOOKMARK_OBSERVERS_FOR_FOLDER(aFolder, - OnItemAdded(aBookmark, aFolder, aIndex)); - ENUMERATE_HISTORY_OBSERVERS(OnItemAdded(aBookmark, aFolder, aIndex)); - return NS_OK; -} - - -// nsNavHistoryResult::OnItemRemoved (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnItemRemoved(nsIURI *aBookmark, - PRInt64 aFolder, PRInt32 aIndex) -{ - ENUMERATE_BOOKMARK_OBSERVERS_FOR_FOLDER(aFolder, - OnItemRemoved(aBookmark, aFolder, aIndex)); - ENUMERATE_HISTORY_OBSERVERS(OnItemRemoved(aBookmark, aFolder, aIndex)); - return NS_OK; -} - - -// nsNavHistoryResult::OnItemChanged (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnItemChanged(nsIURI *aBookmark, - const nsACString &aProperty, - const nsAString &aValue) -{ - nsresult rv; - nsNavBookmarks* bookmarkService = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarkService, NS_ERROR_OUT_OF_MEMORY); - - PRUint32 folderCount; - PRInt64* folders; - rv = bookmarkService->GetBookmarkFolders(aBookmark, &folderCount, &folders); - NS_ENSURE_SUCCESS(rv, rv); - for (PRUint32 i = 0; i < folderCount; i ++) { - ENUMERATE_BOOKMARK_OBSERVERS_FOR_FOLDER(folders[i], - OnItemChanged(aBookmark, aProperty, aValue)); - } - if (folders) - nsMemory::Free(folders); - - // Note: we do NOT call history observers in this case. This notification is - // the same as other history notification, except that here we know the item - // is a bookmark. History observers will handle the history notification - // instead. - return NS_OK; -} - - -// nsNavHistoryResult::OnItemVisited (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnItemVisited(nsIURI* aBookmark, PRInt64 aVisitId, - PRTime aVisitTime) -{ - nsresult rv; - nsNavBookmarks* bookmarkService = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarkService, NS_ERROR_OUT_OF_MEMORY); - - // find all the folders to notify about this item - PRUint32 folderCount; - PRInt64* folders; - rv = bookmarkService->GetBookmarkFolders(aBookmark, &folderCount, &folders); - NS_ENSURE_SUCCESS(rv, rv); - for (PRUint32 i = 0; i < folderCount; i ++) { - ENUMERATE_BOOKMARK_OBSERVERS_FOR_FOLDER(folders[i], - OnItemVisited(aBookmark, aVisitId, aVisitTime)); - } - if (folders) - nsMemory::Free(folders); - - // Note: we do NOT call history observers in this case. This notification is - // the same as OnVisit, except that here we know the item is a bookmark. - // History observers will handle the history notification instead. - return NS_OK; -} - - -// nsNavHistoryResult::OnItemReplaced (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnItemReplaced(PRInt64 aFolder, - nsIURI *aItem, nsIURI *aNewItem) -{ - ENUMERATE_BOOKMARK_OBSERVERS_FOR_FOLDER(aFolder, - OnItemReplaced(aFolder, aItem, aNewItem)); - ENUMERATE_HISTORY_OBSERVERS(OnItemReplaced(aFolder, aItem, aNewItem)); - return NS_OK; -} - - -// nsNavHistoryResult::OnFolderAdded (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnFolderAdded(PRInt64 aFolder, - PRInt64 aParent, PRInt32 aIndex) -{ - ENUMERATE_BOOKMARK_OBSERVERS_FOR_FOLDER(aParent, - OnFolderAdded(aFolder, aParent, aIndex)); - ENUMERATE_HISTORY_OBSERVERS(OnFolderAdded(aFolder, aParent, aIndex)); - return NS_OK; -} - - -// nsNavHistoryResult::OnFolderRemoved (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnFolderRemoved(PRInt64 aFolder, - PRInt64 aParent, PRInt32 aIndex) -{ - ENUMERATE_BOOKMARK_OBSERVERS_FOR_FOLDER(aParent, - OnFolderRemoved(aFolder, aParent, aIndex)); - ENUMERATE_HISTORY_OBSERVERS(OnFolderRemoved(aFolder, aParent, aIndex)); - return NS_OK; -} - - -// nsNavHistoryResult::OnFolderMoved (nsINavBookmarkObserver) -// -// Need to notify both the source and the destination folders (if they -// are different). - -NS_IMETHODIMP -nsNavHistoryResult::OnFolderMoved(PRInt64 aFolder, - PRInt64 aOldParent, PRInt32 aOldIndex, - PRInt64 aNewParent, PRInt32 aNewIndex) -{ - { // scope for loop index for VC6's broken for loop scoping - ENUMERATE_BOOKMARK_OBSERVERS_FOR_FOLDER(aOldParent, - OnFolderMoved(aFolder, aOldParent, aOldIndex, aNewParent, aNewIndex)); - } - if (aNewParent != aOldParent) { - ENUMERATE_BOOKMARK_OBSERVERS_FOR_FOLDER(aNewParent, - OnFolderMoved(aFolder, aOldParent, aOldIndex, aNewParent, aNewIndex)); - } - ENUMERATE_HISTORY_OBSERVERS(OnFolderMoved(aFolder, aOldParent, aOldIndex, - aNewParent, aNewIndex)); - return NS_OK; -} - - -// nsNavHistoryResult::OnFolderChanged (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnFolderChanged(PRInt64 aFolder, - const nsACString &aProperty) -{ - ENUMERATE_BOOKMARK_OBSERVERS_FOR_FOLDER(aFolder, - OnFolderChanged(aFolder, aProperty)); - ENUMERATE_HISTORY_OBSERVERS(OnFolderChanged(aFolder, aProperty)); - return NS_OK; -} - - -// nsNavHistoryResult::OnSeparatorAdded (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnSeparatorAdded(PRInt64 aParent, PRInt32 aIndex) -{ - // Separators only appear in folder nodes, so history observers don't care. - ENUMERATE_BOOKMARK_OBSERVERS_FOR_FOLDER(aParent, - OnSeparatorAdded(aParent, aIndex)); - return NS_OK; -} - - -// nsNavHistoryResult::OnSeparatorRemoved (nsINavBookmarkObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnSeparatorRemoved(PRInt64 aParent, PRInt32 aIndex) -{ - // Separators only appear in folder nodes, so history observers don't care. - ENUMERATE_BOOKMARK_OBSERVERS_FOR_FOLDER(aParent, - OnSeparatorRemoved(aParent, aIndex)); - return NS_OK; -} - - -// nsNavHistoryResult::OnVisit (nsINavHistoryObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnVisit(nsIURI* aURI, PRInt64 aVisitId, PRTime aTime, - PRInt64 aSessionId, PRInt64 aReferringId, - PRUint32 aTransitionType) -{ - ENUMERATE_HISTORY_OBSERVERS(OnVisit(aURI, aVisitId, aTime, aSessionId, - aReferringId, aTransitionType)); - return NS_OK; -} - - -// nsNavHistoryResult::OnTitleChanged (nsINavHistoryObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnTitleChanged(nsIURI* aURI, const nsAString& aPageTitle, - const nsAString& aUserTitle, - PRBool aIsUserTitleChanged) -{ - ENUMERATE_HISTORY_OBSERVERS(OnTitleChanged(aURI, aPageTitle, aUserTitle, - aIsUserTitleChanged)); - return NS_OK; -} - - -// nsNavHistoryResult::OnDeleteURI (nsINavHistoryObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnDeleteURI(nsIURI *aURI) -{ - ENUMERATE_HISTORY_OBSERVERS(OnDeleteURI(aURI)); - return NS_OK; -} - - -// nsNavHistoryResult::OnClearHistory (nsINavHistoryObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnClearHistory() -{ - ENUMERATE_HISTORY_OBSERVERS(OnClearHistory()); - return NS_OK; -} - - -// nsNavHistoryResult::OnPageChanged (nsINavHistoryObserver) - -NS_IMETHODIMP -nsNavHistoryResult::OnPageChanged(nsIURI *aURI, - PRUint32 aWhat, const nsAString &aValue) -{ - ENUMERATE_HISTORY_OBSERVERS(OnPageChanged(aURI, aWhat, aValue)); - return NS_OK; -} - - -// nsNavHistoryResult;:OnPageExpired (nsINavHistoryObserver) -// -// Don't do anything when pages expire. Perhaps we want to find the item -// to delete it. - -NS_IMETHODIMP -nsNavHistoryResult::OnPageExpired(nsIURI* aURI, PRTime aVisitTime, - PRBool aWholeEntry) -{ - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer ************************************************ - -NS_IMPL_ADDREF(nsNavHistoryResultTreeViewer) -NS_IMPL_RELEASE(nsNavHistoryResultTreeViewer) - -NS_INTERFACE_MAP_BEGIN(nsNavHistoryResultTreeViewer) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsINavHistoryResultTreeViewer) - NS_INTERFACE_MAP_ENTRY(nsINavHistoryResultViewer) - NS_INTERFACE_MAP_ENTRY(nsINavHistoryResultTreeViewer) - NS_INTERFACE_MAP_ENTRY(nsITreeView) -NS_INTERFACE_MAP_END - -nsNavHistoryResultTreeViewer::nsNavHistoryResultTreeViewer() : - mCollapseDuplicates(PR_TRUE), - mShowSessions(PR_FALSE) -{ - -} - - -// nsNavHistoryResultTreeViewer::FinishInit -// -// This is called when the result or tree may have changed. It reinitializes -// everything. Result and/or tree can be null when calling. - -nsresult -nsNavHistoryResultTreeViewer::FinishInit() -{ - if (mTree && mResult) { - mResult->mRootNode->SetContainerOpen(PR_TRUE); - SortingChanged(mResult->mSortingMode); - } - // if there is no tree, BuildVisibleList will clear everything for us - return BuildVisibleList(); -} - - -// nsNavHistoryResultTreeViewer::ComputeShowSessions -// -// Computes the value of mShowSessions, which is used to see if we should -// try to set styles for session groupings. We only want to do session -// grouping if we're in an appropriate view, which is view by visit and -// sorted by date. - -void -nsNavHistoryResultTreeViewer::ComputeShowSessions() -{ - NS_ASSERTION(mResult, "Must have a result to show sessions!"); - mShowSessions = PR_FALSE; - - nsNavHistoryQueryOptions* options = mResult->mOptions; - NS_ASSERTION(options, "navHistoryResults must have valid options"); - if (options->ResultType() != nsINavHistoryQueryOptions::RESULTS_AS_VISIT && - options->ResultType() != nsINavHistoryQueryOptions::RESULTS_AS_FULL_VISIT) - return; // not visits - - if (mResult->mSortingMode != nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING && - mResult->mSortingMode != nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING) - return; // not date sorting - - PRUint32 groupCount; - options->GroupingMode(&groupCount); - if (groupCount > 0) - return; - - mShowSessions = PR_TRUE; -} - - -// nsNavHistoryResultTreeViewer::GetRowSessionStatus - -nsNavHistoryResultTreeViewer::SessionStatus -nsNavHistoryResultTreeViewer::GetRowSessionStatus(PRInt32 row) -{ - NS_ASSERTION(row >= 0 && row < PRInt32(mVisibleElements.Length()), - "Invalid row!"); - - nsNavHistoryResultNode *node = mVisibleElements[row]; - if (! node->IsVisit()) - return Session_None; // not a visit, so there are no sessions - nsNavHistoryVisitResultNode* visit = node->GetAsVisit(); - - if (visit->mSessionId != 0) { - if (row == 0) { - return Session_Start; - } else { - nsNavHistoryResultNode* previousNode = mVisibleElements[row - 1]; - if (previousNode->IsVisit() && - visit->mSessionId != previousNode->GetAsVisit()->mSessionId) { - return Session_Start; - } else { - return Session_Continue; - } - } - } - return Session_None; -} - - -// nsNavHistoryResultTreeViewer::BuildVisibleList -// -// Call to completely rebuild the list of visible items. Note if there is no -// tree or root this will just clear out the list, so you can also call this -// when a tree is detached to clear the list. -// -// This does NOT update the screen. - -nsresult -nsNavHistoryResultTreeViewer::BuildVisibleList() -{ - if (mResult) { - // Any current visible elements need to be marked as invisible. However, - // be careful to do this only when there is no result. We don't have owning - // references to these objects, and if the result was detached and freed, - // our pointers will be stale. - for (PRUint32 i = 0; i < mVisibleElements.Length(); i ++) - mVisibleElements[i]->mViewIndex = -1; - } - mVisibleElements.Clear(); - - if (mResult->mRootNode && mTree) { - ComputeShowSessions(); - return BuildVisibleSection(mResult->mRootNode, &mVisibleElements, 0); - } - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::BuildVisibleSection -// -// This takes a container and recursively appends visible elements to the -// given array. This is used to build the visible element list (with -// mVisibleElements passed as the array), or portions thereof (with a -// separate array that is merged with the main list later. -// -// aVisibleStartIndex is the visible index of the beginning of the 'aVisible' -// array. When aVisible is mVisibleElements, this is 0. This is non-zero -// when we are building up a sub-region for insertion. Then, this is the -// index where the new array will be inserted into mVisibleElements. It is -// used to compute each node's mViewIndex. - -nsresult -nsNavHistoryResultTreeViewer::BuildVisibleSection( - nsNavHistoryContainerResultNode* aContainer, - VisibleList* aVisible, - PRUint32 aVisibleStartIndex) -{ - if (! aContainer->mExpanded) - return NS_OK; // nothing to do - for (PRInt32 i = 0; i < aContainer->mChildren.Count(); i ++) { - nsNavHistoryResultNode* cur = aContainer->mChildren[i]; - - // collapse all duplicates starting from here - if (mCollapseDuplicates) { - PRUint32 showThis; - while (i < aContainer->mChildren.Count() - 1 && - CanCollapseDuplicates(cur, aContainer->mChildren[i+1], &showThis)) { - if (showThis) { - // collapse the first and use the second - cur->mViewIndex = -1; - cur = aContainer->mChildren[i + 1]; - } else { - // collapse the second and use the first - aContainer->mChildren[i + 1]->mViewIndex = -1; - } - i ++; - } - } - - // don't display separators when sorted - if (cur->IsSeparator()) { - if (aContainer->GetSortType() != nsINavHistoryQueryOptions::SORT_BY_NONE) { - cur->mViewIndex = -1; - continue; - } - } - - // add item - cur->mViewIndex = aVisibleStartIndex + aVisible->Length(); - if (! aVisible->AppendElement(cur)) - return NS_ERROR_OUT_OF_MEMORY; - - // recursively do containers - if (cur->IsContainer()) { - nsNavHistoryContainerResultNode* curContainer = cur->GetAsContainer(); - if (curContainer->mExpanded && curContainer->mChildren.Count() > 0) { - nsresult rv = BuildVisibleSection(curContainer, aVisible, - aVisibleStartIndex); - NS_ENSURE_SUCCESS(rv, rv); - } - } - } - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::CountVisibleRowsForItem -// -// This counts how many rows an item takes in the tree, that is, the item -// itself plus any nodes following it with an increased indent. This allows -// you to figure out how many rows an item (=1) or a container with all of -// its children takes. - -PRUint32 -nsNavHistoryResultTreeViewer::CountVisibleRowsForItem( - nsNavHistoryResultNode* aNode) -{ - NS_ASSERTION(aNode->mViewIndex >= 0, "Item is not visible, no rows to count"); - PRInt32 outerLevel = aNode->mIndentLevel; - for (PRUint32 i = aNode->mViewIndex + 1; i < mVisibleElements.Length(); i ++) { - if (mVisibleElements[i]->mIndentLevel <= outerLevel) - return i - aNode->mViewIndex; - } - // this node plus its children occupy the bottom of the list - return mVisibleElements.Length() - aNode->mViewIndex; -} - - -// nsNavHistoryResultTreeViewer::RefreshVisibleSection -// -// This is called by containers when they change and we need to update -// everything about the container. We build a new visible section with the -// container as a separate object so we first know how the list changes. This -// way we only have to do one realloc/memcpy to update the list. -// -// We also try to be smart here about redrawing the screen. - -nsresult -nsNavHistoryResultTreeViewer::RefreshVisibleSection( - nsNavHistoryContainerResultNode* aContainer) -{ - NS_ASSERTION(mResult, "Need to have a result to update"); - if (! mTree) - return NS_OK; - - // The root node is invisible, so we can't check its index and indent level. - // Therefore, special case it. This is easy because we just rebuild everything - if (aContainer == mResult->mRootNode) - return BuildVisibleList(); - - // When the container is not the root, it better be visible if we are updating - NS_ASSERTION(aContainer->mViewIndex >= 0 && - aContainer->mViewIndex < PRInt32(mVisibleElements.Length()), - "Trying to expand a node that is not visible"); - NS_ASSERTION(mVisibleElements[aContainer->mViewIndex] == aContainer, - "Visible index is out of sync!"); - - PRUint32 startReplacement = aContainer->mViewIndex + 1; - PRUint32 replaceCount = CountVisibleRowsForItem(aContainer) - 1; - - // Mark the removees as invisible - PRUint32 i; - for (i = startReplacement; i < replaceCount; i ++) - mVisibleElements[startReplacement + i]->mViewIndex = -1; - - // Building the new list will set the new elements' visible indices. - VisibleList newElements; - nsresult rv = BuildVisibleSection(aContainer, &newElements, startReplacement); - NS_ENSURE_SUCCESS(rv, rv); - - // actually update the visible list - if (! mVisibleElements.ReplaceElementsAt(aContainer->mViewIndex + 1, - replaceCount, newElements.Elements(), newElements.Length())) - return NS_ERROR_OUT_OF_MEMORY; - - if (replaceCount == newElements.Length()) { - // length is the same, just repaint the changed area - if (replaceCount > 0) - mTree->InvalidateRange(startReplacement, startReplacement + replaceCount - 1); - } else { - // If the new area is a different size, we'll have to renumber the elements - // following the area. - for (i = startReplacement + newElements.Length(); - i < mVisibleElements.Length(); i ++) - mVisibleElements[i]->mViewIndex = i; - - // repaint the region that stayed the same, also including the container - // element itself since its twisty could have changed. - PRUint32 minLength; - if (replaceCount > newElements.Length()) - minLength = newElements.Length(); - else - minLength = replaceCount; - mTree->InvalidateRange(startReplacement - 1, startReplacement + minLength - 1); - - // now update the number of elements - mTree->RowCountChanged(startReplacement + minLength, - newElements.Length() - replaceCount); - } - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::CanCollapseDuplicates -// -// This returns true if the two results can be collapsed as duplicates. -// *aShowThisOne will be either 0 or 1, indicating which of the duplicates -// should be shown. - -PRBool -nsNavHistoryResultTreeViewer::CanCollapseDuplicates( - nsNavHistoryResultNode* aTop, nsNavHistoryResultNode* aNext, - PRUint32* aShowThisOne) -{ - if (! mCollapseDuplicates) - return PR_FALSE; - if (! aTop->IsVisit() || ! aNext->IsVisit()) - return PR_FALSE; // only collapse two visits - - nsNavHistoryVisitResultNode* topVisit = aTop->GetAsVisit(); - nsNavHistoryVisitResultNode* nextVisit = aNext->GetAsVisit(); - - if (! topVisit->mURI.Equals(nextVisit->mURI)) - return PR_FALSE; // don't collapse nonmatching URIs - - // now we know we want to collapse, show the one with the more recent time - *aShowThisOne = topVisit->mTime < nextVisit->mTime; - return PR_TRUE; -} - - -// nsNavHistoryResultTreeViewer::GetColumnType - -nsNavHistoryResultTreeViewer::ColumnType -nsNavHistoryResultTreeViewer::GetColumnType(nsITreeColumn* col) -{ - const PRUnichar* idConst; - col->GetIdConst(&idConst); - switch(idConst[0]) { - case PRUnichar('t'): - return Column_Title; - case PRUnichar('u'): - return Column_URI; - case PRUnichar('d'): - return Column_Date; - case PRUnichar('v'): - return Column_VisitCount; - default: - return Column_Unknown; - } -} - - -// nsNavHistoryResultTreeViewer::SortTypeToColumnType -// -// Places whether it's ascending or descending into the given boolean buffer - -nsNavHistoryResultTreeViewer::ColumnType -nsNavHistoryResultTreeViewer::SortTypeToColumnType(PRUint32 aSortType, - PRBool* aDescending) -{ - switch(aSortType) { - case nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING: - *aDescending = PR_FALSE; - return Column_Title; - case nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING: - *aDescending = PR_TRUE; - return Column_Title; - case nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING: - *aDescending = PR_FALSE; - return Column_Date; - case nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING: - *aDescending = PR_TRUE; - return Column_Date; - case nsINavHistoryQueryOptions::SORT_BY_URI_ASCENDING: - *aDescending = PR_FALSE; - return Column_URI; - case nsINavHistoryQueryOptions::SORT_BY_URI_DESCENDING: - *aDescending = PR_TRUE; - return Column_URI; - case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING: - *aDescending = PR_FALSE; - return Column_VisitCount; - case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING: - *aDescending = PR_TRUE; - return Column_VisitCount; - default: - *aDescending = PR_FALSE; - return Column_Unknown; - } -} - - -// nsNavHistoryResultTreeViewer::ItemInserted (nsINavHistoryResultViewer) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::ItemInserted( - nsINavHistoryContainerResultNode *aParent, nsINavHistoryResultNode *aItem, - PRUint32 aNewIndex) -{ - if (! mTree) - return NS_OK; // nothing to do - if (! mResult) { - NS_NOTREACHED("Got an inserted message with no result"); - return NS_ERROR_UNEXPECTED; - } - - nsRefPtr parent; - aParent->QueryInterface(NS_GET_IID(nsNavHistoryContainerResultNode), - getter_AddRefs(parent)); - if (! parent) { - NS_NOTREACHED("Parent is not a concrete node"); - return NS_ERROR_UNEXPECTED; - } - nsCOMPtr item = do_QueryInterface(aItem); - if (! aItem) { - NS_NOTREACHED("Item is not a concrete node"); - return NS_ERROR_UNEXPECTED; - } - - // don't display separators when sorted - if (item->IsSeparator()) { - if (parent->GetSortType() != nsINavHistoryQueryOptions::SORT_BY_NONE) { - item->mViewIndex = -1; - return NS_OK; - } - } - - // update parent when inserting the first item because twisty may have changed - if (parent->mChildren.Count() == 1) - ItemChanged(aParent); - - // compute the new view index of the item - PRInt32 newViewIndex = -1; - if (aNewIndex == 0) { - // item is the first thing in our child list, it takes our index +1. Note - // that this computation still works if the parent is an invisible root - // node, because root_index + 1 = -1 + 1 = 0 - newViewIndex = parent->mViewIndex + 1; - } else { - // Here, we try to find the next visible element in the child list so we - // can set the new visible index to be right before that. Note that we - // have to search DOWN instead of up, because some siblings could have - // children themselves that would be in the way. - for (PRInt32 i = aNewIndex + 1; i < parent->mChildren.Count(); i ++) { - if (parent->mChildren[i]->mViewIndex >= 0) { - // the view indices of subsequent children have not been shifted - // so the next item will have what should be our index - newViewIndex = parent->mChildren[i]->mViewIndex; - break; - } - } - if (newViewIndex < 0) { - // At the end of the child list without finding a visible sibling: This - // is a little harder because we don't know how many rows the last item - // in our list takes up (it could be a container with many children). - PRUint32 lastRowCount = - CountVisibleRowsForItem(parent->mChildren[aNewIndex - 1]); - newViewIndex = parent->mChildren[aNewIndex - 1]->mViewIndex + - lastRowCount; - } - } - - // Try collapsing with the previous node. Note that we do not have to try - // to redraw the surrounding rows (which we normally would because session - // boundaries may have changed) because when an item is merged, it will - // always be in the same session. - PRUint32 showThis; - if (newViewIndex > 0 && - CanCollapseDuplicates(mVisibleElements[newViewIndex - 1], - item, &showThis)) { - if (showThis == 0) { - // new node is invisible, collapsed with previous one - item->mViewIndex = -1; - return NS_OK; - } else { - // new node replaces the previous - ItemReplaced(parent, mVisibleElements[newViewIndex - 1], item, 0); - return NS_OK; - } - } - - // try collapsing with the next node (which is currently at the same index - // we are trying to insert at) - if (newViewIndex < PRInt32(mVisibleElements.Length()) && - CanCollapseDuplicates(item, mVisibleElements[newViewIndex], - &showThis)) { - if (showThis == 0) { - // new node replaces the next node - ItemReplaced(parent, mVisibleElements[newViewIndex], item, 0); - return NS_OK; - } else { - // new node is invisible, replaced by next one - item->mViewIndex = -1; - return NS_OK; - } - } - - // no collapsing, insert new item - item->mViewIndex = newViewIndex; - mVisibleElements.InsertElementAt(newViewIndex, item); - for (PRUint32 i = newViewIndex + 1; i < mVisibleElements.Length(); i ++) - mVisibleElements[i]->mViewIndex = i; - mTree->RowCountChanged(newViewIndex, 1); - - // Need to redraw the rows around this one because session boundaries may - // have changed. For example, if we add a page to a session, the previous - // page will need to be redrawn because it's session border will disappear. - if (mShowSessions) { - if (newViewIndex > 0) - mTree->InvalidateRange(newViewIndex - 1, newViewIndex - 1); - if (newViewIndex < PRInt32(mVisibleElements.Length()) - 1) - mTree->InvalidateRange(newViewIndex + 1, newViewIndex + 1); - } - - if (item->IsContainer() && item->GetAsContainer()->mExpanded) - RefreshVisibleSection(item->GetAsContainer()); - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::ItemRemoved (nsINavHistoryResultViewer) -// -// THIS FUNCTION DOES NOT HANDLE cases where a collapsed node is being -// removed but the node it is collapsed with is not being removed (this -// then just swap out the removee with it's collapsing partner). The only -// time when we really remove things is when deleting URIs, which will -// apply to all collapsees. This function is called sometimes when -// resorting items. However, we won't do this when sorted by date because -// dates will never change for visits, and date sorting is the only time -// things are collapsed. - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::ItemRemoved( - nsINavHistoryContainerResultNode *aParent, nsINavHistoryResultNode *aItem, - PRUint32 aOldIndex) -{ - NS_ASSERTION(mResult, "Got a notification but have no result!"); - if (! mTree) - return NS_OK; // nothing to do - - nsCOMPtr item = do_QueryInterface(aItem); - NS_ENSURE_TRUE(item, NS_ERROR_INVALID_ARG); - - PRInt32 oldViewIndex = item->mViewIndex; - if (oldViewIndex < 0) - return NS_OK; // item was already invisible, nothing to do - - // this may have been a container, in which case it has a lot of rows - PRInt32 count = CountVisibleRowsForItem(item); - - // We really want tail recursion here, since we sometimes do another remove - // after this when duplicates are being collapsed. This loop emulates that. - while (PR_TRUE) { - if (oldViewIndex > NS_STATIC_CAST(PRInt32, mVisibleElements.Length())) { - NS_NOTREACHED("Trying to remove invalid row"); - return NS_OK; - } - mVisibleElements.RemoveElementsAt(oldViewIndex, count); - for (PRUint32 i = oldViewIndex; i < mVisibleElements.Length(); i ++) - mVisibleElements[i]->mViewIndex = i; - - mTree->RowCountChanged(oldViewIndex, -count); - - // the removal might have put two things together that should be collapsed - if (oldViewIndex > 0 && - oldViewIndex < NS_STATIC_CAST(PRInt32, mVisibleElements.Length())) { - PRUint32 showThisOne; - if (CanCollapseDuplicates(mVisibleElements[oldViewIndex - 1], - mVisibleElements[oldViewIndex], &showThisOne)) - { - // Fake-tail-recurse to the beginning of this function to remove the - // collapsed row. Note that we need to set the visible index to -1 - // before looping because we can never touch the row we're removing - // (callers may have already destroyed it). - oldViewIndex = oldViewIndex - 1 + showThisOne; - mVisibleElements[oldViewIndex]->mViewIndex = -1; - count = 1; // next time remove one row - continue; - } - } - break; // normal path: just remove once - } - - // redraw parent because twisty may have changed - PRBool hasChildren; - aParent->GetHasChildren(&hasChildren); - if (! hasChildren) - ItemChanged(aParent); - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::ItemReplaced (nsINavHistoryResultViewer) -// -// Be careful, the parameter 'aIndex' here specifies the index in the -// parent node of the item, not the visible index. -// -// This is called from the result when the item is replaced, but this object -// calls this function internally also when duplicate collapsing changes. -// In this case, aIndex will be 0, so we should be careful not to use the -// value. - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::ItemReplaced( - nsINavHistoryContainerResultNode* parent, nsINavHistoryResultNode* aOldItem, - nsINavHistoryResultNode* aNewItem, PRUint32 aIndexDoNotUse) -{ - if (! mTree) - return NS_OK; - - PRInt32 viewIndex; - aOldItem->GetViewIndex(&viewIndex); - aNewItem->SetViewIndex(viewIndex); - if (viewIndex >= 0 && - viewIndex < NS_STATIC_CAST(PRInt32, mVisibleElements.Length())) { - nsCOMPtr newItem = do_QueryInterface(aNewItem); - NS_ASSERTION(newItem, "Node is not one of our concrete ones"); - mVisibleElements[viewIndex] = newItem; - } - aOldItem->SetViewIndex(-1); - mTree->InvalidateRow(viewIndex); - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::ItemChanged (nsINavHistoryResultViewer) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::ItemChanged(nsINavHistoryResultNode *item) -{ - NS_ASSERTION(mResult, "Got a notification but have no result!"); - PRInt32 viewIndex; - item->GetViewIndex(&viewIndex); - if (mTree && viewIndex >= 0) - return mTree->InvalidateRow(viewIndex); - printf("Invalidated row %d\n", viewIndex); - return NS_OK; // nothing to refresh -} - - -// nsNavHistoryResultTreeViewer::ContainerOpened (nsINavHistoryResultViewer) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::ContainerOpened( - nsINavHistoryContainerResultNode *item) -{ - NS_ASSERTION(mResult, "Got a notification but have no result!"); - if (! mTree || item < 0) - return NS_OK; // nothing to do, container is not visible - PRInt32 viewIndex; - item->GetViewIndex(&viewIndex); - if (viewIndex >= NS_STATIC_CAST(PRInt32, mVisibleElements.Length())) { - // be paranoid about visible indices since others can change it - NS_NOTREACHED("Invalid visible index"); - return NS_ERROR_UNEXPECTED; - } - return RefreshVisibleSection( - NS_STATIC_CAST(nsNavHistoryContainerResultNode*, item)); -} - - -// nsNavHistoryResultTreeViewer::ContainerClosed (nsINavHistoryResultViewer) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::ContainerClosed( - nsINavHistoryContainerResultNode *item) -{ - NS_ASSERTION(mResult, "Got a notification but have no result!"); - if (! mTree || item < 0) - return NS_OK; // nothing to do, container is not visible - - PRInt32 viewIndex; - item->GetViewIndex(&viewIndex); - if (viewIndex >= NS_STATIC_CAST(PRInt32, mVisibleElements.Length())) { - // be paranoid about visible indices since others can change it - NS_NOTREACHED("Invalid visible index"); - return NS_ERROR_UNEXPECTED; - } - return RefreshVisibleSection( - NS_STATIC_CAST(nsNavHistoryContainerResultNode*, item)); -} - - -// nsNavHistoryResultTreeViewer::InvalidateContainer (nsINavHistoryResultViewer) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::InvalidateContainer( - nsINavHistoryContainerResultNode* aItem) -{ - NS_ASSERTION(mResult, "Got message but don't have a result!"); - if (! mTree) - return NS_OK; - - return RefreshVisibleSection( - NS_STATIC_CAST(nsNavHistoryContainerResultNode*, aItem)); -} - - -// nsNavHistoryResultTreeViewer::InvalidateAll (nsINavHistoryResultViewer) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::InvalidateAll() -{ - NS_ASSERTION(mResult, "Got message but don't have a result!"); - if (! mTree) - return NS_OK; - - PRInt32 oldRowCount = mVisibleElements.Length(); - - // update flat list to new contents - nsresult rv = BuildVisibleList(); - NS_ENSURE_SUCCESS(rv, rv); - - // redraw the tree, inserting new items - rv = mTree->RowCountChanged(0, mVisibleElements.Length() - oldRowCount); - NS_ENSURE_SUCCESS(rv, rv); - return mTree->Invalidate(); -} - - -// nsNavHistoryResultTreeViewer::SortingChanged (nsINavHistoryResultViewer) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::SortingChanged(PRUint32 aSortingMode) -{ - if (! mTree || ! mResult) - return NS_OK; - - nsCOMPtr columns; - nsresult rv = mTree->GetColumns(getter_AddRefs(columns)); - NS_ENSURE_SUCCESS(rv, rv); - - NS_NAMED_LITERAL_STRING(sortDirectionKey, "sortDirection"); - - // clear old sorting indicator. Watch out: GetSortedColumn returns NS_OK - // but null element if there is no sorted element - nsCOMPtr column; - rv = columns->GetSortedColumn(getter_AddRefs(column)); - NS_ENSURE_SUCCESS(rv, rv); - if (column) { - nsCOMPtr element; - rv = column->GetElement(getter_AddRefs(element)); - NS_ENSURE_SUCCESS(rv, rv); - element->SetAttribute(sortDirectionKey, EmptyString()); - } - - // set new sorting indicator by looking through all columns for ours - if (aSortingMode == nsINavHistoryQueryOptions::SORT_BY_NONE) - return NS_OK; - PRBool desiredIsDescending; - ColumnType desiredColumn = SortTypeToColumnType(aSortingMode, - &desiredIsDescending); - PRInt32 colCount; - rv = columns->GetCount(&colCount); - NS_ENSURE_SUCCESS(rv, rv); - for (PRInt32 i = 0; i < colCount; i ++) { - columns->GetColumnAt(i, getter_AddRefs(column)); - NS_ENSURE_SUCCESS(rv, rv); - if (GetColumnType(column) == desiredColumn) { - // found our desired one, set - nsCOMPtr element; - rv = column->GetElement(getter_AddRefs(element)); - NS_ENSURE_SUCCESS(rv, rv); - if (desiredIsDescending) - element->SetAttribute(sortDirectionKey, NS_LITERAL_STRING("descending")); - else - element->SetAttribute(sortDirectionKey, NS_LITERAL_STRING("ascending")); - break; - } - } - - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::Result (nsINavHistoryResultViewer) -// -// We store a pointer to the concrete object, so need to QI. We fail if the -// given result object doesn't match our concrete one. -// -// @see SetTree - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::GetResult(nsINavHistoryResult** aResult) -{ - *aResult = mResult; - NS_IF_ADDREF(*aResult); - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::SetResult(nsINavHistoryResult* aResult) -{ - if (aResult) { - nsresult rv = aResult->QueryInterface(NS_GET_IID(nsNavHistoryResult), - getter_AddRefs(mResult)); - NS_ENSURE_SUCCESS(rv, rv); - } else { - mResult = nsnull; - } - return FinishInit(); -} - - -// nsNavHistoryResultTreeViewer::AddViewObserver (nsINavHistoryResultViewer) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::AddViewObserver( - nsINavHistoryResultViewObserver* aObserver, PRBool aOwnsWeak) -{ - return mObservers.AppendWeakElement(aObserver, aOwnsWeak); -} - - -// nsNavHistoryResultTreeViewer::RemoveViewObserver (nsINavHistoryResultViewer) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::RemoveViewObserver( - nsINavHistoryResultViewObserver* aObserver) -{ - return mObservers.RemoveWeakElement(aObserver); -} - - -// nsNavHistoryResultTreeViewer::CollapseDuplicates (nsINavHistoryResultTreeViewer) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::GetCollapseDuplicates(PRBool* aCollapseDuplicates) -{ - *aCollapseDuplicates = mCollapseDuplicates; - return NS_OK; -} -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::SetCollapseDuplicates(PRBool aCollapseDuplicates) -{ - if ((aCollapseDuplicates && mCollapseDuplicates) || - (! aCollapseDuplicates && ! mCollapseDuplicates)) - return NS_OK; // no change - - mCollapseDuplicates = aCollapseDuplicates; - - if (mResult && mTree) - InvalidateAll(); - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::GetFlatItemCount (nsINavHistoryResultTreeViewer) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::GetFlatItemCount(PRUint32 *aItemCount) -{ - *aItemCount = mVisibleElements.Length(); - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::NodeForTreeIndex (nsINavHistoryResultTreeViewer) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::NodeForTreeIndex( - PRUint32 index, nsINavHistoryResultNode** aResult) -{ - if (index >= PRUint32(mVisibleElements.Length())) - return NS_ERROR_INVALID_ARG; - NS_ADDREF(*aResult = mVisibleElements[index]); - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::TreeIndexForNode (nsINavHistoryResultTreeViewer) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::TreeIndexForNode(nsINavHistoryResultNode* aNode, - PRUint32* aResult) -{ - nsresult rv; - nsCOMPtr node = do_QueryInterface(aNode, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - if (node->mViewIndex < 0) { - *aResult = nsINavHistoryResultTreeViewer::INDEX_INVISIBLE; - } else { - *aResult = node->mViewIndex; - NS_ASSERTION(mVisibleElements[*aResult] == node, - "Node's visible index and array out of sync"); - } - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::GetRowCount (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::GetRowCount(PRInt32 *aRowCount) -{ - *aRowCount = mVisibleElements.Length(); - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::Get/SetSelection (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::GetSelection(nsITreeSelection** aSelection) -{ - if (! mSelection) { - *aSelection = nsnull; - return NS_ERROR_FAILURE; - } - NS_ADDREF(*aSelection = mSelection); - return NS_OK; -} -NS_IMETHODIMP nsNavHistoryResultTreeViewer::SetSelection(nsITreeSelection* aSelection) -{ - mSelection = aSelection; - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::GetRowProperties (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::GetRowProperties(PRInt32 row, - nsISupportsArray *properties) -{ - if (row < 0 || row >= PRInt32(mVisibleElements.Length())) - return NS_ERROR_INVALID_ARG; - - nsNavHistoryResultNode *node = mVisibleElements[row]; - - // Add the container property if it's applicable for this row. - if (node->IsContainer()) { - properties->AppendElement(nsNavHistory::sContainerAtom); - } - - // Next handle properties for session information. - if (! mShowSessions) - return NS_OK; // don't need to bother to compute session boundaries - - switch (GetRowSessionStatus(row)) - { - case Session_Start: - properties->AppendElement(nsNavHistory::sSessionStartAtom); - break; - case Session_Continue: - properties->AppendElement(nsNavHistory::sSessionContinueAtom); - break; - case Session_None: - break; - default: - NS_NOTREACHED("Invalid session type"); - } - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::GetCellProperties (nsITreeView) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::GetCellProperties(PRInt32 row, nsITreeColumn *col, - nsISupportsArray *properties) -{ - /* - if (row < 0 || row >= mVisibleElements.Length()) - return NS_ERROR_INVALID_ARG; - - nsNavHistoryResultNode *node = mVisibleElements[row]; - PRInt64 folderId, bookmarksRootId, toolbarRootId; - node->GetFolderId(&folderId); - - nsCOMPtr bms(do_GetService( - NS_NAVBOOKMARKSSERVICE_CONTRACTID)); - bms->GetBookmarksRoot(&bookmarksRootId); - bms->GetToolbarRoot(&toolbarRootId); - if (bookmarksRootId == folderId) - properties->AppendElement(nsNavHistory::sMenuRootAtom); - else if (toolbarRootId == folderId) - properties->AppendElement(nsNavHistory::sToolbarRootAtom); - - if (mShowSessions && node->mSessionID != 0) { - if (row == 0 || - node->mSessionID != VisibleElementAt(row - 1)->mSessionID) { - properties->AppendElement(nsNavHistory::sSessionStartAtom); - } else { - properties->AppendElement(nsNavHistory::sSessionContinueAtom); - } - } - */ - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::GetColumnProperties (nsITreeView) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::GetColumnProperties(nsITreeColumn *col, - nsISupportsArray *properties) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - - -// nsNavHistoryResultTreeViewer::IsContainer (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::IsContainer(PRInt32 row, PRBool *_retval) -{ - if (row < 0 || row >= PRInt32(mVisibleElements.Length())) - return NS_ERROR_INVALID_ARG; - *_retval = mVisibleElements[row]->IsContainer(); - if (*_retval) { - // treat non-expandable queries as non-containers - if (mVisibleElements[row]->IsQuery() && - ! mVisibleElements[row]->GetAsQuery()->CanExpand()) - *_retval = PR_FALSE; - } - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::IsContainerOpen (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::IsContainerOpen(PRInt32 row, PRBool *_retval) -{ - if (row < 0 || row >= PRInt32(mVisibleElements.Length())) - return NS_ERROR_INVALID_ARG; - if (! mVisibleElements[row]->IsContainer()) - return NS_ERROR_INVALID_ARG; - - nsINavHistoryContainerResultNode* container = - mVisibleElements[row]->GetAsContainer(); - return container->GetContainerOpen(_retval); -} - - -// nsNavHistoryResultTreeViewer::IsContainerEmpty (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::IsContainerEmpty(PRInt32 row, PRBool *_retval) -{ - if (row < 0 || row >= PRInt32(mVisibleElements.Length())) - return NS_ERROR_INVALID_ARG; - if (! mVisibleElements[row]->IsContainer()) - return NS_ERROR_INVALID_ARG; - - nsINavHistoryContainerResultNode* container = - mVisibleElements[row]->GetAsContainer(); - PRBool hasChildren = PR_FALSE; - nsresult rv = container->GetHasChildren(&hasChildren); - *_retval = ! hasChildren; - return rv; -} - - -// nsNavHistoryResultTreeViewer::IsSeparator (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::IsSeparator(PRInt32 row, PRBool *_retval) -{ - if (row < 0 || row >= PRInt32(mVisibleElements.Length())) - return NS_ERROR_INVALID_ARG; - - *_retval = mVisibleElements[row]->IsSeparator(); - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::IsSorted (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::IsSorted(PRBool *_retval) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - - -// nsNavHistoryResultTreeViewer::CanDrop (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::CanDrop(PRInt32 row, PRInt32 orientation, - PRBool *_retval) -{ - PRUint32 count = mObservers.Length(); - for (PRUint32 i = 0; i < count; ++i) { - const nsCOMPtr &obs = mObservers[i]; - if (obs) { - obs->CanDrop(row, orientation, _retval); - if (*_retval) { - break; - } - } - } - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::Drop (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::Drop(PRInt32 row, PRInt32 orientation) -{ - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryResultViewObserver, - OnDrop(row, orientation)) - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::GetParentIndex (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::GetParentIndex(PRInt32 row, PRInt32* _retval) -{ - if (row < 0 || row >= PRInt32(mVisibleElements.Length())) - return NS_ERROR_INVALID_ARG; - nsNavHistoryResultNode* parent = mVisibleElements[row]->mParent; - if (! parent || parent->mViewIndex < 0) - *_retval = -1; - else - *_retval = parent->mViewIndex; - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::HasNextSibling (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::HasNextSibling(PRInt32 row, - PRInt32 afterIndex, - PRBool* _retval) -{ - if (row < 0 || row >= PRInt32(mVisibleElements.Length())) - return NS_ERROR_INVALID_ARG; - if (row == PRInt32(mVisibleElements.Length()) - 1) { - // this is the last thing in the list -> no next sibling - *_retval = PR_FALSE; - return NS_OK; - } - *_retval = (mVisibleElements[row]->mIndentLevel == - mVisibleElements[row + 1]->mIndentLevel); - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::GetLevel (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::GetLevel(PRInt32 row, PRInt32* _retval) -{ - if (row < 0 || row >= PRInt32(mVisibleElements.Length())) - return NS_ERROR_INVALID_ARG; - *_retval = mVisibleElements[row]->mIndentLevel; - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::GetImageSrc (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::GetImageSrc(PRInt32 row, nsITreeColumn *col, - nsAString& _retval) -{ - if (row < 0 || row >= PRInt32(mVisibleElements.Length())) - return NS_ERROR_INVALID_ARG; - - // only the first column has an image - PRInt32 colIndex; - col->GetIndex(&colIndex); - if (colIndex != 0) { - _retval.Truncate(0); - return NS_OK; - } - - nsNavHistoryResultNode* node = mVisibleElements[row]; - - // Containers may or may not have favicons. If not, we will return nothing - // as the image, and the style rule should pick up the default. - // Separator rows never have icons. - if (node->IsSeparator() || - (node->IsContainer() && node->mFaviconURI.IsEmpty())) { - _retval.Truncate(0); - return NS_OK; - } - - // For consistency, we always return a favicon for non-containers, even if - // it is just the default one. - nsFaviconService* faviconService = nsFaviconService::GetFaviconService(); - NS_ENSURE_TRUE(faviconService, NS_ERROR_NO_INTERFACE); - - nsCAutoString spec; - faviconService->GetFaviconSpecForIconString( - mVisibleElements[row]->mFaviconURI, spec); - CopyUTF8toUTF16(spec, _retval); - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::GetProgressMode (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::GetProgressMode(PRInt32 row, - nsITreeColumn* col, - PRInt32* _retval) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - - -// nsNavHistoryResultTreeViewer::GetCellValue (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::GetCellValue( - PRInt32 row, nsITreeColumn *col, nsAString& _retval) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - - -// nsNavHistoryResultTreeViewer::GetCellText (nsITreeView) - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::GetCellText(PRInt32 row, - nsITreeColumn *col, - nsAString& _retval) -{ - if (row < 0 || row >= PRInt32(mVisibleElements.Length())) - return NS_ERROR_INVALID_ARG; - PRInt32 colIndex; - col->GetIndex(&colIndex); - - nsNavHistoryResultNode* node = mVisibleElements[row]; - - switch (GetColumnType(col)) { - case Column_Title: - { - // title: normally, this is just the title, but we don't want empty - // items in the tree view so return a special string if the title is - // empty. Do it here so that callers can still get at the 0 length title - // if they go through the "result" API. - if (node->IsSeparator()) { - _retval.Truncate(0); - } else if (! node->mTitle.IsEmpty()) { - _retval = NS_ConvertUTF8toUTF16(node->mTitle); - } else { - nsXPIDLString value; - nsresult rv = - nsNavHistory::GetHistoryService()->GetBundle()->GetStringFromName( - NS_LITERAL_STRING("noTitle").get(), getter_Copies(value)); - NS_ENSURE_SUCCESS(rv, rv); - _retval = value; - } -#if 0 - // this code is for debugging bookmark indices. It will prepend the - // bookmark index to the title of each item. - nsString newTitle; - newTitle.AssignLiteral("("); - newTitle.AppendInt(node->mBookmarkIndex); - newTitle.AppendLiteral(") "); - newTitle.Append(_retval); - _retval = newTitle; -#endif - break; - } - case Column_URI: - { - if (node->IsURI()) - _retval = NS_ConvertUTF8toUTF16(node->mURI); - else - _retval.Truncate(0); - break; - } - case Column_Date: - { - if (node->mTime == 0 || ! node->IsURI()) { - // hosts and days shouldn't have a value for the date column. Actually, - // you could argue this point, but looking at the results, seeing the - // most recently visited date is not what I expect, and gives me no - // information I know how to use. Only show this for URI-based items. - _retval.Truncate(0); - } else { - if (GetRowSessionStatus(row) != Session_Continue) - return FormatFriendlyTime(node->mTime, _retval); - _retval.Truncate(0); - break; - } - break; - } - case Column_VisitCount: - { - if (node->IsSeparator()) { - _retval.Truncate(0); - } else { - _retval = NS_ConvertUTF8toUTF16(nsPrintfCString("%d", node->mAccessCount)); - } - break; - } - default: - return NS_ERROR_INVALID_ARG; - } - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::SetTree (nsITreeView) -// -// This will get called when this view is attached to the tree to tell us -// the box object, AND when the tree is being destroyed, with NULL. -// -// @see SetResult - -NS_IMETHODIMP nsNavHistoryResultTreeViewer::SetTree(nsITreeBoxObject* tree) -{ - PRBool hasOldTree = (mTree != nsnull); - mTree = tree; - - // do this before detaching from result when there is no tree. This ensures - // that the visible indices of the elements in the result have been set to -1 - FinishInit(); - - if (! tree && hasOldTree && mResult) { - // detach from result when we are detaching from the tree. This breaks the - // reference cycle between us and the result. - mResult->SetViewer(nsnull); - } - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::ToggleOpenState (nsITreeView) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::ToggleOpenState(PRInt32 row) -{ - if (! mResult) - return NS_ERROR_UNEXPECTED; - if (row < 0 || row >= PRInt32(mVisibleElements.Length())) - return NS_ERROR_INVALID_ARG; - - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryResultViewObserver, - OnToggleOpenState(row)) - - nsNavHistoryResultNode* node = mVisibleElements[row]; - if (! node->IsContainer()) - return NS_OK; // not a container, nothing to do - nsNavHistoryContainerResultNode* container = node->GetAsContainer(); - - nsresult rv; - if (container->mExpanded) - rv = container->CloseContainer(); - else - rv = container->OpenContainer(); - NS_ENSURE_SUCCESS(rv, rv); - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::CycleHeader (nsITreeView) -// -// If we already sorted by that column, the sorting is reversed, otherwise -// we use the default sorting direction for that data type. - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::CycleHeader(nsITreeColumn* col) -{ - if (! mResult) - return NS_ERROR_UNEXPECTED; - - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryResultViewObserver, - OnCycleHeader(col)) - - PRInt32 colIndex; - col->GetIndex(&colIndex); - - // Sometimes you want a tri-state sorting, and sometimes you don't. This - // rule allows tri-state sorting when the root node is a folder. This will - // catch the most common cases. When you are looking at folders, you want - // the third state to reset the sorting to the natural bookmark order. - // When you are looking at history, that third state has no meaning so we - // try to disallow it. - // - // The problem occurs when you have a query that results in bookmark folders. - // One example of this is the subscriptions view. In these cases, this rule - // doesn't allow you to sort those sub-folders by their natural order. - PRBool allowTriState = PR_FALSE; - if (mResult->mRootNode->IsFolder()) - allowTriState = PR_TRUE; - - PRInt32 oldSort = mResult->mSortingMode; - PRInt32 newSort; - switch (GetColumnType(col)) { - case Column_Title: - if (oldSort == nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING) { - newSort = nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING; - } else { - if (allowTriState && oldSort == nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING) - newSort = nsINavHistoryQueryOptions::SORT_BY_NONE; - else - newSort = nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING; - } - break; - case Column_URI: - if (oldSort == nsINavHistoryQueryOptions::SORT_BY_URI_ASCENDING) { - newSort = nsINavHistoryQueryOptions::SORT_BY_URI_DESCENDING; - } else { - if (allowTriState && oldSort == nsINavHistoryQueryOptions::SORT_BY_URI_DESCENDING) - newSort = nsINavHistoryQueryOptions::SORT_BY_NONE; - else - newSort = nsINavHistoryQueryOptions::SORT_BY_URI_ASCENDING; - } - break; - case Column_Date: - if (oldSort == nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING) { - newSort = nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING; - } else { - if (allowTriState && oldSort == nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING) - newSort = nsINavHistoryQueryOptions::SORT_BY_NONE; - else - newSort = nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING; - } - break; - case Column_VisitCount: - // visit count default is unusual because we sort by descending by default - // because you are most likely to be looking for highly visited sites when - // you click it - if (oldSort == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING) { - newSort = nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING; - } else { - if (allowTriState && oldSort == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING) - newSort = nsINavHistoryQueryOptions::SORT_BY_NONE; - else - newSort = nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING; - } - break; - default: - return NS_ERROR_INVALID_ARG; - } - return mResult->SetSortingMode(newSort); -} - - -// nsNavHistoryResultTreeViewer::SelectionChanged (nsITreeView) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::SelectionChanged() -{ - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryResultViewObserver, - OnSelectionChanged()) - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::CycleCell (nsITreeView) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::CycleCell(PRInt32 row, nsITreeColumn* col) -{ - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryResultViewObserver, - OnCycleCell(row, col)) - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::IsEditable (nsITreeView) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::IsEditable(PRInt32 row, nsITreeColumn* col, - PRBool* _retval) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -// nsNavHistoryResultTreeViewer::IsSelectable (nsITreeView) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::IsSelectable(PRInt32 row, nsITreeColumn* col, - PRBool* _retval) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - - -// nsNavHistoryResultTreeViewer::SetCellValue (nsITreeView) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::SetCellValue(PRInt32 row, nsITreeColumn* col, - const nsAString & value) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - - -// nsNavHistoryResultTreeViewer::SetCellText (nsITreeView) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::SetCellText(PRInt32 row, nsITreeColumn* col, - const nsAString & value) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - - -// nsNavHistoryResultTreeViewer::PerformAction (nsITreeView) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::PerformAction(const PRUnichar* action) -{ - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryResultViewObserver, - OnPerformAction(action)) - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::PerformActionOnRow (nsITreeView) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::PerformActionOnRow(const PRUnichar* action, - PRInt32 row) -{ - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryResultViewObserver, - OnPerformActionOnRow(action, row)) - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::PerformActionOnCell (nsITreeView) - -NS_IMETHODIMP -nsNavHistoryResultTreeViewer::PerformActionOnCell(const PRUnichar* action, - PRInt32 row, - nsITreeColumn* col) -{ - ENUMERATE_WEAKARRAY(mObservers, nsINavHistoryResultViewObserver, - OnPerformActionOnCell(action, row, col)) - return NS_OK; -} - - -// nsNavHistoryResultTreeViewer::FormatFriendlyTime - -nsresult -nsNavHistoryResultTreeViewer::FormatFriendlyTime(PRTime aTime, - nsAString& aResult) -{ - // use navHistory's GetNow function for performance - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); - PRTime now = history->GetNow(); - - const PRInt64 ago = now - aTime; - - /* - * This code generates strings like "X minutes ago" when the time is very - * recent. This looks much nicer, but we will have to redraw the list - * periodically. I also found it a little strange watching the numbers in - * the list change as I browse. For now, I'll leave this commented out, - * perhaps we can revisit whether it is a good idea to do this and put in - * the proper auto-refresh code so the numbers stay correct. - * - * To enable, you'll need to put these in places.properties: - * 0MinutesAgo=<1 minute ago - * 1MinutesAgo=1 minute ago - * XMinutesAgo=%S minutes ago - - static const PRInt64 minuteThreshold = (PRInt64)1000000 * 60 * 60; - if (ago > -10000000 && ago < minuteThreshold) { - // show "X minutes ago" - PRInt64 minutesAgo = NSToIntFloor( - NS_STATIC_CAST(float, (ago / 1000000)) / 60.0); - - nsXPIDLString resultString; - if (minutesAgo == 0) { - nsresult rv = mBundle->GetStringFromName( - NS_LITERAL_STRING("0MinutesAgo").get(), getter_Copies(resultString)); - NS_ENSURE_SUCCESS(rv, rv); - } else if (minutesAgo == 1) { - nsresult rv = mBundle->GetStringFromName( - NS_LITERAL_STRING("1MinutesAgo").get(), getter_Copies(resultString)); - NS_ENSURE_SUCCESS(rv, rv); - } else { - nsAutoString minutesAgoString; - minutesAgoString.AppendInt(minutesAgo); - const PRUnichar* stringPtr = minutesAgoString.get(); - nsresult rv = mBundle->FormatStringFromName( - NS_LITERAL_STRING("XMinutesAgo").get(), - &stringPtr, 1, getter_Copies(resultString)); - NS_ENSURE_SUCCESS(rv, rv); - } - aResult = resultString; - return NS_OK; - } */ - - // Check if it is today and only display the time. Only bother checking for - // today if it's within the last 24 hours, since computing midnight is not - // really cheap. Sometimes we may get dates in the future, so always show - // those. Note the 10s slop in case the cached GetNow value in NavHistory is - // out-of-date. - nsDateFormatSelector dateFormat = nsIScriptableDateFormat::dateFormatShort; - if (ago > -10000000 && ago < (PRInt64)1000000 * 24 * 60 * 60) { - PRExplodedTime explodedTime; - PR_ExplodeTime(aTime, PR_LocalTimeParameters, &explodedTime); - explodedTime.tm_min = - explodedTime.tm_hour = - explodedTime.tm_sec = - explodedTime.tm_usec = 0; - PRTime midnight = PR_ImplodeTime(&explodedTime); - if (aTime > midnight) - dateFormat = nsIScriptableDateFormat::dateFormatNone; - } - nsAutoString resultString; - history->GetDateFormatter()->FormatPRTime(history->GetLocale(), dateFormat, - nsIScriptableDateFormat::timeFormatNoSeconds, - aTime, resultString); - aResult = resultString; - return NS_OK; -} diff --git a/mozilla/browser/components/places/src/nsNavHistoryResult.h b/mozilla/browser/components/places/src/nsNavHistoryResult.h deleted file mode 100644 index 590fa7cf9c5..00000000000 --- a/mozilla/browser/components/places/src/nsNavHistoryResult.h +++ /dev/null @@ -1,803 +0,0 @@ -/* -*- 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 code. - * - * 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): - * Brett Wilson (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 ***** */ - -/** - * The definitions of objects that make up a history query result set. This file - * should only be included by nsNavHistory.h, include that if you want these - * classes. - */ - -#ifndef nsNavHistoryResult_h_ -#define nsNavHistoryResult_h_ - -#include "nsTArray.h" -#include "nsInterfaceHashtable.h" -#include "nsDataHashtable.h" - -class nsNavHistory; -class nsIDateTimeFormat; -class nsIWritablePropertyBag; -class nsNavHistoryQuery; -class nsNavHistoryQueryOptions; - -class nsNavHistoryContainerResultNode; -class nsNavHistoryFolderResultNode; -class nsNavHistoryQueryResultNode; -class nsNavHistoryVisitResultNode; - -/** - * hashkey wrapper using PRInt64 KeyType - * - * @see nsTHashtable::EntryType for specification - * - * This just truncates the 64-bit int to a 32-bit one for using a hash number. - * It is used for bookmark folder IDs, which should be way less than 2^32. - */ -class nsTrimInt64HashKey : public PLDHashEntryHdr -{ -public: - typedef const PRInt64& KeyType; - typedef const PRInt64* KeyTypePointer; - - nsTrimInt64HashKey(KeyTypePointer aKey) : mValue(*aKey) { } - nsTrimInt64HashKey(const nsTrimInt64HashKey& toCopy) : mValue(toCopy.mValue) { } - ~nsTrimInt64HashKey() { } - - KeyType GetKey() const { return mValue; } - KeyTypePointer GetKeyPointer() const { return &mValue; } - PRBool KeyEquals(KeyTypePointer aKey) const { return *aKey == mValue; } - - static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } - static PLDHashNumber HashKey(KeyTypePointer aKey) - { return NS_STATIC_CAST(PRUint32, (*aKey) & PR_UINT32_MAX); } - enum { ALLOW_MEMMOVE = PR_TRUE }; - -private: - const PRInt64 mValue; -}; - - -// Declare methods for implementing nsINavBookmarkObserver -// and nsINavHistoryObserver (some methods, such as BeginUpdateBatch overlap) -#define NS_DECL_BOOKMARK_HISTORY_OBSERVER \ - NS_DECL_NSINAVBOOKMARKOBSERVER \ - NS_IMETHOD OnVisit(nsIURI* aURI, PRInt64 aVisitId, PRTime aTime, \ - PRInt64 aSessionId, PRInt64 aReferringId, \ - PRUint32 aTransitionType); \ - NS_IMETHOD OnTitleChanged(nsIURI* aURI, const nsAString& aPageTitle, \ - const nsAString& aUserTitle, \ - PRBool aIsUserTitleChanged); \ - NS_IMETHOD OnDeleteURI(nsIURI *aURI); \ - NS_IMETHOD OnClearHistory(); \ - NS_IMETHOD OnPageChanged(nsIURI *aURI, PRUint32 aWhat, \ - const nsAString &aValue); \ - NS_IMETHOD OnPageExpired(nsIURI* aURI, PRTime aVisitTime, \ - PRBool aWholeEntry); - - -// nsNavHistoryResult -// -// nsNavHistory creates this object and fills in mChildren (by getting -// it through GetTopLevel()). Then FilledAllResults() is called to finish -// object initialization. -// -// This object implements nsITreeView so you can just set it to a tree -// view and it will work. This object also observes the necessary history -// and bookmark events to keep itself up-to-date. - -#define NS_NAVHISTORYRESULT_IID \ - { 0x455d1d40, 0x1b9b, 0x40e6, { 0xa6, 0x41, 0x8b, 0xb7, 0xe8, 0x82, 0x23, 0x87 } } - -class nsNavHistoryResult : public nsSupportsWeakReference, - public nsINavHistoryResult, - public nsINavBookmarkObserver, - public nsINavHistoryObserver -{ -public: - static nsresult NewHistoryResult(nsINavHistoryQuery** aQueries, - PRUint32 aQueryCount, - nsNavHistoryQueryOptions* aOptions, - nsNavHistoryContainerResultNode* aRoot, - nsNavHistoryResult** result); - - // the tree viewer can go faster if it can bypass XPCOM - friend class nsNavHistoryResultTreeViewer; - -#ifdef MOZILLA_1_8_BRANCH - NS_DEFINE_STATIC_IID_ACCESSOR(NS_NAVHISTORYRESULT_IID) -#else - NS_DECLARE_STATIC_IID_ACCESSOR(NS_NAVHISTORYRESULT_IID) -#endif - - nsresult PropertyBagFor(nsISupports* aObject, - nsIWritablePropertyBag** aBag); - - NS_DECL_ISUPPORTS - NS_DECL_NSINAVHISTORYRESULT - NS_DECL_BOOKMARK_HISTORY_OBSERVER - - void AddEverythingObserver(nsNavHistoryQueryResultNode* aNode); - void AddBookmarkObserver(nsNavHistoryFolderResultNode* aNode, PRInt64 aFolder); - void RemoveEverythingObserver(nsNavHistoryQueryResultNode* aNode); - void RemoveBookmarkObserver(nsNavHistoryFolderResultNode* aNode, PRInt64 aFolder); - - // returns the view. NOT-ADDREFED. May be NULL if there is no view - nsINavHistoryResultViewer* GetView() const - { return mView; } - -public: - // two-stage init, use NewHistoryResult to construct - nsNavHistoryResult(nsNavHistoryContainerResultNode* mRoot); - ~nsNavHistoryResult(); - nsresult Init(nsINavHistoryQuery** aQueries, - PRUint32 aQueryCount, - nsNavHistoryQueryOptions *aOptions); - - nsRefPtr mRootNode; - - nsCOMArray mQueries; - nsCOMPtr mOptions; - - // One of nsNavHistoryQueryOptions.SORY_BY_* This is initialized to mOptions.sortingMode, - // but may be overridden if the user clicks on one of the columns. - PRUint32 mSortingMode; - - nsCOMPtr mView; - - // property bags for all result nodes, see PropertyBagFor - nsInterfaceHashtable mPropertyBags; - - // node observers - PRBool mIsHistoryObserver; - PRBool mIsBookmarksObserver; - nsTArray mEverythingObservers; - typedef nsTArray FolderObserverList; - nsDataHashtable mBookmarkObservers; - FolderObserverList* BookmarkObserversForId(PRInt64 aFolderId, PRBool aCreate); - - void RecursiveExpandCollapse(nsNavHistoryContainerResultNode* aContainer, - PRBool aExpand); - - void InvalidateTree(); -}; - - -// nsNavHistoryResultNode -// -// This is the base class for every node in a result set. The result itself -// is a node (nsNavHistoryResult inherits from this), as well as every -// leaf and branch on the tree. - -#define NS_NAVHISTORYRESULTNODE_IID \ - {0x54b61d38, 0x57c1, 0x11da, {0x95, 0xb8, 0x00, 0x13, 0x21, 0xc9, 0xf6, 0x9e}} - -// These are all the simple getters, they can be used for the result node -// implementation and all subclasses. More complex are GetIcon, GetParent -// (which depends on the definition of container result node), and GetUri -// (which is overridded for lazy construction for some containers). -#define NS_IMPLEMENT_SIMPLE_RESULTNODE \ - NS_IMETHOD GetTitle(nsACString& aTitle) \ - { aTitle = mTitle; return NS_OK; } \ - NS_IMETHOD GetAccessCount(PRUint32* aAccessCount) \ - { *aAccessCount = mAccessCount; return NS_OK; } \ - NS_IMETHOD GetTime(PRTime* aTime) \ - { *aTime = mTime; return NS_OK; } \ - NS_IMETHOD GetIndentLevel(PRUint32* aIndentLevel) \ - { *aIndentLevel = mIndentLevel; return NS_OK; } \ - NS_IMETHOD GetViewIndex(PRInt32* aViewIndex) \ - { *aViewIndex = mViewIndex; return NS_OK; } \ - NS_IMETHOD SetViewIndex(PRInt32 aViewIndex) \ - { mViewIndex = aViewIndex; return NS_OK; } \ - NS_IMETHOD GetBookmarkIndex(PRInt32* aIndex) \ - { *aIndex = mBookmarkIndex; return NS_OK; } - -// This is used by the base classes instead of -// NS_FORWARD_NSINAVHISTORYRESULTNODE(nsNavHistoryResultNode) because they -// need to redefine GetType and GetUri rather than forwarding them. This -// implements all the simple getters instead of forwarding because they are so -// short and we can save a virtual function call. -// -// (GetUri is redefined only by QueryResultNode and FolderResultNode because -// the queries might not necessarily be parsed. The rest just return the node's -// buffer.) -#define NS_FORWARD_COMMON_RESULTNODE_TO_BASE \ - NS_IMPLEMENT_SIMPLE_RESULTNODE \ - NS_IMETHOD GetIcon(nsIURI** aIcon) \ - { return nsNavHistoryResultNode::GetIcon(aIcon); } \ - NS_IMETHOD GetParent(nsINavHistoryContainerResultNode** aParent) \ - { return nsNavHistoryResultNode::GetParent(aParent); } \ - NS_IMETHOD GetPropertyBag(nsIWritablePropertyBag** aBag) \ - { return nsNavHistoryResultNode::GetPropertyBag(aBag); } - -class nsNavHistoryResultNode : public nsINavHistoryResultNode -{ -public: - nsNavHistoryResultNode(const nsACString& aURI, const nsACString& aTitle, - PRUint32 aAccessCount, PRTime aTime, - const nsACString& aIconURI); - virtual ~nsNavHistoryResultNode() {} - -#ifdef MOZILLA_1_8_BRANCH - NS_DEFINE_STATIC_IID_ACCESSOR(NS_NAVHISTORYRESULTNODE_IID) -#else - NS_DECLARE_STATIC_IID_ACCESSOR(NS_NAVHISTORYRESULTNODE_IID) -#endif - - NS_DECL_ISUPPORTS - NS_IMPLEMENT_SIMPLE_RESULTNODE - NS_IMETHOD GetIcon(nsIURI** aIcon); - NS_IMETHOD GetParent(nsINavHistoryContainerResultNode** aParent); - NS_IMETHOD GetPropertyBag(nsIWritablePropertyBag** aBag); - NS_IMETHOD GetType(PRUint32* type) - { *type = nsNavHistoryResultNode::RESULT_TYPE_URI; return NS_OK; } - NS_IMETHOD GetUri(nsACString& aURI) - { aURI = mURI; return NS_OK; } - - virtual void OnRemoving(); - -public: - - nsNavHistoryResult* GetResult(); - nsNavHistoryQueryOptions* GetGeneratingOptions(); - - // These functions test the type. We don't use a virtual function since that - // would take a vtable slot for every one of (potentially very many) nodes. - // Note that GetType() already has a vtable slot because its on the iface. - PRBool IsTypeContainer(PRUint32 type) { - return (type == nsINavHistoryResultNode::RESULT_TYPE_HOST || - type == nsINavHistoryResultNode::RESULT_TYPE_REMOTE_CONTAINER || - type == nsINavHistoryResultNode::RESULT_TYPE_QUERY || - type == nsINavHistoryResultNode::RESULT_TYPE_FOLDER); - } - PRBool IsContainer() { - PRUint32 type; - GetType(&type); - return IsTypeContainer(type); - } - static PRBool IsTypeQuerySubcontainer(PRUint32 type) { - // Tests containers that are inside queries that really belong to the query - // itself, and is used when recursively updating a query. This currently - // includes only host containers, but may be extended to support things - // like days or other criteria. It doesn't include other queries and folders. - return (type == nsINavHistoryResultNode::RESULT_TYPE_HOST); - } - PRBool IsQuerySubcontainer() { - PRUint32 type; - GetType(&type); - return IsTypeQuerySubcontainer(type); - } - static PRBool IsTypeURI(PRUint32 type) { - return (type == nsINavHistoryResultNode::RESULT_TYPE_URI || - type == nsINavHistoryResultNode::RESULT_TYPE_VISIT || - type == nsINavHistoryResultNode::RESULT_TYPE_FULL_VISIT); - } - PRBool IsURI() { - PRUint32 type; - GetType(&type); - return IsTypeURI(type); - } - static PRBool IsTypeVisit(PRUint32 type) { - return (type == nsINavHistoryResultNode::RESULT_TYPE_VISIT || - type == nsINavHistoryResultNode::RESULT_TYPE_FULL_VISIT); - } - PRBool IsVisit() { - PRUint32 type; - GetType(&type); - return IsTypeVisit(type); - } - static PRBool IsTypeFolder(PRUint32 type) { - return (type == nsINavHistoryResultNode::RESULT_TYPE_FOLDER); - } - PRBool IsFolder() { - PRUint32 type; - GetType(&type); - return IsTypeFolder(type); - } - static PRBool IsTypeQuery(PRUint32 type) { - return (type == nsINavHistoryResultNode::RESULT_TYPE_QUERY); - } - PRBool IsQuery() { - PRUint32 type; - GetType(&type); - return IsTypeQuery(type); - } - PRBool IsSeparator() { - PRUint32 type; - GetType(&type); - return (type == nsINavHistoryResultNode::RESULT_TYPE_SEPARATOR); - } - nsNavHistoryContainerResultNode* GetAsContainer() { - NS_ASSERTION(IsContainer(), "Not a container"); - return NS_REINTERPRET_CAST(nsNavHistoryContainerResultNode*, this); - } - nsNavHistoryVisitResultNode* GetAsVisit() { - NS_ASSERTION(IsVisit(), "Not a visit"); - return NS_REINTERPRET_CAST(nsNavHistoryVisitResultNode*, this); - } - nsNavHistoryFolderResultNode* GetAsFolder() { - NS_ASSERTION(IsFolder(), "Not a folder"); - return NS_REINTERPRET_CAST(nsNavHistoryFolderResultNode*, this); - } - nsNavHistoryQueryResultNode* GetAsQuery() { - NS_ASSERTION(IsQuery(), "Not a query"); - return NS_REINTERPRET_CAST(nsNavHistoryQueryResultNode*, this); - } - - nsNavHistoryContainerResultNode* mParent; - nsCString mURI; // not necessarily valid for containers, call GetUri - nsCString mTitle; - PRUint32 mAccessCount; - PRInt64 mTime; - nsCString mFaviconURI; - PRInt32 mBookmarkIndex; - - // The indent level of this node. The root node will have a value of -1. The - // root's children will have a value of 0, and so on. - PRInt32 mIndentLevel; - - // Value used by the view for whatever it wants. For the built-in tree view, - // this is the index into the result's mVisibleElements list of this element. - // This is -1 if it is invalid. For items, >= 0 can be used to determine if - // the node is visible in the list or not. For folders, call IsVisible, since - // they can be the root node which is not itself visible, but its children - // are. - PRInt32 mViewIndex; -}; - -#ifndef MOZILLA_1_8_BRANCH -NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHistoryResultNode, NS_NAVHISTORYRESULTNODE_IID) -#endif - -// nsNavHistoryVisitResultNode - -#define NS_IMPLEMENT_VISITRESULT \ - NS_IMETHOD GetUri(nsACString& aURI) { aURI = mURI; return NS_OK; } \ - NS_IMETHOD GetSessionId(PRInt64* aSessionId) \ - { *aSessionId = mSessionId; return NS_OK; } - -class nsNavHistoryVisitResultNode : public nsNavHistoryResultNode, - public nsINavHistoryVisitResultNode -{ -public: - nsNavHistoryVisitResultNode(const nsACString& aURI, const nsACString& aTitle, - PRUint32 aAccessCount, PRTime aTime, - const nsACString& aIconURI, PRInt64 aSession); - - NS_DECL_ISUPPORTS_INHERITED - NS_FORWARD_COMMON_RESULTNODE_TO_BASE - NS_IMETHOD GetType(PRUint32* type) - { *type = nsNavHistoryResultNode::RESULT_TYPE_VISIT; return NS_OK; } - NS_IMPLEMENT_VISITRESULT - -public: - - PRInt64 mSessionId; -}; - - -// nsNavHistoryFullVisitResultNode - -#define NS_IMPLEMENT_FULLVISITRESULT \ - NS_IMPLEMENT_VISITRESULT \ - NS_IMETHOD GetVisitId(PRInt64 *aVisitId) \ - { *aVisitId = mVisitId; return NS_OK; } \ - NS_IMETHOD GetReferringVisitId(PRInt64 *aReferringVisitId) \ - { *aReferringVisitId = mReferringVisitId; return NS_OK; } \ - NS_IMETHOD GetTransitionType(PRInt32 *aTransitionType) \ - { *aTransitionType = mTransitionType; return NS_OK; } - -class nsNavHistoryFullVisitResultNode : public nsNavHistoryVisitResultNode, - public nsINavHistoryFullVisitResultNode -{ -public: - nsNavHistoryFullVisitResultNode( - const nsACString& aURI, const nsACString& aTitle, PRUint32 aAccessCount, - PRTime aTime, const nsACString& aIconURI, PRInt64 aSession, - PRInt64 aVisitId, PRInt64 aReferringVisitId, PRInt32 aTransitionType); - - NS_DECL_ISUPPORTS_INHERITED - NS_FORWARD_COMMON_RESULTNODE_TO_BASE - NS_IMETHOD GetType(PRUint32* type) - { *type = nsNavHistoryResultNode::RESULT_TYPE_FULL_VISIT; return NS_OK; } - NS_IMPLEMENT_FULLVISITRESULT - -public: - PRInt64 mVisitId; - PRInt64 mReferringVisitId; - PRInt32 mTransitionType; -}; - - -// nsNavHistoryContainerResultNode -// -// This is the base class for all nodes that can have children. It is -// overridden for nodes that are dynamically populated such as queries and -// folders. It is used directly for simple containers such as host groups -// in history views. - -// derived classes each provide their own implementation of has children and -// forward the rest to us using this macro -#define NS_FORWARD_CONTAINERNODE_EXCEPT_HASCHILDREN_AND_READONLY \ - NS_IMETHOD GetContainerOpen(PRBool *aContainerOpen) \ - { return nsNavHistoryContainerResultNode::GetContainerOpen(aContainerOpen); } \ - NS_IMETHOD SetContainerOpen(PRBool aContainerOpen) \ - { return nsNavHistoryContainerResultNode::SetContainerOpen(aContainerOpen); } \ - NS_IMETHOD GetChildCount(PRUint32 *aChildCount) \ - { return nsNavHistoryContainerResultNode::GetChildCount(aChildCount); } \ - NS_IMETHOD GetChild(PRUint32 index, nsINavHistoryResultNode **_retval) \ - { return nsNavHistoryContainerResultNode::GetChild(index, _retval); } \ - NS_IMETHOD GetRemoteContainerType(nsACString& aRemoteContainerType) \ - { return nsNavHistoryContainerResultNode::GetRemoteContainerType(aRemoteContainerType); } -/* Untested container API functions - NS_IMETHOD AppendURINode(const nsACString& aURI, const nsACString& aTitle, PRUint32 aAccessCount, PRTime aTime, const nsACString& aIconURI, nsINavHistoryResultNode **_retval) \ - { return nsNavHistoryContainerResultNode::AppendURINode(aURI, aTitle, aAccessCount, aTime, aIconURI, _retval); } \ - NS_IMETHOD AppendVisitNode(const nsACString& aURI, const nsACString & aTitle, PRUint32 aAccessCount, PRTime aTime, const nsACString & aIconURI, PRInt64 aSession, nsINavHistoryVisitResultNode **_retval) \ - { return nsNavHistoryContainerResultNode::AppendVisitNode(aURI, aTitle, aAccessCount, aTime, aIconURI, aSession, _retval); } \ - NS_IMETHOD AppendFullVisitNode(const nsACString& aURI, const nsACString & aTitle, PRUint32 aAccessCount, PRTime aTime, const nsACString & aIconURI, PRInt64 aSession, PRInt64 aVisitId, PRInt64 aReferringVisitId, PRInt32 aTransitionType, nsINavHistoryFullVisitResultNode **_retval) \ - { return nsNavHistoryContainerResultNode::AppendFullVisitNode(aURI, aTitle, aAccessCount, aTime, aIconURI, aSession, aVisitId, aReferringVisitId, aTransitionType, _retval); } \ - NS_IMETHOD AppendContainerNode(const nsACString & aTitle, const nsACString & aIconURI, PRUint32 aContainerType, const nsACString & aRemoteContainerType, nsINavHistoryContainerResultNode **_retval) \ - { return nsNavHistoryContainerResultNode::AppendContainerNode(aTitle, aIconURI, aContainerType, aRemoteContainerType, _retval); } \ - NS_IMETHOD AppendQueryNode(const nsACString& aQueryURI, const nsACString & aTitle, const nsACString & aIconURI, nsINavHistoryQueryResultNode **_retval) \ - { return nsNavHistoryContainerResultNode::AppendQueryNode(aQueryURI, aTitle, aIconURI, _retval); } \ - NS_IMETHOD AppendFolderNode(PRInt64 aFolderId, nsINavHistoryFolderResultNode **_retval) \ - { return nsNavHistoryContainerResultNode::AppendFolderNode(aFolderId, _retval); } \ - NS_IMETHOD ClearContents() \ - { return nsNavHistoryContainerResultNode::ClearContents(); } -*/ - -#define NS_NAVHISTORYCONTAINERRESULTNODE_IID \ - { 0x6e3bf8d3, 0x22aa, 0x4065, { 0x86, 0xbc, 0x37, 0x46, 0xb5, 0xb3, 0x2c, 0xe8 } } - -class nsNavHistoryContainerResultNode : public nsNavHistoryResultNode, - public nsINavHistoryContainerResultNode -{ -public: - nsNavHistoryContainerResultNode( - const nsACString& aURI, const nsACString& aTitle, - const nsACString& aIconURI, PRUint32 aContainerType, - PRBool aReadOnly, const nsACString& aRemoteContainerType); - -#ifdef MOZILLA_1_8_BRANCH - NS_DEFINE_STATIC_IID_ACCESSOR(NS_NAVHISTORYCONTAINERRESULTNODE_IID) -#else - NS_DECLARE_STATIC_IID_ACCESSOR(NS_NAVHISTORYCONTAINERRESULTNODE_IID) -#endif - - NS_DECL_ISUPPORTS_INHERITED - NS_FORWARD_COMMON_RESULTNODE_TO_BASE - NS_IMETHOD GetType(PRUint32* type) - { *type = mContainerType; return NS_OK; } - NS_IMETHOD GetUri(nsACString& aURI) - { aURI = mURI; return NS_OK; } - NS_DECL_NSINAVHISTORYCONTAINERRESULTNODE - -public: - - virtual void OnRemoving(); - - PRBool AreChildrenVisible(); - - // overridded by descendents to populate - virtual nsresult OpenContainer(); - nsresult CloseContainer(PRBool aUpdateView = PR_TRUE); - - // this points to the result that owns this container. All containers have - // their result pointer set so we can quickly get to the result without having - // to walk the tree. Yet, this also saves us from storing a million pointers - // for every leaf node to the result. - nsNavHistoryResult* mResult; - - // for example, RESULT_TYPE_HOST. Query and Folder results override GetType - // so this is not used, but is still kept in sync. - PRUint32 mContainerType; - - // when there are children, this stores the open state in the tree - // this is set to the default in the constructor - PRBool mExpanded; - - // Filled in by the result type generator in nsNavHistory - nsCOMArray mChildren; - - PRBool mChildrenReadOnly; - - // ID of a remote container interface that we can use GetService to get. - // This is empty to indicate there is no remote container service for this - // container (the common case). - nsCString mRemoteContainerType; - - void FillStats(); - void ReverseUpdateStats(PRInt32 aAccessCountChange); - - // sorting - typedef nsCOMArray::nsCOMArrayComparatorFunc SortComparator; - virtual PRUint32 GetSortType(); - static SortComparator GetSortingComparator(PRUint32 aSortType); - virtual void RecursiveSort(nsICollation* aCollation, - SortComparator aComparator); - PRUint32 FindInsertionPoint(nsNavHistoryResultNode* aNode, SortComparator aComparator); - PRBool DoesChildNeedResorting(PRUint32 aIndex, SortComparator aComparator); - - PR_STATIC_CALLBACK(int) SortComparison_Bookmark( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure); - PR_STATIC_CALLBACK(int) SortComparison_TitleLess( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure); - PR_STATIC_CALLBACK(int) SortComparison_TitleGreater( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure); - PR_STATIC_CALLBACK(int) SortComparison_DateLess( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure); - PR_STATIC_CALLBACK(int) SortComparison_DateGreater( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure); - PR_STATIC_CALLBACK(int) SortComparison_URILess( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure); - PR_STATIC_CALLBACK(int) SortComparison_URIGreater( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure); - PR_STATIC_CALLBACK(int) SortComparison_VisitCountLess( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure); - PR_STATIC_CALLBACK(int) SortComparison_VisitCountGreater( - nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure); - - // finding children: THESE DO NOT ADDREF - nsNavHistoryResultNode* FindChildURI(nsIURI* aURI, PRUint32* aNodeIndex) - { - nsCAutoString spec; - if (NS_FAILED(aURI->GetSpec(spec))) - return PR_FALSE; - return FindChildURI(spec, aNodeIndex); - } - nsNavHistoryResultNode* FindChildURI(const nsACString& aSpec, - PRUint32* aNodeIndex); - nsNavHistoryFolderResultNode* FindChildFolder(PRInt64 aFolderId, - PRUint32* aNodeIndex); - nsNavHistoryContainerResultNode* FindChildContainerByName(const nsACString& aTitle, - PRUint32* aNodeIndex); - // returns the index of the given node, -1 if not found - PRInt32 FindChild(nsNavHistoryResultNode* aNode) - { return mChildren.IndexOf(aNode); } - - nsresult InsertChildAt(nsNavHistoryResultNode* aNode, PRInt32 aIndex, - PRBool aIsTemporary = PR_FALSE); - nsresult InsertSortedChild(nsNavHistoryResultNode* aNode, - PRBool aIsTemporary = PR_FALSE); - void MergeResults(nsCOMArray* aNodes); - nsresult ReplaceChildURIAt(PRUint32 aIndex, nsNavHistoryResultNode* aNode); - nsresult RemoveChildAt(PRInt32 aIndex, PRBool aIsTemporary = PR_FALSE); - - PRBool CanRemoteContainersChange(); - - void RecursiveFindURIs(PRBool aOnlyOne, - nsNavHistoryContainerResultNode* aContainer, - const nsCString& aSpec, - nsCOMArray* aMatches); - void UpdateURIs(PRBool aRecursive, PRBool aOnlyOne, PRBool aUpdateSort, - const nsCString& aSpec, - void (*aCallback)(nsNavHistoryResultNode*,void*), - void* aClosure); - nsresult ChangeTitles(nsIURI* aURI, const nsACString& aNewTitle, - PRBool aRecursive, PRBool aOnlyOne); -}; - - -// nsNavHistoryQueryResultNode -// -// Overridden container type for complex queries over history and/or -// bookmarks. This keeps itself in sync by listening to history and -// bookmark notifications. - -class nsNavHistoryQueryResultNode : public nsNavHistoryContainerResultNode, - public nsINavHistoryQueryResultNode -{ -public: - nsNavHistoryQueryResultNode(const nsACString& aQueryURI, - const nsACString& aTitle, - const nsACString& aIconURI); - nsNavHistoryQueryResultNode(const nsACString& aTitle, - const nsACString& aIconURI, - const nsCOMArray& aQueries, - nsNavHistoryQueryOptions* aOptions); - - NS_DECL_ISUPPORTS_INHERITED - NS_FORWARD_COMMON_RESULTNODE_TO_BASE - NS_IMETHOD GetType(PRUint32* type) - { *type = nsNavHistoryResultNode::RESULT_TYPE_QUERY; return NS_OK; } - NS_IMETHOD GetUri(nsACString& aURI); // does special lazy creation - NS_FORWARD_CONTAINERNODE_EXCEPT_HASCHILDREN_AND_READONLY - NS_IMETHOD GetHasChildren(PRBool* aHasChildren); - NS_IMETHOD GetChildrenReadOnly(PRBool *aChildrenReadOnly) - { return nsNavHistoryContainerResultNode::GetChildrenReadOnly(aChildrenReadOnly); } - NS_DECL_NSINAVHISTORYQUERYRESULTNODE - - PRBool CanExpand(); - - virtual nsresult OpenContainer(); - - NS_DECL_BOOKMARK_HISTORY_OBSERVER - virtual void OnRemoving(); - -public: - // this constructs lazily mURI from mQueries and mOptions, call - // VerifyQueriesSerialized either this or mQueries/mOptions should be valid - nsresult VerifyQueriesSerialized(); - - // these may be constructed lazily from mURI, call VerifyQueriesParsed - // either this or mURI should be valid - nsCOMArray mQueries; - nsCOMPtr mOptions; - PRUint32 mLiveUpdate; // one of QUERYUPDATE_* in nsNavHistory.h - PRBool mHasSearchTerms; - nsresult VerifyQueriesParsed(); - - // this indicates whether the query contents are valid, they don't go away - // after the container is closed until a notification comes in - PRBool mContentsValid; - - PRBool mBatchInProgress; - - nsresult FillChildren(); - void ClearChildren(PRBool unregister); - nsresult Refresh(); - - virtual PRUint32 GetSortType(); -}; - - -// nsNavHistoryFolderResultNode -// -// Overridden container type for bookmark folders. It will keep the contents -// of the folder in sync with the bookmark service. - -class nsNavHistoryFolderResultNode : public nsNavHistoryContainerResultNode, - public nsINavHistoryFolderResultNode -{ -public: - nsNavHistoryFolderResultNode(const nsACString& aTitle, - nsNavHistoryQueryOptions* options, - PRInt64 aFolderId, - const nsACString& aRemoteContainerType); - - NS_DECL_ISUPPORTS_INHERITED - NS_FORWARD_COMMON_RESULTNODE_TO_BASE - NS_IMETHOD GetType(PRUint32* type) - { *type = nsNavHistoryResultNode::RESULT_TYPE_FOLDER; return NS_OK; } - NS_IMETHOD GetUri(nsACString& aURI); - NS_FORWARD_CONTAINERNODE_EXCEPT_HASCHILDREN_AND_READONLY - NS_IMETHOD GetHasChildren(PRBool* aHasChildren); - NS_IMETHOD GetChildrenReadOnly(PRBool *aChildrenReadOnly); - NS_DECL_NSINAVHISTORYQUERYRESULTNODE - - NS_IMETHOD GetFolderId(PRInt64* aFolderId) - { *aFolderId = mFolderId; return NS_OK; } - - virtual nsresult OpenContainer(); - - // This object implements a bookmark observer interface without deriving from - // the bookmark observers. This is called from the result's actual observer - // and it knows all observers are FolderResultNodes - NS_DECL_NSINAVBOOKMARKOBSERVER - - virtual void OnRemoving(); - -public: - - // this indicates whether the folder contents are valid, they don't go away - // after the container is closed until a notification comes in - PRBool mContentsValid; - - nsCOMPtr mOptions; - PRInt64 mFolderId; - - nsresult FillChildren(); - void ClearChildren(PRBool aUnregister); - nsresult Refresh(); - - PRBool StartIncrementalUpdate(); - void ReindexRange(PRInt32 aStartIndex, PRInt32 aEndIndex, PRInt32 aDelta); -}; - -// nsNavHistorySeparatorResultNode -// -// Separator result nodes do not hold any data. -class nsNavHistorySeparatorResultNode : public nsNavHistoryResultNode -{ -public: - nsNavHistorySeparatorResultNode(); - - NS_IMETHOD GetType(PRUint32* type) - { *type = nsNavHistoryResultNode::RESULT_TYPE_SEPARATOR; return NS_OK; } -}; - - -// nsNavHistoryResultTreeViewer -// - -class nsNavHistoryResultTreeViewer : public nsINavHistoryResultTreeViewer, - public nsITreeView -{ -public: - nsNavHistoryResultTreeViewer(); - virtual ~nsNavHistoryResultTreeViewer() {} - - NS_DECL_ISUPPORTS - NS_DECL_NSINAVHISTORYRESULTVIEWER - NS_DECL_NSINAVHISTORYRESULTTREEVIEWER - NS_DECL_NSITREEVIEW - -protected: - nsRefPtr mResult; - nsCOMPtr mTree; // will be null when no tree attached - nsCOMPtr mSelection; // may be null - - PRBool mCollapseDuplicates; - - // This value indicates whether we should try to compute session boundaries. - // It is cached so we don't have to compute it every time we want to get a - // row style. - PRBool mShowSessions; - void ComputeShowSessions(); - enum SessionStatus { Session_None, Session_Start, Session_Continue }; - SessionStatus GetRowSessionStatus(PRInt32 row); - - // This list is used to map rows to nodes. - typedef nsTArray< nsCOMPtr > VisibleList; - VisibleList mVisibleElements; - nsresult BuildVisibleList(); - nsresult BuildVisibleSection(nsNavHistoryContainerResultNode* aContainer, - VisibleList* aVisible, - PRUint32 aVisibleStartIndex); - PRUint32 CountVisibleRowsForItem(nsNavHistoryResultNode* aNode); - nsresult RefreshVisibleSection(nsNavHistoryContainerResultNode* aContainer); - - PRBool CanCollapseDuplicates(nsNavHistoryResultNode* aTop, - nsNavHistoryResultNode* aNext, - PRUint32* aShowThisOne); - - // external observers - nsMaybeWeakPtrArray mObservers; - - nsresult FinishInit(); - - // columns - enum ColumnType { Column_Unknown = -1, Column_Title, Column_URI, Column_Date, - Column_VisitCount }; - ColumnType GetColumnType(nsITreeColumn* col); - ColumnType SortTypeToColumnType(PRUint32 aSortType, - PRBool* aDescending = nsnull); - - nsresult FormatFriendlyTime(PRTime aTime, nsAString& aResult); -}; - -#endif // nsNavHistoryResult_h_