From 17cc116f682423a362159a53bfb51a30beab8b7f Mon Sep 17 00:00:00 2001 From: Marco Bonardo Date: Fri, 21 Dec 2012 15:19:04 +0100 Subject: [PATCH] Bug 822710 (part 1) - Implement a new getAnnotationsHavingName method. r=Mano sr=gavin --- .../components/places/nsAnnotationService.cpp | 165 +++++++++++++++++- .../components/places/nsAnnotationService.h | 24 +++ .../places/nsIAnnotationService.idl | 57 +++++- .../places/tests/unit/test_annotations.js | 54 ++++++ 4 files changed, 298 insertions(+), 2 deletions(-) diff --git a/toolkit/components/places/nsAnnotationService.cpp b/toolkit/components/places/nsAnnotationService.cpp index e8e7205b678b..edd4cc229c3e 100644 --- a/toolkit/components/places/nsAnnotationService.cpp +++ b/toolkit/components/places/nsAnnotationService.cpp @@ -23,6 +23,7 @@ #include "nsNetCID.h" using namespace mozilla; +using namespace mozilla::places; #define ENSURE_ANNO_TYPE(_type, _statement) \ PR_BEGIN_MACRO \ @@ -47,7 +48,64 @@ const int32_t nsAnnotationService::kAnnoIndex_Type = 7; const int32_t nsAnnotationService::kAnnoIndex_DateAdded = 8; const int32_t nsAnnotationService::kAnnoIndex_LastModified = 9; -using namespace mozilla::places; +namespace mozilla { +namespace places { + +//////////////////////////////////////////////////////////////////////////////// +//// AnnotatedResult + +AnnotatedResult::AnnotatedResult(const nsCString& aGUID, + nsIURI* aURI, + int64_t aItemId, + const nsACString& aAnnotationName, + nsIVariant* aAnnotationValue) +: mGUID(aGUID) +, mURI(aURI) +, mItemId(aItemId) +, mAnnotationName(aAnnotationName) +, mAnnotationValue(aAnnotationValue) +{ +} + +NS_IMETHODIMP +AnnotatedResult::GetGuid(nsACString& _guid) +{ + _guid = mGUID; + return NS_OK; +} + +NS_IMETHODIMP +AnnotatedResult::GetUri(nsIURI** _uri) +{ + NS_IF_ADDREF(*_uri = mURI); + return NS_OK; +} + +NS_IMETHODIMP +AnnotatedResult::GetItemId(int64_t* _itemId) +{ + *_itemId = mItemId; + return NS_OK; +} + +NS_IMETHODIMP +AnnotatedResult::GetAnnotationName(nsACString& _annotationName) +{ + _annotationName = mAnnotationName; + return NS_OK; +} + +NS_IMETHODIMP +AnnotatedResult::GetAnnotationValue(nsIVariant** _annotationValue) +{ + NS_IF_ADDREF(*_annotationValue = mAnnotationValue); + return NS_OK; +} + +NS_IMPL_ISUPPORTS1(AnnotatedResult, mozIAnnotatedResult) + +} // namespace places +} // namespace mozilla PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsAnnotationService, gAnnotationService) @@ -1163,6 +1221,111 @@ nsAnnotationService::GetItemsWithAnnotation(const nsACString& aName, } +NS_IMETHODIMP +nsAnnotationService::GetAnnotationsWithName(const nsACString& aName, + uint32_t* _count, + mozIAnnotatedResult*** _annotations) +{ + NS_ENSURE_ARG(!aName.IsEmpty()); + NS_ENSURE_ARG_POINTER(_annotations); + + *_count = 0; + *_annotations = nullptr; + nsCOMArray annotations; + + nsCOMPtr stmt = mDB->GetStatement( + "SELECT h.guid, h.url, -1, a.type, a.content " + "FROM moz_anno_attributes n " + "JOIN moz_annos a ON n.id = a.anno_attribute_id " + "JOIN moz_places h ON h.id = a.place_id " + "WHERE n.name = :anno_name " + "UNION ALL " + "SELECT b.guid, h.url, b.id, a.type, a.content " + "FROM moz_anno_attributes n " + "JOIN moz_items_annos a ON n.id = a.anno_attribute_id " + "JOIN moz_bookmarks b ON b.id = a.item_id " + "LEFT JOIN moz_places h ON h.id = b.fk " + "WHERE n.name = :anno_name " + ); + NS_ENSURE_STATE(stmt); + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), + aName); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasMore = false; + while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasMore)) && hasMore) { + nsAutoCString guid; + rv = stmt->GetUTF8String(0, guid); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr uri; + bool uriIsNull = false; + rv = stmt->GetIsNull(1, &uriIsNull); + NS_ENSURE_SUCCESS(rv, rv); + if (!uriIsNull) { + nsAutoCString url; + rv = stmt->GetUTF8String(1, url); + NS_ENSURE_SUCCESS(rv, rv); + rv = NS_NewURI(getter_AddRefs(uri), url); + NS_ENSURE_SUCCESS(rv, rv); + } + + int64_t itemId = stmt->AsInt64(2); + int32_t type = stmt->AsInt32(3); + + nsCOMPtr variant = new nsVariant(); + switch (type) { + case nsIAnnotationService::TYPE_INT32: { + rv = variant->SetAsInt32(stmt->AsInt32(4)); + break; + } + case nsIAnnotationService::TYPE_INT64: { + rv = variant->SetAsInt64(stmt->AsInt64(4)); + break; + } + case nsIAnnotationService::TYPE_DOUBLE: { + rv = variant->SetAsDouble(stmt->AsDouble(4)); + break; + } + case nsIAnnotationService::TYPE_STRING: { + nsAutoString valueString; + rv = stmt->GetString(4, valueString); + NS_ENSURE_SUCCESS(rv, rv); + + rv = variant->SetAsAString(valueString); + break; + } + default: + MOZ_ASSERT("Unsupported annotation type"); + // Move to the next result. + continue; + } + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr anno = new AnnotatedResult(guid, uri, itemId, + aName, variant); + NS_ENSURE_TRUE(annotations.AppendObject(anno), NS_ERROR_OUT_OF_MEMORY); + } + + // Convert to raw array. + if (annotations.Count() == 0) + return NS_OK; + + *_annotations = static_cast + (nsMemory::Alloc(annotations.Count() * sizeof(mozIAnnotatedResult*))); + NS_ENSURE_TRUE(*_annotations, NS_ERROR_OUT_OF_MEMORY); + + *_count = annotations.Count(); + for (uint32_t i = 0; i < *_count; ++i) { + NS_ADDREF((*_annotations)[i] = annotations[i]); + } + + return NS_OK; +} + + nsresult nsAnnotationService::GetItemsWithAnnotationTArray(const nsACString& aName, nsTArray* _results) diff --git a/toolkit/components/places/nsAnnotationService.h b/toolkit/components/places/nsAnnotationService.h index 7927c54e07bf..4f5c8eca0135 100644 --- a/toolkit/components/places/nsAnnotationService.h +++ b/toolkit/components/places/nsAnnotationService.h @@ -17,6 +17,30 @@ #include "nsString.h" #include "mozilla/Attributes.h" +namespace mozilla { +namespace places { + +class AnnotatedResult MOZ_FINAL : public mozIAnnotatedResult +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_MOZIANNOTATEDRESULT + + AnnotatedResult(const nsCString& aGUID, nsIURI* aURI, int64_t aItemd, + const nsACString& aAnnotationName, + nsIVariant* aAnnotationValue); + +private: + const nsCString mGUID; + nsCOMPtr mURI; + const int64_t mItemId; + const nsCString mAnnotationName; + nsCOMPtr mAnnotationValue; +}; + +} // namespace places +} // namespace mozilla + class nsAnnotationService MOZ_FINAL : public nsIAnnotationService , public nsIObserver , public nsSupportsWeakReference diff --git a/toolkit/components/places/nsIAnnotationService.idl b/toolkit/components/places/nsIAnnotationService.idl index 1ee8e4cff8cd..0f018a5e583f 100644 --- a/toolkit/components/places/nsIAnnotationService.idl +++ b/toolkit/components/places/nsIAnnotationService.idl @@ -7,6 +7,7 @@ interface nsIURI; interface nsIVariant; +interface mozIAnnotatedResult; [scriptable, uuid(63fe98e0-6889-4c2c-ac9f-703e4bc25027)] interface nsIAnnotationObserver : nsISupports @@ -31,7 +32,7 @@ interface nsIAnnotationObserver : nsISupports in AUTF8String aName); }; -[scriptable, uuid(ba249b58-346f-42a9-a393-203ae34ec6c4)] +[scriptable, uuid(c7f425cc-a063-49ef-9f4c-b08eb2f1730a)] interface nsIAnnotationService : nsISupports { /** @@ -324,6 +325,19 @@ interface nsIAnnotationService : nsISupports [optional] out unsigned long resultCount, [retval, array, size_is(resultCount)] out long long results); + /** + * Returns a list of mozIAnnotation(s), having a given annotation name. + * + * @param name + * The annotation to search for. + * @return list of mozIAnnotation objects. + * @note binary annotations are not supported and thus skipped. + */ + void getAnnotationsWithName( + in AUTF8String name, + [optional] out unsigned long count, + [retval, array, size_is(count)] out mozIAnnotatedResult results); + /** * Get the names of all annotations for this URI. * @@ -402,3 +416,44 @@ interface nsIAnnotationService : nsISupports nsIURI getAnnotationURI(in nsIURI aURI, in AUTF8String aName); }; + +/** + * Represents a place annotated with a given annotation. If a place has + * multiple annotations, it can be represented by multiple + * mozIAnnotatedResult(s). + */ +[scriptable, uuid(81fd0188-db6a-492e-80b6-f6414913b396)] +interface mozIAnnotatedResult : nsISupports +{ + /** + * The globally unique identifier of the place with this annotation. + * + * @note if itemId is valid this is the guid of the bookmark, otherwise + * of the page. + */ + readonly attribute AUTF8String guid; + + /** + * The URI of the place with this annotation, if available, null otherwise. + */ + readonly attribute nsIURI uri; + + /** + * The bookmark id of the place with this annotation, if available, + * -1 otherwise. + * + * @note if itemId is -1, it doesn't mean the page is not bookmarked, just + * that this annotation is relative to the page, not to the bookmark. + */ + readonly attribute long long itemId; + + /** + * Name of the annotation. + */ + readonly attribute AUTF8String annotationName; + + /** + * Value of the annotation. + */ + readonly attribute nsIVariant annotationValue; +}; diff --git a/toolkit/components/places/tests/unit/test_annotations.js b/toolkit/components/places/tests/unit/test_annotations.js index e5ce4336519d..c6b950442405 100644 --- a/toolkit/components/places/tests/unit/test_annotations.js +++ b/toolkit/components/places/tests/unit/test_annotations.js @@ -357,3 +357,57 @@ add_task(function test_execute() annosvc.removeObserver(annoObserver); }); + +add_test(function test_getAnnotationsHavingName() { + let uri = NetUtil.newURI("http://cat.mozilla.org"); + let id = PlacesUtils.bookmarks.insertBookmark( + PlacesUtils.unfiledBookmarksFolderId, uri, + PlacesUtils.bookmarks.DEFAULT_INDEX, "cat"); + let fid = PlacesUtils.bookmarks.createFolder( + PlacesUtils.unfiledBookmarksFolderId, "pillow", + PlacesUtils.bookmarks.DEFAULT_INDEX); + + const ANNOS = { + "int": 7, + "double": 7.7, + "string": "seven" + }; + for (let name in ANNOS) { + PlacesUtils.annotations.setPageAnnotation( + uri, name, ANNOS[name], 0, + PlacesUtils.annotations.EXPIRE_SESSION); + PlacesUtils.annotations.setItemAnnotation( + id, name, ANNOS[name], 0, + PlacesUtils.annotations.EXPIRE_SESSION); + PlacesUtils.annotations.setItemAnnotation( + fid, name, ANNOS[name], 0, + PlacesUtils.annotations.EXPIRE_SESSION); + } + + for (let name in ANNOS) { + let results = PlacesUtils.annotations.getAnnotationsWithName(name); + do_check_eq(results.length, 3); + + for (let result of results) { + do_check_eq(result.annotationName, name); + do_check_eq(result.annotationValue, ANNOS[name]); + if (result.uri) + do_check_true(result.uri.equals(uri)); + else + do_check_true(result.itemId > 0); + + if (result.itemId != -1) { + if (result.uri) + do_check_eq(result.itemId, id); + else + do_check_eq(result.itemId, fid); + do_check_guid_for_bookmark(result.itemId, result.guid); + } + else { + do_check_guid_for_uri(result.uri, result.guid); + } + } + } + + run_next_test(); +});