Bug 306640, Add generic annotation service (first pass)

r=annie.sullivan r=bryner
This commit is contained in:
brettw%gmail.com 2005-11-15 23:46:08 +00:00
parent f3033246a0
commit 89064c9e36
7 changed files with 880 additions and 28 deletions

View File

@ -47,6 +47,7 @@ MODULE = places
XPIDL_MODULE = places
XPIDLSRCS = \
nsIAnnotationService.idl \
nsINavHistory.idl \
$(NULL)

View File

@ -0,0 +1,208 @@
/* -*- 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 <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 ***** */
#include "nsISupports.idl"
#include "nsIURI.idl"
#include "nsIVariant.idl"
[scriptable, uuid(05537263-9bb4-45c8-ae96-461817f53972)]
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 expiratin, 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 long EXPIRE_SESSION = 0;
// for short-lived temporary data that you still want to outlast a session
const long 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 long 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 long EXPIRE_MONTHS = 3;
// For small, user-entered data like notes that should never expire.
const long EXPIRE_NEVER = 5;
/**
* 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, ":",
* or many other symbols.
*
* aExpiration is one of EXPIRE_* above. aFlags should be 0 for now, some
* flags will be defined in the future.
*/
void setAnnotation(in nsIURI aURI, in ACString aName, in nsIVariant aValue,
in long aFlags, in long aExpiration);
/**
* Sets an annotation just like setAnnotation, but takes a string as
* input, which will be more convenient for C++.
*/
void setAnnotationString(in nsIURI aURI, in ACString aName,
in AString aValue, in long aFlags,
in long aExpiration);
/**
* Sets an annotation just like setAnnotation, but takes an Int32 as input
* for convenience.
*/
void setAnnotationInt32(in nsIURI aURI, in ACString aName,
in PRInt32 aValue, in long aFlags,
in long aExpiration);
/*
* Sets an annotation just like setAnnotation, but takes binary data as
* input. You MUST supply a valid MIME type.
*/
void setAnnotationBinary(in nsIURI aURI, in ACString aName,
[const,array,size_is(aDataLen)] in octet aData,
in unsigned long aDataLen, in ACString aMimeType,
in long aFlags, in long aExpiration);
/**
* Retrieves the value of an existing annotation. Throws if the annotation
* does not exist.
*/
nsIVariant getAnnotation(in nsIURI aURI, in ACString aName);
/**
* Same as getAnnotation but a convenience function for C++ for strings.
*/
AString getAnnotationString(in nsIURI aURI, in ACString aName);
/**
* Same as getAnnotation but a convenience function for C++ 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 likely
* change in the future if we start caching stuff).
*/
PRInt32 getAnnotationInt32(in nsIURI aURI, in ACString aName);
/**
* Same as getAnnotation but for binary data. This also returns the
* MIME type.
*/
void getAnnotationBinary(in nsIURI aURI, in ACString aName,
[array,size_is(aDataLen)] out octet aData,
out unsigned long aDataLen,
out ACString 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 ACString aName,
out long aFlags, out long aExpiration,
out ACString aMimeType, out long aStorageType);
/**
* Get the names of all annotations for this URI.
*
* example JS:
* var annotations = annotator.getAnnotations(myURI, {});
*/
// IMPLEMENT ME
//void getAnnotations(in AString aURI, out unsigned long count,
// [retval, array, size_is(count)] out wstring result);
/**
* Get the values of several annotations with arbitrary URI/name pairs.
* There is some latency associated with each annotation query, so it is
* a good idea to use this function if it is possible for you to batch
* your requests together.
*
* This will return an array with the same number of values you requested.
* If the requested URI/name pair does not exist, the corresponding result
* element will be NULL.
*
* @param aURIList The list of URIs
*/
/* IMPLEMENT ME?
void getMultipleAnnotations([array, size_is(aCount)] in nsIURI aURIList,
[array, size_is(aCount)] in wstring aNameList, in unsigned long aCount,
out unsigned long aResultCount,
[retval, array, size_is(aResultCount)] out wstring aResultList);
*/
/**
* Test for annotation existance.
*/
boolean hasAnnotation(in nsIURI aURI, in ACString aName);
/**
* Removes a specific annotation. Succeeds even if the annotation is
* not found.
*/
void removeAnnotation(in nsIURI aURI, in ACString 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);
};

