Using the Places annotation service

  • Revision slug: Using_the_Places_annotation_service
  • Revision title: Using the Places annotation service
  • Revision id: 139552
  • Created:
  • Creator: Sheppy
  • Is current revision? No
  • Comment

Revision Content

{{template.Firefox3()}}

The annotation service is designed to store arbitrary data about a web page in Firefox 3. It is usable from trusted Firefox code such as extensions, but not web pages. The interface is defined in {{template.Source("toolkit/components/places/public/nsIAnnotationService.idl")}}, and the implementation is in {{template.Source("toolkit/components/places/src/nsAnnotationService.cpp")}}.

For an overview about how the database design of Places, see The Places database.

Creating the annotation service

The annotation contract ID is @mozilla.org/browser/annotation-service;1

var annotationService = Components.classes["@mozilla.org/browser/annotation-service;1"]
                                  .getService(Components.interfaces.nsIAnnotationService);

Note that the annotation service is not thread-safe. You should only use it from the main thread.

Naming your annotations

For your annotation name, you should use the format <namespace>/<name>. For example, "my_extension/page_state". The annotation service does not currently enforce the annotation name format, but this may change in the future. Also, we may add functions to get all of "your" annotations matching a given namespace.

You should not use any characters in your annotation names that are not valid as part of an HTML path. This includes colons, spaces, most punctuation, and non-ASCII characters.

You should try to have relatively few annotation names. The service stores these names in a separate table, and the fewer names there are, the more efficient retrieving names will be. You should not design your service so that you require hundreds of unique annotation names.

Setting an annotation

The annotation provides a variety of setters for different types (see {{template.Source("toolkit/components/places/public/nsIAnnotationService.idl", "nsIAnnotationService.idl")}} for the exact declarations):

  • setPageAnnotationString
  • setPageAnnotationInt32
  • setPageAnnotationInt64
  • setPageAnnotationDouble
  • setPageAnnotationBinary: Watch out, any web page can load an image from the annotation service. Although they can not send the image elsewhere or get its contents, you should not store sensitive images in the annotation service.

These annotations all take similar parameters:

  • URI: This is the URI of the page to annotate.
  • name: This is the name of the annotation. See the section on naming above.
  • value
  • flags: currently undefined, should be 0.
  • expiration: expiration is currently unimplemented (see "Lifetime of annotations" below).
var ioservice = Components.classes["@mozilla.org/network/io-service;1"]
                          .getService(Components.interfaces.nsIIOService);
var uri = ioservice.newURI("http://www.mozilla.org/", null, null);

annotationService.setPageAnnotation(uri, "my_extension/some_annotation",
  "This is the annotation value", 0, 0);

Retrieving annotations

There are corresponding getters for the setters above (see {{template.Source("toolkit/components/places/public/nsIAnnotationService.idl", "nsIAnnotationService.idl")}} for the exact declarations):

  • getPageAnnotationString
  • getPageAnnotationInt32
  • getPageAnnotationInt64
  • getPageAnnotationDouble
  • getPageAnnotationBinary

It is the caller's responsibility to request the correct type for the given annotation. The annotation service passes these types along to the Storage service, which uses sqlite. Sqlite will try to automatically convert types if the type you request does not match the value in the database. Sometimes this works fine (Int32 to double), but sometimes it doesn't (string to Int32). When it doesn't work, you will get some kind of default value for whatever type you request. Providing a more robust way to handle annotations in a type-agnostic way is {{template.Bug(331654)}}.

These functions will return NS_ERROR_NOT_AVAILABLE if the annotation requested does not exist. You can use hasAnnotation to determine in advance if the page has the requested annotation. However, it is more efficient to just try to do the operation and catch the exception; the extra check requires an additional database lookup (which has higher overhead).

The getter functions return only the value of the annotation. Use nsIAnnotationService.getAnnotationBinary to get the other values associated with an annotation: flags, expiration, MIME type, and data type. The data type is one of the VALUE_TYPE constants in {{template.Source("storage/public/mozIStorageValueArray.idl", "mozIStorageValueArray.idl")}}:

After {{template.Bug(377066)}} the VALUE_TYPE_* type handling was changed to this:

  • TYPE_INT32 = 1
  • TYPE_DOUBLE = 2
  • TYPE_STRING = 3
  • TYPE_BINARY = 4
  • TYPE_INT64 = 5
try {
  var value = annotationService.getPageAnnotation(uri, "my_extension/some_annotation");
} catch(e) {
  // annotation does not exist
}

