Add generic annotation service for associating data with web pages




Bookmarks & History
13 years ago
9 years ago


(Reporter: Brett Wilson, Assigned: Brett Wilson)



Firefox Tracking Flags

(Not tracked)



(2 attachments, 1 obsolete attachment)



13 years ago
User-Agent:       Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.9) Gecko/20050711 Firefox/1.0.5
Build Identifier: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.9) Gecko/20050711 Firefox/1.0.5

There should be some kind of service for associating data with web pages. This
could be used by extensions to provide interesting services like associating
notes with a web page. This will also be used to write richer bookmarks and
history components, and to as a replacement for current things like storing form
data and favorite icons.

Reproducible: Always

Steps to Reproduce:


13 years ago
Ever confirmed: true
Assignee: vladimir → brettw

Comment 1

13 years ago
Created attachment 194570 [details]
Proposed interface

Proposed interface for the annotation service. This does not yet implement the
expiration of annotations. That is better saved until after the new history
system is ready.

Comment 2

13 years ago
Anybody have comments on whether the interface should take a nsIURI or a string URI?

The current implementation uses nsIURI, but it might be easier for some callers
to just take a string, especially if you get a URL from history and want see
what it has: you have to create an object which parses the URI, all for naught.

Comment 3

13 years ago
Comment on attachment 194570 [details]
Proposed interface

please don't use //, use
 * ...
even for documenting constants. the parsers we have will do useful things for
such comments.

i'm not particularly fond of expire_never = 5.
i'd rather -1....

don't use wstring.

why are you using nsIVariant instead of nsISupports or nsISerializable ?

document what happens if i call getAnnotationInfo for something that has no
such annotation.

I'd rather removePageAnnotations take a second parameter that's some sort of
string or regexp or whatever.