View File

@ -69,6 +69,7 @@ REQUIRES = xpcom \
LOCAL_INCLUDES = -I$(DEPTH)/toolkit/components/autocomplete/src
CPPSRCS = \
nsAnnotationService.cpp \
nsNavHistory.cpp \
$(NULL)

View File

@ -0,0 +1,457 @@
//* -*- 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 "mozIStorageValueArray.h"
#include "mozIStorageStatement.h"
#include "mozIStorageFunction.h"
#include "mozStorageHelper.h"
#include "nsIServiceManager.h"
#include "nsString.h"
const int nsAnnotationService::kAnnoIndex_ID = 0;
const int nsAnnotationService::kAnnoIndex_Page = 1;
const int nsAnnotationService::kAnnoIndex_Name = 2;
const int nsAnnotationService::kAnnoIndex_MimeType = 3;
const int nsAnnotationService::kAnnoIndex_Content = 4;
const int nsAnnotationService::kAnnoIndex_Flags = 5;
const int nsAnnotationService::kAnnoIndex_Expiration = 6;
static NS_DEFINE_CID(kmozStorageServiceCID, MOZ_STORAGE_SERVICE_CID);
static NS_DEFINE_CID(kmozStorageConnectionCID, MOZ_STORAGE_CONNECTION_CID);
NS_IMPL_ISUPPORTS1(nsAnnotationService,
nsIAnnotationService);
// nsAnnotationService::nsAnnotationService
nsAnnotationService::nsAnnotationService()
{
}
// nsAnnotationService::~nsAnnotationService
nsAnnotationService::~nsAnnotationService()
{
}
// nsAnnotationService::Init
nsresult
nsAnnotationService::Init()
{
nsresult rv;
nsNavHistory* history = nsNavHistory::GetHistoryService();
if (! history)
return NS_ERROR_FAILURE;
mDBConn = history->GetStorageConnection();
// create the database
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_anno (anno_id INTEGER PRIMARY KEY, page INTEGER NOT NULL, name VARCHAR(32) NOT NULL, mime_type VARCHAR(32) DEFAULT NULL, content LONGVARCHAR, flags INTEGER DEFAULT 0, expiration INTEGER DEFAULT 0)"));
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE INDEX moz_anno_pageindex ON moz_anno (page)"));
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE INDEX moz_anno_nameindex ON moz_anno (name)"));
// annotation statements
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);
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("SELECT * FROM moz_anno WHERE page = ?1 AND name = ?2"),
getter_AddRefs(mDBGetAnnotation));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("SELECT a.anno_id, a.page, a.name, 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 = ?2"),
getter_AddRefs(mDBGetAnnotationFromURI));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_anno (page, name, mime_type, content, flags, expiration) VALUES (?2, ?3, ?4, ?5, ?6, ?7)"),
getter_AddRefs(mDBAddAnnotation));
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// nsAnnotationService::SetAnnotation
NS_IMETHODIMP
nsAnnotationService::SetAnnotation(nsIURI* aURI,
const nsACString& aName,
nsIVariant *aValue,
PRInt32 aFlags, PRInt32 aExpiration)
{
if (! aValue)
return NS_ERROR_INVALID_ARG;
nsString stringValue;
nsresult rv = aValue->GetAsAString(stringValue);
NS_ENSURE_SUCCESS(rv, rv);
return SetAnnotationString(aURI, aName, stringValue, aFlags, aExpiration);
}
// 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);
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();
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);
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();
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);
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();
return NS_OK;
}
// nsAnnotationService::GetAnnotation
NS_IMETHODIMP
nsAnnotationService::GetAnnotation(nsIURI* aURI,
const nsACString& aName,
nsIVariant** _retval)
{
nsString stringValue;
nsresult rv = GetAnnotationString(aURI, aName, stringValue);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIWritableVariant> var = do_CreateInstance("@mozilla.org/variant;1");
if (! var)
return NS_ERROR_FAILURE;
rv = var->SetWritable(PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
rv = var->SetAsAString(stringValue);
NS_ENSURE_SUCCESS(rv, rv);
*_retval = var;
(*_retval)->AddRef();
return NS_OK;
}
// nsAnnotationService::GetAnnotationString
NS_IMETHODIMP
nsAnnotationService::GetAnnotationString(nsIURI* aURI,
const nsACString& aName,
nsAString& _retval)
{
nsresult rv = StartGetAnnotationFromURI(aURI, aName);
NS_ENSURE_SUCCESS(rv, 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);
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBGetAnnotationFromURI->GetInt32(kAnnoIndex_Content, _retval);
mDBGetAnnotationFromURI->Reset();
return rv;
}
// nsAnnotationService::GetAnnotationBinary
NS_IMETHODIMP
nsAnnotationService::GetAnnotationBinary(nsIURI* aURI,
const nsACString& aName,
PRUint8** aData, PRUint32* aDataLen,
nsACString& aMimeType)
{
nsresult rv = StartGetAnnotationFromURI(aURI, aName);
NS_ENSURE_SUCCESS(rv, 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);
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper resetter(mDBGetAnnotationFromURI);
rv = mDBGetAnnotationFromURI->GetInt32(kAnnoIndex_Flags, aFlags);
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBGetAnnotationFromURI->GetInt32(kAnnoIndex_Expiration, aExpiration);
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBGetAnnotationFromURI->GetUTF8String(kAnnoIndex_MimeType, aMimeType);
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBGetAnnotationFromURI->GetTypeOfIndex(kAnnoIndex_Content, aStorageType);
return rv;
}
// 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
NS_IMETHODIMP
nsAnnotationService::RemoveAnnotation(nsIURI* aURI,
const nsACString& aName)
{
// FIXME
return NS_ERROR_NOT_IMPLEMENTED;
}
// 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 it's 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;
rv = (*aStatement)->BindInt64Parameter(kAnnoIndex_Page, uriID);
NS_ENSURE_SUCCESS(rv, rv);
rv = (*aStatement)->BindUTF8StringParameter(kAnnoIndex_Name, aName);
}
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;
}