Other functions

  • getPagesWithAnnotation: Retrieves a list of all pages with the given annotation. C++ callers will want to use GetPagesWithAnnotationCOMArray which returns a COM array, making memory management much easier and reducing the chance of leaks.
  • getPageAnnotationNames: Retrieves a list of all the annotations on a given URI. C++ callers will want to use GetPageAnnotationNamesTArray which returns a COM array, making memory management much easier and reducing the chance of leaks.
  • pageHasAnnotation: Returns true if the page has an annotation with the given name.
  • removePageAnnotation: Removes a given annotation from a page.
  • removePageAnnotations: Removes all the annotations from a given page.
  • copyPageAnnotations: Copies all the annotations from one page to another. You can specify whether you want to preserve or replace the destination annotations in case of collisions.

The annotation protocol

The annotation service provides a protocol handler for the "moz-anno:" protocol. This allows you to link directly to data stored in the annotation service. You can get an annotation URI for a given URI/name pair by calling nsIAnnotationService.getAnnotationURI.

In order for the annotation protocol to work, the annotation in question must have been declared with a MIME type. Annotations with no MIME type will not work.

The annotation service also provides special-case handling of favicons. When the annotation name is "favicon," the annotation protocol handler will pass the request through to the favicon service for handling. To get a favicon annotation URI for a given favicon, use nsIFaviconService.getFaviconLinkForIcon and to get the favicon annotation URI for a given page, use nsIFaviconService.getFaviconLinkForPage. It is important that you use these functions and not make up your own URIs, because these functions will efficiently default to the default page favicon when it does not exist. See Places Favicon Service for more information.

Lifetime of annotations

Currently, annotation expiration is unimplemented so annotations will last forever. We will definitely expire annotations in the future, although the policy has yet to be determined. The bug for this capability is {{template.Bug(319455)}}.

{{ wiki.languages( { "ja": "ja/Places/Annotation_Service" } ) }}

Revision Source

<p>
{{template.Firefox3()}}
</p><p>The annotation service is designed to store arbitrary data about a web page in Firefox 3. It is usable from trusted Firefox code such as extensions, but not web pages. The interface is defined in {{template.Source("toolkit/components/places/public/nsIAnnotationService.idl")}}, and the implementation is in {{template.Source("toolkit/components/places/src/nsAnnotationService.cpp")}}.
</p><p>For an overview about how the database design of Places, see <a href="en/The_Places_database">The Places database</a>.
</p>
<h3 name="Creating_the_annotation_service"> Creating the annotation service </h3>
<p>The annotation contract ID is <code>@mozilla.org/browser/annotation-service;1</code>
</p>
<pre>var annotationService = Components.classes["@mozilla.org/browser/annotation-service;1"]
                                  .getService(Components.interfaces.nsIAnnotationService);
</pre>
<p>Note that the annotation service is <i>not</i> thread-safe. You should only use it from the main thread.
</p>
<h3 name="Naming_your_annotations"> Naming your annotations </h3>
<p>For your annotation name, you should use the format <i>&lt;namespace&gt;</i>/<i>&lt;name&gt;</i>. For example, "my_extension/page_state". The annotation service does not currently enforce the annotation name format, but this may change in the future. Also, we may add functions to get all of "your" annotations matching a given namespace.
</p><p>You should not use any characters in your annotation names that are not valid as part of an HTML path. This includes colons, spaces, most punctuation, and non-ASCII characters.
</p><p>You should try to have relatively few annotation names. The service stores these names in a separate table, and the fewer names there are, the more efficient retrieving names will be. You should not design your service so that you require hundreds of unique annotation names.
</p>
<h3 name="Setting_an_annotation"> Setting an annotation </h3>
<p>The annotation provides a variety of setters for different types (see {{template.Source("toolkit/components/places/public/nsIAnnotationService.idl", "nsIAnnotationService.idl")}} for the exact declarations):
</p>
<ul><li> <b>setPageAnnotationString</b>
</li><li> <b>setPageAnnotationInt32</b>
</li><li> <b>setPageAnnotationInt64</b>
</li><li> <b>setPageAnnotationDouble</b>
</li><li> <b>setPageAnnotationBinary</b>: Watch out, any web page can load an image from the annotation service. Although they can not send the image elsewhere or get its contents, you should not store sensitive images in the annotation service.
</li></ul>
<p>These annotations all take similar parameters:
</p>
<ul><li> URI: This is the URI of the page to annotate.
</li><li> name: This is the name of the annotation. See the section on naming above.
</li><li> value
</li><li> flags: currently undefined, should be 0.
</li><li> expiration: expiration is currently unimplemented (see "Lifetime of annotations" below).
</li></ul>
<pre>var ioservice = Components.classes["@mozilla.org/network/io-service;1"]
                          .getService(Components.interfaces.nsIIOService);
var uri = ioservice.newURI("http://www.mozilla.org/", null, null);

annotationService.setPageAnnotation(uri, "my_extension/some_annotation",
  "This is the annotation value", 0, 0);