bill should be able to kill all bill annotations easily w/o blowing away fred
(In reply to comment #3)
> why are you using nsIVariant instead of nsISupports or nsISerializable ?

nsISupports/nsISerializable suck horribly when used from JS.  nsIVariant lets
the JS API be sane through xpconnect.  (Do we get a SupportsString or whatever
if you pass in a string to a nsISupports param through xpconnect?)

Comment 5

13 years ago
The purpose I had in mind for removePageAnnotations would be when we are purging
the page from the DB and we need to remove everything associated with it.

If you want to do something fancier, you can always just get the annotations and
delete the ones you don't want. This seems almost as easy and more flexible than
a regexp. I think if there is going to be a lot of contention about this
function, we should just remove it.

Comment 6

13 years ago
See also the Wiki:

Comment 7

13 years ago
Created attachment 202424 [details] [diff] [review]
Preliminary annotation service interface and implementation

This adds the annotation service to the "places" component (not built by default). There are some missing implementations and commented out interfaces: this is just a first pass, we can add some more helpful functions to get lists of annotation snd expire annotations later.
Attachment #194570 - Attachment is obsolete: true
Attachment #202424 - Flags: second-review?(bryner)
Attachment #202424 - Flags: first-review?(annie.sullivan)


13 years ago
Attachment #202424 - Flags: first-review?(annie.sullivan) → first-review+
(In reply to comment #7)
> Created an attachment (id=202424)

> Index: components/places/public/nsIAnnotationService.idl
> Index: components/places/src/nsAnnotationService.cpp
> Index: components/places/src/nsAnnotationService.h

Then these are "browser" components?
I agree "places" should be one of browser components (bug 266174), but
annotation service might be useful for other products, Thunderbird and
thrid-party apps with xulrunner.
(In reply to comment #7)
> expire annotations later.

IMHO, setting/getting the expiration date would be more solid spec than
setting/getting the life time days/weeks/months.


13 years ago
Blocks: 316077

Comment 10

13 years ago
(In reply to comment #8)
> Then these are "browser" components?

This service is very tied to history/bookmarks. Implementation of a similar system for other consumers is trivial using mozStorage.

(In reply to comment #9)
> IMHO, setting/getting the expiration date would be more solid spec than
> setting/getting the life time days/weeks/months.

Expiration is unimplemented and the policy still needs to be flushed out.
Comment on attachment 202424 [details] [diff] [review]
Preliminary annotation service interface and implementation

>--- /dev/null	1 Jan 1970 00:00:00 -0000
>+++ components/places/public/nsIAnnotationService.idl	9 Nov 2005 22:11:51 -0000
>@@ -0,0 +1,208 @@
>+#include "nsISupports.idl"
>+#include "nsIURI.idl"
>+#include "nsIVariant.idl"

nsIURI and nsIVariant should just be forward-declared.

>+    const long EXPIRE_MONTHS = 3;
>+      // For small, user-entered data like notes that should never expire.
>+    const long EXPIRE_NEVER = 5;

Is 4 bad luck?

This interface uses an odd mish-mash of |long| and |PRInt32| to refer to 32-bit signed ints.  Pick one.

>--- /dev/null	1 Jan 1970 00:00:00 -0000
>+++ components/places/src/nsAnnotationService.cpp	9 Nov 2005 22:11:51 -0000
>+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;


>+nsAnnotationService::SetAnnotation(nsIURI* aURI,
>+                                   const nsACString& aName,
>+                                   nsIVariant *aValue,
>+                                   PRInt32 aFlags, PRInt32 aExpiration)
>+  if (! aValue)
>+    return NS_ERROR_INVALID_ARG;
>+  nsString stringValue;


>+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);

The statement will be left open if either of these fail.  Likewise in the other SetAnnotation* methods.

>+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(";1");
>+  if (! var)
>+    return NS_ERROR_FAILURE;

You might want to pass &rv to do_CreateInstance and return that error code.

>+  rv = var->SetWritable(PR_TRUE);
>+  NS_ENSURE_SUCCESS(rv, rv);

I don't think this is necessary.

>+  rv = var->SetAsAString(stringValue);
>+  NS_ENSURE_SUCCESS(rv, rv);
>+  *_retval = var;
>+  (*_retval)->AddRef();


>+// nsAnnotationService::GetAnnotationInt32
>+nsAnnotationService::GetAnnotationInt32(nsIURI* aURI,
>+                                        const nsACString& aName,
>+                                        PRInt32 *_retval)
>+  nsresult rv = StartGetAnnotationFromURI(aURI, aName);
>+  NS_ENSURE_SUCCESS(rv, rv);
>+  rv = mDBGetAnnotationFromURI->GetAsInt32(kAnnoIndex_Content, _retval);
>+  mDBGetAnnotationFromURI->Reset();
>+  return rv;

It's more or less implied by the API that GetAsInt32 won't fail, given the presence of AsInt32().  So, maybe just:

*_retval = mDBGetAnnotationFromURI->GetAsInt32(kAnnoIndex_Content);
return NS_OK;

>+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->GetAsInt32(kAnnoIndex_Flags, aFlags);
>+  NS_ENSURE_SUCCESS(rv, rv);
>+  rv = mDBGetAnnotationFromURI->GetAsInt32(kAnnoIndex_Expiration, aExpiration);
>+  NS_ENSURE_SUCCESS(rv, rv);

Same here, use AsInt32().

>+// 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.


>--- /dev/null	1 Jan 1970 00:00:00 -0000
>+++ components/places/src/nsAnnotationService.h	9 Nov 2005 22:11:51 -0000
>+class nsAnnotationService : public nsIAnnotationService
>+  nsAnnotationService();
>+  virtual ~nsAnnotationService();

doesn't need to be virtual or public.

>--- components/places/src/nsNavHistory.cpp	9 Nov 2005 19:19:33 -0000	1.2
>+++ components/places/src/nsNavHistory.cpp	9 Nov 2005 22:11:51 -0000
> 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;

This should just assert, like the constructor.

>@@ -423,16 +426,59 @@ nsNavHistory::InitMemDB()
>   }
>   transaction.Commit();
>   printf("DONE initializing history in-memory DB\n");
>   return NS_OK;
> }
>+// nsNavHistory::GetUrlIdFor
>+//    Called by the bookmarks and annotation servii, this function returns the

Hm, let's just go with "services."

>--- components/places/src/nsNavHistory.h	9 Nov 2005 19:19:33 -0000	1.2
>+++ components/places/src/nsNavHistory.h	9 Nov 2005 22:11:51 -0000
>@@ -55,16 +55,17 @@
> #include "nsIPrefService.h"
> #include "nsIPrefBranch.h"
> #include "nsIObserver.h"
> #include "nsIObserverService.h"
> #include "nsIStringBundle.h"
> #include "nsITimer.h"
> #include "nsITreeSelection.h"
> #include "nsITreeView.h"
>+#include "nsServiceManagerUtils.h"
> #include "nsString.h"
> #include "nsVoidArray.h"
> #include "nsWeakReference.h"
> // Number of prefixes used in the autocomplete sort comparison function
> // Size of visit count boost to give to urls which are sites or paths
>@@ -263,45 +264,86 @@ protected:
>       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);
> };
>+class AutoCompleteIntermediateResultSet;
> // nsNavHistory
> class nsNavHistory : nsSupportsWeakReference,
>                      public nsINavHistory,
>                      public nsIObserver,
>                      public nsIBrowserHistory,
>                      public nsIAutoCompleteSearch
> {
>-  friend struct AutoCompleteIntermediateResultSet;
>+  friend class AutoCompleteIntermediateResultSet;
> public:
>   nsNavHistory();
>   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;

I'd do this the other way, I think:

if (!gHistoryService) {
return gHistoryService;

It's less code and I think it agrees better with the way branch prediction works on most cpus.

sr=me with those changes.
Attachment #202424 - Flags: second-review?(bryner) → second-review+

Comment 12

13 years ago
Created attachment 203535 [details] [diff] [review]
Add annotation observers and moz-anno URI generator

This also includes some cleanup of the interface: the integer type has been standardized on PRInt32.
Attachment #203535 - Flags: first-review?(annie.sullivan)


13 years ago
Attachment #203535 - Flags: first-review?(annie.sullivan) → first-review+


13 years ago
Component: Storage → Places
Flags: second-review+
Flags: first-review+
Product: Toolkit → Firefox
Version: unspecified → Trunk

Comment 13

13 years ago
Basic implementation is on trunk in the places system (off by default). It needs many improvements: I'll make those separate bugs.
Last Resolved: 13 years ago
Resolution: --- → FIXED
Bug 451915 - move Firefox/Places bugs to Firefox/Bookmarks and History. Remove all bugspam from this move by filtering for the string "places-to-b-and-h".

In Thunderbird 3.0b, you do that as follows:
Tools | Message Filters
Make sure the correct account is selected. Click "New"
Conditions: Body   contains   places-to-b-and-h
Change the action to "Delete Message".
Select "Manually Run" from the dropdown at the top.
Click OK.

Select the filter in the list, make sure "Inbox" is selected at the bottom, and click "Run Now". This should delete all the bugspam. You can then delete the filter.

Component: Places → Bookmarks & History
QA Contact: nobody → bookmarks
You need to log in before you can comment on or make changes to this bug.