View File

@ -0,0 +1,80 @@
//* -*- 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 "nsIAnnotationService.h"
#include "nsCOMPtr.h"
#include "mozIStorageService.h"
#include "mozIStorageConnection.h"
class nsAnnotationService : public nsIAnnotationService
{
public:
nsAnnotationService();
virtual ~nsAnnotationService();
nsresult Init();
NS_DECL_ISUPPORTS
NS_DECL_NSIANNOTATIONSERVICE
protected:
nsCOMPtr<mozIStorageService> mDBService;
nsCOMPtr<mozIStorageConnection> mDBConn;
nsCOMPtr<mozIStorageStatement> mDBSetAnnotation;
nsCOMPtr<mozIStorageStatement> mDBGetAnnotation;
nsCOMPtr<mozIStorageStatement> mDBGetAnnotationFromURI;
nsCOMPtr<mozIStorageStatement> mDBAddAnnotation;
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);
};

View File

@ -217,6 +217,8 @@ const int nsNavHistory::kAutoCompleteIndex_Typed = 3;
static nsDataHashtable<nsStringHashKey, int>* gTldTypes;
static const char* gQuitApplicationMessage = "quit-application";
nsNavHistory* nsNavHistory::gHistoryService;
// nsNavHistory::nsNavHistory
@ -224,7 +226,8 @@ nsNavHistory::nsNavHistory() : mNowValid(PR_FALSE),
mExpireNowTimer(nsnull),
mBatchesInProgress(0)
{
NS_ASSERTION(! gHistoryService, "YOU ARE CREATING 2 COPIES OF THE HISTORY SERVICE. Everything will break.");
gHistoryService = this;
}
@ -233,6 +236,11 @@ nsNavHistory::nsNavHistory() : mNowValid(PR_FALSE),
nsNavHistory::~nsNavHistory()
{
gObserverService->RemoveObserver(this, gQuitApplicationMessage);
// remove the static reference to the service. Check to make sure its us
// in case somebody creates an extra instance of the service.
if (gHistoryService == this)
gHistoryService = nsnull;
}
@ -440,6 +448,49 @@ nsNavHistory::SaveExpandItem(const nsAString& aTitle)
}
// nsNavHistory::GetUrlIdFor
//
// Called by the bookmarks and annotation servii, 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)
{
nsresult rv;
*aEntryID = 0;
mozStorageTransaction transaction(mDBConn, PR_FALSE);
mozStorageStatementScoper statementResetter(mDBGetURLPageInfo);
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) {
mDBGetURLPageInfo->Reset();
statementResetter.Abandon();
rv = InternalAddNewPage(aURI, nsnull, 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
@ -484,7 +535,7 @@ nsNavHistory::InternalAdd(nsIURI* aURI, PRUint32 aSessionID,
#ifdef IN_MEMORY_LINKS
if (!mMemDBConn)
InitMemDB();
rv = BindURI(mMemDBAddPage, 0, aURI);
rv = BindStatementURI(mMemDBAddPage, 0, aURI);
NS_ENSURE_SUCCESS(rv, rv);
mMemDBAddPage->Execute();
#endif
@ -499,7 +550,7 @@ nsNavHistory::InternalAdd(nsIURI* aURI, PRUint32 aSessionID,
NS_LITERAL_CSTRING("SELECT id,visit_count,typed,hidden FROM moz_history WHERE url = ?1"),
getter_AddRefs(dbSelectStatement));
NS_ENSURE_SUCCESS(rv, rv);
rv = BindURI(dbSelectStatement, 0, aURI);
rv = BindStatementURI(dbSelectStatement, 0, aURI);
NS_ENSURE_SUCCESS(rv, rv);
PRBool alreadyVisited = PR_TRUE;
rv = dbSelectStatement->ExecuteStep(&alreadyVisited);
@ -631,7 +682,7 @@ nsNavHistory::InternalAddNewPage(nsIURI* aURI, const PRUnichar* aTitle,
NS_ENSURE_SUCCESS(rv, rv);
// URL
rv = BindURI(dbInsertStatement, 0, aURI);
rv = BindStatementURI(dbInsertStatement, 0, aURI);
NS_ENSURE_SUCCESS(rv, rv);
// title: use NULL if not given to distinguish it from empty titles
@ -708,24 +759,6 @@ nsresult nsNavHistory::AddVisit(PRInt64 aFromStep, PRInt64 aPageID,
}
// nsNavHistory::BindURI
//
// Binds the specified URI as the parameter 'index' for the statment.
// URIs are always bound as UTF8
nsresult nsNavHistory::BindURI(mozIStorageStatement* statement, int index,
nsIURI* aURI) const
{
nsCAutoString utf8URISpec;
nsresult rv = aURI->GetSpec(utf8URISpec);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindUTF8StringParameter(index, utf8URISpec);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// nsNavHistory::IsURIStringVisited
//
// Takes a URL as a string and returns true if we've visited it.
@ -815,6 +848,10 @@ nsNavHistory::VacuumDB()
rv = visitDelete->Execute();
NS_ENSURE_SUCCESS(rv, rv);
// FIXME: delete annotations for expiring pages
// FIXME: delete favicons when no pages reference them
// delete history entries with no visits
nsCOMPtr<mozIStorageStatement> historyDelete;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
@ -831,6 +868,8 @@ nsNavHistory::VacuumDB()
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM moz_historyvisit"));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM moz_anno"));
NS_ENSURE_SUCCESS(rv, rv);
*/
return NS_OK;
@ -1318,7 +1357,7 @@ nsNavHistory::RemovePage(nsIURI *aURI)
"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 = BindURI(statement, 0, aURI);
rv = BindStatementURI(statement, 0, aURI);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
@ -1328,7 +1367,7 @@ nsNavHistory::RemovePage(nsIURI *aURI)
"DELETE FROM moz_history WHERE url = ?1"),
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
rv = BindURI(statement, 0, aURI);
rv = BindStatementURI(statement, 0, aURI);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
@ -1561,7 +1600,7 @@ nsNavHistory::HidePage(nsIURI *aURI)
NS_LITERAL_CSTRING("SELECT id,hidden FROM moz_history WHERE url = ?1"),
getter_AddRefs(dbSelectStatement));
NS_ENSURE_SUCCESS(rv, rv);
rv = BindURI(dbSelectStatement, 0, aURI);
rv = BindStatementURI(dbSelectStatement, 0, aURI);
NS_ENSURE_SUCCESS(rv, rv);
PRBool alreadyVisited = PR_TRUE;
rv = dbSelectStatement->ExecuteStep(&alreadyVisited);
@ -1643,7 +1682,7 @@ nsNavHistory::MarkPageAsTyped(nsIURI *aURI)
NS_LITERAL_CSTRING("SELECT id,typed FROM moz_history WHERE url = ?1"),
getter_AddRefs(dbSelectStatement));
NS_ENSURE_SUCCESS(rv, rv);
rv = BindURI(dbSelectStatement, 0, aURI);
rv = BindStatementURI(dbSelectStatement, 0, aURI);
NS_ENSURE_SUCCESS(rv, rv);
PRBool alreadyVisited = PR_TRUE;
rv = dbSelectStatement->ExecuteStep(&alreadyVisited);
@ -1757,7 +1796,7 @@ nsNavHistory::SetPageTitle(nsIURI *aURI,
NS_ENSURE_SUCCESS(rv, rv);
// url
rv = BindURI(dbModStatement, 1, aURI);
rv = BindStatementURI(dbModStatement, 1, aURI);
NS_ENSURE_SUCCESS(rv, rv);
rv = dbModStatement->Execute();
@ -3196,6 +3235,24 @@ void GetSubstringFromNthDot(const nsString& aInput, int aStartingSpot,
}
// BindStatementURI
//
// Binds the specified URI as the parameter 'index' for the statment.
// URIs are always bound as UTF8
nsresult BindStatementURI(mozIStorageStatement* statement, int index,
nsIURI* aURI)
{
nsCAutoString utf8URISpec;
nsresult rv = aURI->GetSpec(utf8URISpec);
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->BindUTF8StringParameter(index, utf8URISpec);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// nsNavHistoryResultNode ******************************************************

View File

@ -56,6 +56,7 @@
#include "nsIPrefBranch.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsServiceManagerUtils.h"
#include "nsIStringBundle.h"
#include "nsITimer.h"
#include "nsITreeSelection.h"
@ -292,12 +293,53 @@ public:
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)
return gHistoryService;
// don't want the return value, since that's the interface. We want the
// pointer to the implementation.
do_GetService("@mozilla.org/browser/nav-history;1");
// our constructor should have set the static variable. If it didn't,
// something is wrong.
NS_ASSERTION(gHistoryService, "History service creation failed");
return gHistoryService;
}
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.
*
* This connection can only be used in the thread that created it the
* history service!
*/
mozIStorageConnection* GetStorageConnection()
{
return mDBConn;
}
// remember tree state
void SaveExpandItem(const nsAString& aTitle);
void SaveCollapseItem(const nsAString& aTitle);
private:
~nsNavHistory();
// used by GetHistoryService
static nsNavHistory* gHistoryService;
protected:
//
@ -346,7 +388,6 @@ protected:
int aVisitCount, PRInt64* aPageID);
nsresult AddVisit(PRInt64 aFromStep, PRInt64 aPageID, PRTime aTime,
PRInt32 aTransitionType, PRInt64 aSessionID);
nsresult BindURI(mozIStorageStatement* statement, int index, nsIURI* aURI) const;
PRBool IsURIStringVisited(const nsACString& url);
nsresult VacuumDB();
nsresult LoadPrefs();
@ -439,3 +480,10 @@ protected:
nsresult ImportFromMork();
};
/**
* Shared between the places components, this function binds the given URI as
* UTF8 to the given parameter for the statement.
*/
nsresult BindStatementURI(mozIStorageStatement* statement, int index,
nsIURI* aURI);