</pre>
<h3 name="Retrieving_annotations"> Retrieving annotations </h3>
<p>There are corresponding getters for the setters above (see {{template.Source("toolkit/components/places/public/nsIAnnotationService.idl", "nsIAnnotationService.idl")}} for the exact declarations):
</p>
<ul><li> <b>getPageAnnotationString</b>
</li><li> <b>getPageAnnotationInt32</b>
</li><li> <b>getPageAnnotationInt64</b>
</li><li> <b>getPageAnnotationDouble</b>
</li><li> <b>getPageAnnotationBinary</b>
</li></ul>
<p>It is the caller's responsibility to request the correct type for the given annotation. The annotation service passes these types along to the <a href="en/Storage">Storage</a> service, which uses sqlite. Sqlite will try to automatically convert types if the type you request does not match the value in the database. Sometimes this works fine (Int32 to double), but sometimes it doesn't (string to Int32). When it doesn't work, you will get some kind of default value for whatever type you request. Providing a more robust way to handle annotations in a type-agnostic way is {{template.Bug(331654)}}.
</p><p>These functions will return <code>NS_ERROR_NOT_AVAILABLE</code> if the annotation requested does not exist. You can use <b>hasAnnotation</b> to determine in advance if the page has the requested annotation. However, it is more efficient to just try to do the operation and catch the exception; the extra check requires an additional database lookup (which has higher overhead).
</p><p>The getter functions return only the value of the annotation. Use <code>nsIAnnotationService.getAnnotationBinary</code> to get the other values associated with an annotation: flags, expiration, MIME type, and data type. The data type is one of the VALUE_TYPE constants in {{template.Source("storage/public/mozIStorageValueArray.idl", "mozIStorageValueArray.idl")}}:
</p><p>After {{template.Bug(377066)}} the VALUE_TYPE_* type handling was changed to this:
</p>
<ul><li> TYPE_INT32  = 1
</li><li> TYPE_DOUBLE = 2
</li><li> TYPE_STRING = 3
</li><li> TYPE_BINARY = 4
</li><li> TYPE_INT64  = 5
</li></ul>
<pre>try {
  var value = annotationService.getPageAnnotation(uri, "my_extension/some_annotation");
} catch(e) {
  // annotation does not exist
}
</pre>
<h3 name="Other_functions"> Other functions </h3>
<ul><li> <b>getPagesWithAnnotation</b>: Retrieves a list of all pages with the given annotation. C++ callers will want to use <b>GetPagesWithAnnotationCOMArray</b> which returns a COM array, making memory management much easier and reducing the chance of leaks.
</li></ul>
<ul><li> <b>getPageAnnotationNames</b>: Retrieves a list of all the annotations on a given URI. C++ callers will want to use <b>GetPageAnnotationNamesTArray</b> which returns a COM array, making memory management much easier and reducing the chance of leaks.
</li></ul>
<ul><li> <b>pageHasAnnotation</b>: Returns <code>true</code> if the page has an annotation with the given name.
</li></ul>
<ul><li> <b>removePageAnnotation</b>: Removes a given annotation from a page.
</li></ul>
<ul><li> <b>removePageAnnotations</b>: Removes all the annotations from a given page.
</li></ul>
<ul><li> <b>copyPageAnnotations</b>: Copies all the annotations from one page to another. You can specify whether you want to preserve or replace the destination annotations in case of collisions.
</li></ul>
<h3 name="The_annotation_protocol"> The annotation protocol </h3>
<p>The annotation service provides a protocol handler for the "moz-anno:" protocol. This allows you to link directly to data stored in the annotation service. You can get an annotation URI for a given URI/name pair by calling <code>nsIAnnotationService.getAnnotationURI</code>.
</p><p>In order for the annotation protocol to work, the annotation in question must have been declared with a MIME type. Annotations with no MIME type will not work.
</p><p>The annotation service also provides special-case handling of favicons. When the annotation name is "favicon," the annotation protocol handler will pass the request through to the favicon service for handling. To get a favicon annotation URI for a given favicon, use <b>nsIFaviconService.getFaviconLinkForIcon</b> and to get the favicon annotation URI for a given page, use <b>nsIFaviconService.getFaviconLinkForPage</b>. It is important that you use these functions and not make up your own URIs, because these functions will efficiently default to the default page favicon when it does not exist. See <a href="en/Places/Favicon_Service">Places Favicon Service</a> for more information.
</p>
<h3 name="Lifetime_of_annotations"> Lifetime of annotations </h3>
<p>Currently, annotation expiration is unimplemented so annotations will last forever. We will definitely expire annotations in the future, although the policy has yet to be determined. The bug for this capability is {{template.Bug(319455)}}.
</p>
<div class="noinclude">
</div>
{{ wiki.languages( { "ja": "ja/Places/Annotation_Service" } ) }}
Revert to this revision