michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: michael@0: #include "nsAnnotationService.h" michael@0: #include "nsNavHistory.h" michael@0: #include "nsPlacesTables.h" michael@0: #include "nsPlacesIndexes.h" michael@0: #include "nsPlacesMacros.h" michael@0: #include "Helpers.h" michael@0: michael@0: #include "nsNetUtil.h" michael@0: #include "nsIVariant.h" michael@0: #include "nsString.h" michael@0: #include "nsVariant.h" michael@0: #include "mozilla/storage.h" michael@0: michael@0: #include "GeckoProfiler.h" michael@0: michael@0: #include "nsNetCID.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::places; michael@0: michael@0: #define ENSURE_ANNO_TYPE(_type, _statement) \ michael@0: PR_BEGIN_MACRO \ michael@0: int32_t type = _statement->AsInt32(kAnnoIndex_Type); \ michael@0: NS_ENSURE_TRUE(type == nsIAnnotationService::_type, NS_ERROR_INVALID_ARG); \ michael@0: PR_END_MACRO michael@0: michael@0: #define NOTIFY_ANNOS_OBSERVERS(_notification) \ michael@0: PR_BEGIN_MACRO \ michael@0: for (int32_t i = 0; i < mObservers.Count(); i++) \ michael@0: mObservers[i]->_notification; \ michael@0: PR_END_MACRO michael@0: michael@0: const int32_t nsAnnotationService::kAnnoIndex_ID = 0; michael@0: const int32_t nsAnnotationService::kAnnoIndex_PageOrItem = 1; michael@0: const int32_t nsAnnotationService::kAnnoIndex_NameID = 2; michael@0: const int32_t nsAnnotationService::kAnnoIndex_Content = 3; michael@0: const int32_t nsAnnotationService::kAnnoIndex_Flags = 4; michael@0: const int32_t nsAnnotationService::kAnnoIndex_Expiration = 5; michael@0: const int32_t nsAnnotationService::kAnnoIndex_Type = 6; michael@0: const int32_t nsAnnotationService::kAnnoIndex_DateAdded = 7; michael@0: const int32_t nsAnnotationService::kAnnoIndex_LastModified = 8; michael@0: michael@0: namespace mozilla { michael@0: namespace places { michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// AnnotatedResult michael@0: michael@0: AnnotatedResult::AnnotatedResult(const nsCString& aGUID, michael@0: nsIURI* aURI, michael@0: int64_t aItemId, michael@0: const nsACString& aAnnotationName, michael@0: nsIVariant* aAnnotationValue) michael@0: : mGUID(aGUID) michael@0: , mURI(aURI) michael@0: , mItemId(aItemId) michael@0: , mAnnotationName(aAnnotationName) michael@0: , mAnnotationValue(aAnnotationValue) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AnnotatedResult::GetGuid(nsACString& _guid) michael@0: { michael@0: _guid = mGUID; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AnnotatedResult::GetUri(nsIURI** _uri) michael@0: { michael@0: NS_IF_ADDREF(*_uri = mURI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AnnotatedResult::GetItemId(int64_t* _itemId) michael@0: { michael@0: *_itemId = mItemId; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AnnotatedResult::GetAnnotationName(nsACString& _annotationName) michael@0: { michael@0: _annotationName = mAnnotationName; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AnnotatedResult::GetAnnotationValue(nsIVariant** _annotationValue) michael@0: { michael@0: NS_IF_ADDREF(*_annotationValue = mAnnotationValue); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(AnnotatedResult, mozIAnnotatedResult) michael@0: michael@0: } // namespace places michael@0: } // namespace mozilla michael@0: michael@0: PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsAnnotationService, gAnnotationService) michael@0: michael@0: NS_IMPL_ISUPPORTS(nsAnnotationService michael@0: , nsIAnnotationService michael@0: , nsIObserver michael@0: , nsISupportsWeakReference michael@0: ) michael@0: michael@0: michael@0: nsAnnotationService::nsAnnotationService() michael@0: : mHasSessionAnnotations(false) michael@0: { michael@0: NS_ASSERTION(!gAnnotationService, michael@0: "Attempting to create two instances of the service!"); michael@0: gAnnotationService = this; michael@0: } michael@0: michael@0: michael@0: nsAnnotationService::~nsAnnotationService() michael@0: { michael@0: NS_ASSERTION(gAnnotationService == this, michael@0: "Deleting a non-singleton instance of the service"); michael@0: if (gAnnotationService == this) michael@0: gAnnotationService = nullptr; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsAnnotationService::Init() michael@0: { michael@0: mDB = Database::GetDatabase(); michael@0: NS_ENSURE_STATE(mDB); michael@0: michael@0: nsCOMPtr obsSvc = mozilla::services::GetObserverService(); michael@0: if (obsSvc) { michael@0: (void)obsSvc->AddObserver(this, TOPIC_PLACES_SHUTDOWN, true); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsAnnotationService::SetAnnotationStringInternal(nsIURI* aURI, michael@0: int64_t aItemId, michael@0: const nsACString& aName, michael@0: const nsAString& aValue, michael@0: int32_t aFlags, michael@0: uint16_t aExpiration) michael@0: { michael@0: mozStorageTransaction transaction(mDB->MainConn(), false); michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration, michael@0: nsIAnnotationService::TYPE_STRING, michael@0: statement); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: mozStorageStatementScoper scoper(statement); michael@0: michael@0: rv = statement->BindStringByName(NS_LITERAL_CSTRING("content"), aValue); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = statement->Execute(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = transaction.Commit(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::SetPageAnnotation(nsIURI* aURI, michael@0: const nsACString& aName, michael@0: nsIVariant* aValue, michael@0: int32_t aFlags, michael@0: uint16_t aExpiration) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: NS_ENSURE_ARG(aValue); michael@0: michael@0: uint16_t dataType; michael@0: nsresult rv = aValue->GetDataType(&dataType); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: switch (dataType) { michael@0: case nsIDataType::VTYPE_INT8: michael@0: case nsIDataType::VTYPE_UINT8: michael@0: case nsIDataType::VTYPE_INT16: michael@0: case nsIDataType::VTYPE_UINT16: michael@0: case nsIDataType::VTYPE_INT32: michael@0: case nsIDataType::VTYPE_UINT32: michael@0: case nsIDataType::VTYPE_BOOL: { michael@0: int32_t valueInt; michael@0: rv = aValue->GetAsInt32(&valueInt); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = SetPageAnnotationInt32(aURI, aName, valueInt, aFlags, aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: // Fall through int64_t case otherwise. michael@0: } michael@0: case nsIDataType::VTYPE_INT64: michael@0: case nsIDataType::VTYPE_UINT64: { michael@0: int64_t valueLong; michael@0: rv = aValue->GetAsInt64(&valueLong); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = SetPageAnnotationInt64(aURI, aName, valueLong, aFlags, aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: // Fall through double case otherwise. michael@0: } michael@0: case nsIDataType::VTYPE_FLOAT: michael@0: case nsIDataType::VTYPE_DOUBLE: { michael@0: double valueDouble; michael@0: rv = aValue->GetAsDouble(&valueDouble); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = SetPageAnnotationDouble(aURI, aName, valueDouble, aFlags, aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: case nsIDataType::VTYPE_CHAR: michael@0: case nsIDataType::VTYPE_WCHAR: michael@0: case nsIDataType::VTYPE_DOMSTRING: michael@0: case nsIDataType::VTYPE_CHAR_STR: michael@0: case nsIDataType::VTYPE_WCHAR_STR: michael@0: case nsIDataType::VTYPE_STRING_SIZE_IS: michael@0: case nsIDataType::VTYPE_WSTRING_SIZE_IS: michael@0: case nsIDataType::VTYPE_UTF8STRING: michael@0: case nsIDataType::VTYPE_CSTRING: michael@0: case nsIDataType::VTYPE_ASTRING: { michael@0: nsAutoString stringValue; michael@0: rv = aValue->GetAsAString(stringValue); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = SetPageAnnotationString(aURI, aName, stringValue, aFlags, aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::SetItemAnnotation(int64_t aItemId, michael@0: const nsACString& aName, michael@0: nsIVariant* aValue, michael@0: int32_t aFlags, michael@0: uint16_t aExpiration) michael@0: { michael@0: PROFILER_LABEL("AnnotationService", "SetItemAnnotation"); michael@0: NS_ENSURE_ARG_MIN(aItemId, 1); michael@0: NS_ENSURE_ARG(aValue); michael@0: michael@0: if (aExpiration == EXPIRE_WITH_HISTORY) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: uint16_t dataType; michael@0: nsresult rv = aValue->GetDataType(&dataType); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: switch (dataType) { michael@0: case nsIDataType::VTYPE_INT8: michael@0: case nsIDataType::VTYPE_UINT8: michael@0: case nsIDataType::VTYPE_INT16: michael@0: case nsIDataType::VTYPE_UINT16: michael@0: case nsIDataType::VTYPE_INT32: michael@0: case nsIDataType::VTYPE_UINT32: michael@0: case nsIDataType::VTYPE_BOOL: { michael@0: int32_t valueInt; michael@0: rv = aValue->GetAsInt32(&valueInt); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = SetItemAnnotationInt32(aItemId, aName, valueInt, aFlags, aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: // Fall through int64_t case otherwise. michael@0: } michael@0: case nsIDataType::VTYPE_INT64: michael@0: case nsIDataType::VTYPE_UINT64: { michael@0: int64_t valueLong; michael@0: rv = aValue->GetAsInt64(&valueLong); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = SetItemAnnotationInt64(aItemId, aName, valueLong, aFlags, aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: // Fall through double case otherwise. michael@0: } michael@0: case nsIDataType::VTYPE_FLOAT: michael@0: case nsIDataType::VTYPE_DOUBLE: { michael@0: double valueDouble; michael@0: rv = aValue->GetAsDouble(&valueDouble); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = SetItemAnnotationDouble(aItemId, aName, valueDouble, aFlags, aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: case nsIDataType::VTYPE_CHAR: michael@0: case nsIDataType::VTYPE_WCHAR: michael@0: case nsIDataType::VTYPE_DOMSTRING: michael@0: case nsIDataType::VTYPE_CHAR_STR: michael@0: case nsIDataType::VTYPE_WCHAR_STR: michael@0: case nsIDataType::VTYPE_STRING_SIZE_IS: michael@0: case nsIDataType::VTYPE_WSTRING_SIZE_IS: michael@0: case nsIDataType::VTYPE_UTF8STRING: michael@0: case nsIDataType::VTYPE_CSTRING: michael@0: case nsIDataType::VTYPE_ASTRING: { michael@0: nsAutoString stringValue; michael@0: rv = aValue->GetAsAString(stringValue); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = SetItemAnnotationString(aItemId, aName, stringValue, aFlags, aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::SetPageAnnotationString(nsIURI* aURI, michael@0: const nsACString& aName, michael@0: const nsAString& aValue, michael@0: int32_t aFlags, michael@0: uint16_t aExpiration) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: michael@0: nsresult rv = SetAnnotationStringInternal(aURI, 0, aName, aValue, michael@0: aFlags, aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NOTIFY_ANNOS_OBSERVERS(OnPageAnnotationSet(aURI, aName)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::SetItemAnnotationString(int64_t aItemId, michael@0: const nsACString& aName, michael@0: const nsAString& aValue, michael@0: int32_t aFlags, michael@0: uint16_t aExpiration) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aItemId, 1); michael@0: michael@0: if (aExpiration == EXPIRE_WITH_HISTORY) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsresult rv = SetAnnotationStringInternal(nullptr, aItemId, aName, aValue, michael@0: aFlags, aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsAnnotationService::SetAnnotationInt32Internal(nsIURI* aURI, michael@0: int64_t aItemId, michael@0: const nsACString& aName, michael@0: int32_t aValue, michael@0: int32_t aFlags, michael@0: uint16_t aExpiration) michael@0: { michael@0: mozStorageTransaction transaction(mDB->MainConn(), false); michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration, michael@0: nsIAnnotationService::TYPE_INT32, michael@0: statement); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: mozStorageStatementScoper scoper(statement); michael@0: michael@0: rv = statement->BindInt32ByName(NS_LITERAL_CSTRING("content"), aValue); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = statement->Execute(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = transaction.Commit(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::SetPageAnnotationInt32(nsIURI* aURI, michael@0: const nsACString& aName, michael@0: int32_t aValue, michael@0: int32_t aFlags, michael@0: uint16_t aExpiration) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: michael@0: nsresult rv = SetAnnotationInt32Internal(aURI, 0, aName, aValue, michael@0: aFlags, aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NOTIFY_ANNOS_OBSERVERS(OnPageAnnotationSet(aURI, aName)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::SetItemAnnotationInt32(int64_t aItemId, michael@0: const nsACString& aName, michael@0: int32_t aValue, michael@0: int32_t aFlags, michael@0: uint16_t aExpiration) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aItemId, 1); michael@0: michael@0: if (aExpiration == EXPIRE_WITH_HISTORY) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsresult rv = SetAnnotationInt32Internal(nullptr, aItemId, aName, aValue, michael@0: aFlags, aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsAnnotationService::SetAnnotationInt64Internal(nsIURI* aURI, michael@0: int64_t aItemId, michael@0: const nsACString& aName, michael@0: int64_t aValue, michael@0: int32_t aFlags, michael@0: uint16_t aExpiration) michael@0: { michael@0: mozStorageTransaction transaction(mDB->MainConn(), false); michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration, michael@0: nsIAnnotationService::TYPE_INT64, michael@0: statement); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: mozStorageStatementScoper scoper(statement); michael@0: michael@0: rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("content"), aValue); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = statement->Execute(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = transaction.Commit(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::SetPageAnnotationInt64(nsIURI* aURI, michael@0: const nsACString& aName, michael@0: int64_t aValue, michael@0: int32_t aFlags, michael@0: uint16_t aExpiration) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: michael@0: nsresult rv = SetAnnotationInt64Internal(aURI, 0, aName, aValue, michael@0: aFlags, aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NOTIFY_ANNOS_OBSERVERS(OnPageAnnotationSet(aURI, aName)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::SetItemAnnotationInt64(int64_t aItemId, michael@0: const nsACString& aName, michael@0: int64_t aValue, michael@0: int32_t aFlags, michael@0: uint16_t aExpiration) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aItemId, 1); michael@0: michael@0: if (aExpiration == EXPIRE_WITH_HISTORY) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsresult rv = SetAnnotationInt64Internal(nullptr, aItemId, aName, aValue, michael@0: aFlags, aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsAnnotationService::SetAnnotationDoubleInternal(nsIURI* aURI, michael@0: int64_t aItemId, michael@0: const nsACString& aName, michael@0: double aValue, michael@0: int32_t aFlags, michael@0: uint16_t aExpiration) michael@0: { michael@0: mozStorageTransaction transaction(mDB->MainConn(), false); michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration, michael@0: nsIAnnotationService::TYPE_DOUBLE, michael@0: statement); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: mozStorageStatementScoper scoper(statement); michael@0: michael@0: rv = statement->BindDoubleByName(NS_LITERAL_CSTRING("content"), aValue); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = statement->Execute(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = transaction.Commit(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::SetPageAnnotationDouble(nsIURI* aURI, michael@0: const nsACString& aName, michael@0: double aValue, michael@0: int32_t aFlags, michael@0: uint16_t aExpiration) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: michael@0: nsresult rv = SetAnnotationDoubleInternal(aURI, 0, aName, aValue, michael@0: aFlags, aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NOTIFY_ANNOS_OBSERVERS(OnPageAnnotationSet(aURI, aName)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::SetItemAnnotationDouble(int64_t aItemId, michael@0: const nsACString& aName, michael@0: double aValue, michael@0: int32_t aFlags, michael@0: uint16_t aExpiration) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aItemId, 1); michael@0: michael@0: if (aExpiration == EXPIRE_WITH_HISTORY) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsresult rv = SetAnnotationDoubleInternal(nullptr, aItemId, aName, aValue, michael@0: aFlags, aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetPageAnnotationString(nsIURI* aURI, michael@0: const nsACString& aName, michael@0: nsAString& _retval) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartGetAnnotation(aURI, 0, aName, statement); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mozStorageStatementScoper scoper(statement); michael@0: ENSURE_ANNO_TYPE(TYPE_STRING, statement); michael@0: rv = statement->GetString(kAnnoIndex_Content, _retval); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetItemAnnotationString(int64_t aItemId, michael@0: const nsACString& aName, michael@0: nsAString& _retval) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aItemId, 1); michael@0: michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartGetAnnotation(nullptr, aItemId, aName, statement); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mozStorageStatementScoper scoper(statement); michael@0: ENSURE_ANNO_TYPE(TYPE_STRING, statement); michael@0: rv = statement->GetString(kAnnoIndex_Content, _retval); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetPageAnnotation(nsIURI* aURI, michael@0: const nsACString& aName, michael@0: nsIVariant** _retval) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartGetAnnotation(aURI, 0, aName, statement); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mozStorageStatementScoper scoper(statement); michael@0: michael@0: nsCOMPtr value = new nsVariant(); michael@0: int32_t type = statement->AsInt32(kAnnoIndex_Type); michael@0: switch (type) { michael@0: case nsIAnnotationService::TYPE_INT32: michael@0: case nsIAnnotationService::TYPE_INT64: michael@0: case nsIAnnotationService::TYPE_DOUBLE: { michael@0: rv = value->SetAsDouble(statement->AsDouble(kAnnoIndex_Content)); michael@0: break; michael@0: } michael@0: case nsIAnnotationService::TYPE_STRING: { michael@0: nsAutoString valueString; michael@0: rv = statement->GetString(kAnnoIndex_Content, valueString); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = value->SetAsAString(valueString); michael@0: break; michael@0: } michael@0: default: { michael@0: rv = NS_ERROR_UNEXPECTED; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv)) michael@0: NS_ADDREF(*_retval = value); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetItemAnnotation(int64_t aItemId, michael@0: const nsACString& aName, michael@0: nsIVariant** _retval) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aItemId, 1); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartGetAnnotation(nullptr, aItemId, aName, statement); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mozStorageStatementScoper scoper(statement); michael@0: michael@0: nsCOMPtr value = new nsVariant(); michael@0: int32_t type = statement->AsInt32(kAnnoIndex_Type); michael@0: switch (type) { michael@0: case nsIAnnotationService::TYPE_INT32: michael@0: case nsIAnnotationService::TYPE_INT64: michael@0: case nsIAnnotationService::TYPE_DOUBLE: { michael@0: rv = value->SetAsDouble(statement->AsDouble(kAnnoIndex_Content)); michael@0: break; michael@0: } michael@0: case nsIAnnotationService::TYPE_STRING: { michael@0: nsAutoString valueString; michael@0: rv = statement->GetString(kAnnoIndex_Content, valueString); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = value->SetAsAString(valueString); michael@0: break; michael@0: } michael@0: default: { michael@0: rv = NS_ERROR_UNEXPECTED; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv)) michael@0: NS_ADDREF(*_retval = value); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetPageAnnotationInt32(nsIURI* aURI, michael@0: const nsACString& aName, michael@0: int32_t* _retval) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartGetAnnotation(aURI, 0, aName, statement); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mozStorageStatementScoper scoper(statement); michael@0: ENSURE_ANNO_TYPE(TYPE_INT32, statement); michael@0: *_retval = statement->AsInt32(kAnnoIndex_Content); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetItemAnnotationInt32(int64_t aItemId, michael@0: const nsACString& aName, michael@0: int32_t* _retval) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aItemId, 1); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartGetAnnotation(nullptr, aItemId, aName, statement); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mozStorageStatementScoper scoper(statement); michael@0: ENSURE_ANNO_TYPE(TYPE_INT32, statement); michael@0: *_retval = statement->AsInt32(kAnnoIndex_Content); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetPageAnnotationInt64(nsIURI* aURI, michael@0: const nsACString& aName, michael@0: int64_t* _retval) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartGetAnnotation(aURI, 0, aName, statement); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mozStorageStatementScoper scoper(statement); michael@0: ENSURE_ANNO_TYPE(TYPE_INT64, statement); michael@0: *_retval = statement->AsInt64(kAnnoIndex_Content); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetItemAnnotationInt64(int64_t aItemId, michael@0: const nsACString& aName, michael@0: int64_t* _retval) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aItemId, 1); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartGetAnnotation(nullptr, aItemId, aName, statement); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mozStorageStatementScoper scoper(statement); michael@0: ENSURE_ANNO_TYPE(TYPE_INT64, statement); michael@0: *_retval = statement->AsInt64(kAnnoIndex_Content); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetPageAnnotationType(nsIURI* aURI, michael@0: const nsACString& aName, michael@0: uint16_t* _retval) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartGetAnnotation(aURI, 0, aName, statement); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mozStorageStatementScoper scoper(statement); michael@0: *_retval = statement->AsInt32(kAnnoIndex_Type); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetItemAnnotationType(int64_t aItemId, michael@0: const nsACString& aName, michael@0: uint16_t* _retval) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aItemId, 1); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartGetAnnotation(nullptr, aItemId, aName, statement); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mozStorageStatementScoper scoper(statement); michael@0: *_retval = statement->AsInt32(kAnnoIndex_Type); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetPageAnnotationDouble(nsIURI* aURI, michael@0: const nsACString& aName, michael@0: double* _retval) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartGetAnnotation(aURI, 0, aName, statement); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mozStorageStatementScoper scoper(statement); michael@0: ENSURE_ANNO_TYPE(TYPE_DOUBLE, statement); michael@0: *_retval = statement->AsDouble(kAnnoIndex_Content); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetItemAnnotationDouble(int64_t aItemId, michael@0: const nsACString& aName, michael@0: double* _retval) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aItemId, 1); michael@0: michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartGetAnnotation(nullptr, aItemId, aName, statement); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mozStorageStatementScoper scoper(statement); michael@0: ENSURE_ANNO_TYPE(TYPE_DOUBLE, statement); michael@0: *_retval = statement->AsDouble(kAnnoIndex_Content); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetPageAnnotationInfo(nsIURI* aURI, michael@0: const nsACString& aName, michael@0: int32_t* _flags, michael@0: uint16_t* _expiration, michael@0: uint16_t* _storageType) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: NS_ENSURE_ARG_POINTER(_flags); michael@0: NS_ENSURE_ARG_POINTER(_expiration); michael@0: NS_ENSURE_ARG_POINTER(_storageType); michael@0: michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartGetAnnotation(aURI, 0, aName, statement); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mozStorageStatementScoper scoper(statement); michael@0: *_flags = statement->AsInt32(kAnnoIndex_Flags); michael@0: *_expiration = (uint16_t)statement->AsInt32(kAnnoIndex_Expiration); michael@0: int32_t type = (uint16_t)statement->AsInt32(kAnnoIndex_Type); michael@0: if (type == 0) { michael@0: // For annotations created before explicit typing, michael@0: // we can't determine type, just return as string type. michael@0: *_storageType = nsIAnnotationService::TYPE_STRING; michael@0: } michael@0: else michael@0: *_storageType = type; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetItemAnnotationInfo(int64_t aItemId, michael@0: const nsACString& aName, michael@0: int32_t* _flags, michael@0: uint16_t* _expiration, michael@0: uint16_t* _storageType) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aItemId, 1); michael@0: NS_ENSURE_ARG_POINTER(_flags); michael@0: NS_ENSURE_ARG_POINTER(_expiration); michael@0: NS_ENSURE_ARG_POINTER(_storageType); michael@0: michael@0: nsCOMPtr statement; michael@0: nsresult rv = StartGetAnnotation(nullptr, aItemId, aName, statement); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mozStorageStatementScoper scoper(statement); michael@0: *_flags = statement->AsInt32(kAnnoIndex_Flags); michael@0: *_expiration = (uint16_t)statement->AsInt32(kAnnoIndex_Expiration); michael@0: int32_t type = (uint16_t)statement->AsInt32(kAnnoIndex_Type); michael@0: if (type == 0) { michael@0: // For annotations created before explicit typing, michael@0: // we can't determine type, just return as string type. michael@0: *_storageType = nsIAnnotationService::TYPE_STRING; michael@0: } michael@0: else { michael@0: *_storageType = type; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetPagesWithAnnotation(const nsACString& aName, michael@0: uint32_t* _resultCount, michael@0: nsIURI*** _results) michael@0: { michael@0: NS_ENSURE_TRUE(!aName.IsEmpty(), NS_ERROR_INVALID_ARG); michael@0: NS_ENSURE_ARG_POINTER(_resultCount); michael@0: NS_ENSURE_ARG_POINTER(_results); michael@0: michael@0: *_resultCount = 0; michael@0: *_results = nullptr; michael@0: nsCOMArray results; michael@0: michael@0: nsresult rv = GetPagesWithAnnotationCOMArray(aName, &results); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Convert to raw array. michael@0: if (results.Count() == 0) michael@0: return NS_OK; michael@0: michael@0: *_results = static_cast michael@0: (nsMemory::Alloc(results.Count() * sizeof(nsIURI*))); michael@0: NS_ENSURE_TRUE(*_results, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: *_resultCount = results.Count(); michael@0: for (uint32_t i = 0; i < *_resultCount; i ++) { michael@0: (*_results)[i] = results[i]; michael@0: NS_ADDREF((*_results)[i]); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsAnnotationService::GetPagesWithAnnotationCOMArray(const nsACString& aName, michael@0: nsCOMArray* _results) michael@0: { michael@0: nsCOMPtr stmt = mDB->GetStatement( michael@0: "SELECT h.url " michael@0: "FROM moz_anno_attributes n " michael@0: "JOIN moz_annos a ON n.id = a.anno_attribute_id " michael@0: "JOIN moz_places h ON h.id = a.place_id " michael@0: "WHERE n.name = :anno_name" michael@0: ); michael@0: NS_ENSURE_STATE(stmt); michael@0: mozStorageStatementScoper scoper(stmt); michael@0: michael@0: nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), aName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool hasMore = false; michael@0: while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasMore)) && michael@0: hasMore) { michael@0: nsAutoCString uristring; michael@0: rv = stmt->GetUTF8String(0, uristring); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // convert to a URI, in case of some invalid URI, just ignore this row michael@0: // so we can mostly continue. michael@0: nsCOMPtr uri; michael@0: rv = NS_NewURI(getter_AddRefs(uri), uristring); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: michael@0: bool added = _results->AppendObject(uri); michael@0: NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetItemsWithAnnotation(const nsACString& aName, michael@0: uint32_t* _resultCount, michael@0: int64_t** _results) michael@0: { michael@0: NS_ENSURE_TRUE(!aName.IsEmpty(), NS_ERROR_INVALID_ARG); michael@0: NS_ENSURE_ARG_POINTER(_resultCount); michael@0: NS_ENSURE_ARG_POINTER(_results); michael@0: michael@0: *_resultCount = 0; michael@0: *_results = nullptr; michael@0: nsTArray results; michael@0: michael@0: nsresult rv = GetItemsWithAnnotationTArray(aName, &results); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Convert to raw array. michael@0: if (results.Length() == 0) michael@0: return NS_OK; michael@0: michael@0: *_results = static_cast michael@0: (nsMemory::Alloc(results.Length() * sizeof(int64_t))); michael@0: NS_ENSURE_TRUE(*_results, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: *_resultCount = results.Length(); michael@0: for (uint32_t i = 0; i < *_resultCount; i ++) { michael@0: (*_results)[i] = results[i]; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetAnnotationsWithName(const nsACString& aName, michael@0: uint32_t* _count, michael@0: mozIAnnotatedResult*** _annotations) michael@0: { michael@0: NS_ENSURE_ARG(!aName.IsEmpty()); michael@0: NS_ENSURE_ARG_POINTER(_annotations); michael@0: michael@0: *_count = 0; michael@0: *_annotations = nullptr; michael@0: nsCOMArray annotations; michael@0: michael@0: nsCOMPtr stmt = mDB->GetStatement( michael@0: "SELECT h.guid, h.url, -1, a.type, a.content " michael@0: "FROM moz_anno_attributes n " michael@0: "JOIN moz_annos a ON n.id = a.anno_attribute_id " michael@0: "JOIN moz_places h ON h.id = a.place_id " michael@0: "WHERE n.name = :anno_name " michael@0: "UNION ALL " michael@0: "SELECT b.guid, h.url, b.id, a.type, a.content " michael@0: "FROM moz_anno_attributes n " michael@0: "JOIN moz_items_annos a ON n.id = a.anno_attribute_id " michael@0: "JOIN moz_bookmarks b ON b.id = a.item_id " michael@0: "LEFT JOIN moz_places h ON h.id = b.fk " michael@0: "WHERE n.name = :anno_name " michael@0: ); michael@0: NS_ENSURE_STATE(stmt); michael@0: mozStorageStatementScoper scoper(stmt); michael@0: michael@0: nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), michael@0: aName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool hasMore = false; michael@0: while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasMore)) && hasMore) { michael@0: nsAutoCString guid; michael@0: rv = stmt->GetUTF8String(0, guid); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr uri; michael@0: bool uriIsNull = false; michael@0: rv = stmt->GetIsNull(1, &uriIsNull); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (!uriIsNull) { michael@0: nsAutoCString url; michael@0: rv = stmt->GetUTF8String(1, url); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = NS_NewURI(getter_AddRefs(uri), url); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: int64_t itemId = stmt->AsInt64(2); michael@0: int32_t type = stmt->AsInt32(3); michael@0: michael@0: nsCOMPtr variant = new nsVariant(); michael@0: switch (type) { michael@0: case nsIAnnotationService::TYPE_INT32: { michael@0: rv = variant->SetAsInt32(stmt->AsInt32(4)); michael@0: break; michael@0: } michael@0: case nsIAnnotationService::TYPE_INT64: { michael@0: rv = variant->SetAsInt64(stmt->AsInt64(4)); michael@0: break; michael@0: } michael@0: case nsIAnnotationService::TYPE_DOUBLE: { michael@0: rv = variant->SetAsDouble(stmt->AsDouble(4)); michael@0: break; michael@0: } michael@0: case nsIAnnotationService::TYPE_STRING: { michael@0: nsAutoString valueString; michael@0: rv = stmt->GetString(4, valueString); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = variant->SetAsAString(valueString); michael@0: break; michael@0: } michael@0: default: michael@0: MOZ_ASSERT(false, "Unsupported annotation type"); michael@0: // Move to the next result. michael@0: continue; michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr anno = new AnnotatedResult(guid, uri, itemId, michael@0: aName, variant); michael@0: NS_ENSURE_TRUE(annotations.AppendObject(anno), NS_ERROR_OUT_OF_MEMORY); michael@0: } michael@0: michael@0: // Convert to raw array. michael@0: if (annotations.Count() == 0) michael@0: return NS_OK; michael@0: michael@0: *_annotations = static_cast michael@0: (nsMemory::Alloc(annotations.Count() * sizeof(mozIAnnotatedResult*))); michael@0: NS_ENSURE_TRUE(*_annotations, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: *_count = annotations.Count(); michael@0: for (uint32_t i = 0; i < *_count; ++i) { michael@0: NS_ADDREF((*_annotations)[i] = annotations[i]); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsAnnotationService::GetItemsWithAnnotationTArray(const nsACString& aName, michael@0: nsTArray* _results) michael@0: { michael@0: nsCOMPtr stmt = mDB->GetStatement( michael@0: "SELECT a.item_id " michael@0: "FROM moz_anno_attributes n " michael@0: "JOIN moz_items_annos a ON n.id = a.anno_attribute_id " michael@0: "WHERE n.name = :anno_name" michael@0: ); michael@0: NS_ENSURE_STATE(stmt); michael@0: mozStorageStatementScoper scoper(stmt); michael@0: michael@0: nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), aName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool hasMore = false; michael@0: while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && michael@0: hasMore) { michael@0: if (!_results->AppendElement(stmt->AsInt64(0))) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetPageAnnotationNames(nsIURI* aURI, michael@0: uint32_t* _count, michael@0: nsIVariant*** _result) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: NS_ENSURE_ARG_POINTER(_count); michael@0: NS_ENSURE_ARG_POINTER(_result); michael@0: michael@0: *_count = 0; michael@0: *_result = nullptr; michael@0: michael@0: nsTArray names; michael@0: nsresult rv = GetAnnotationNamesTArray(aURI, 0, &names); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (names.Length() == 0) michael@0: return NS_OK; michael@0: michael@0: *_result = static_cast michael@0: (nsMemory::Alloc(sizeof(nsIVariant*) * names.Length())); michael@0: NS_ENSURE_TRUE(*_result, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: for (uint32_t i = 0; i < names.Length(); i ++) { michael@0: nsCOMPtr var = new nsVariant(); michael@0: if (!var) { michael@0: // need to release all the variants we've already created michael@0: for (uint32_t j = 0; j < i; j ++) michael@0: NS_RELEASE((*_result)[j]); michael@0: nsMemory::Free(*_result); michael@0: *_result = nullptr; michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: var->SetAsAUTF8String(names[i]); michael@0: NS_ADDREF((*_result)[i] = var); michael@0: } michael@0: *_count = names.Length(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsAnnotationService::GetAnnotationNamesTArray(nsIURI* aURI, michael@0: int64_t aItemId, michael@0: nsTArray* _result) michael@0: { michael@0: _result->Clear(); michael@0: michael@0: bool isItemAnnotation = (aItemId > 0); michael@0: nsCOMPtr statement; michael@0: if (isItemAnnotation) { michael@0: statement = mDB->GetStatement( michael@0: "SELECT n.name " michael@0: "FROM moz_anno_attributes n " michael@0: "JOIN moz_items_annos a ON a.anno_attribute_id = n.id " michael@0: "WHERE a.item_id = :item_id" michael@0: ); michael@0: } michael@0: else { michael@0: statement = mDB->GetStatement( michael@0: "SELECT n.name " michael@0: "FROM moz_anno_attributes n " michael@0: "JOIN moz_annos a ON a.anno_attribute_id = n.id " michael@0: "JOIN moz_places h ON h.id = a.place_id " michael@0: "WHERE h.url = :page_url" michael@0: ); michael@0: } michael@0: NS_ENSURE_STATE(statement); michael@0: mozStorageStatementScoper scoper(statement); michael@0: michael@0: nsresult rv; michael@0: if (isItemAnnotation) michael@0: rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId); michael@0: else michael@0: rv = URIBinder::Bind(statement, NS_LITERAL_CSTRING("page_url"), aURI); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool hasResult = false; michael@0: while (NS_SUCCEEDED(statement->ExecuteStep(&hasResult)) && michael@0: hasResult) { michael@0: nsAutoCString name; michael@0: rv = statement->GetUTF8String(0, name); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (!_result->AppendElement(name)) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::GetItemAnnotationNames(int64_t aItemId, michael@0: uint32_t* _count, michael@0: nsIVariant*** _result) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aItemId, 1); michael@0: NS_ENSURE_ARG_POINTER(_count); michael@0: NS_ENSURE_ARG_POINTER(_result); michael@0: michael@0: *_count = 0; michael@0: *_result = nullptr; michael@0: michael@0: nsTArray names; michael@0: nsresult rv = GetAnnotationNamesTArray(nullptr, aItemId, &names); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (names.Length() == 0) michael@0: return NS_OK; michael@0: michael@0: *_result = static_cast michael@0: (nsMemory::Alloc(sizeof(nsIVariant*) * names.Length())); michael@0: NS_ENSURE_TRUE(*_result, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: for (uint32_t i = 0; i < names.Length(); i ++) { michael@0: nsCOMPtr var = new nsVariant(); michael@0: if (!var) { michael@0: // need to release all the variants we've already created michael@0: for (uint32_t j = 0; j < i; j ++) michael@0: NS_RELEASE((*_result)[j]); michael@0: nsMemory::Free(*_result); michael@0: *_result = nullptr; michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: var->SetAsAUTF8String(names[i]); michael@0: NS_ADDREF((*_result)[i] = var); michael@0: } michael@0: *_count = names.Length(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::PageHasAnnotation(nsIURI* aURI, michael@0: const nsACString& aName, michael@0: bool* _retval) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: nsresult rv = HasAnnotationInternal(aURI, 0, aName, _retval); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::ItemHasAnnotation(int64_t aItemId, michael@0: const nsACString& aName, michael@0: bool* _retval) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aItemId, 1); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: nsresult rv = HasAnnotationInternal(nullptr, aItemId, aName, _retval); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * @note We don't remove anything from the moz_anno_attributes table. If we michael@0: * delete the last item of a given name, that item really should go away. michael@0: * It will be cleaned up by expiration. michael@0: */ michael@0: nsresult michael@0: nsAnnotationService::RemoveAnnotationInternal(nsIURI* aURI, michael@0: int64_t aItemId, michael@0: const nsACString& aName) michael@0: { michael@0: bool isItemAnnotation = (aItemId > 0); michael@0: nsCOMPtr statement; michael@0: if (isItemAnnotation) { michael@0: statement = mDB->GetStatement( michael@0: "DELETE FROM moz_items_annos " michael@0: "WHERE item_id = :item_id " michael@0: "AND anno_attribute_id = " michael@0: "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name)" michael@0: ); michael@0: } michael@0: else { michael@0: statement = mDB->GetStatement( michael@0: "DELETE FROM moz_annos " michael@0: "WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url) " michael@0: "AND anno_attribute_id = " michael@0: "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name)" michael@0: ); michael@0: } michael@0: NS_ENSURE_STATE(statement); michael@0: mozStorageStatementScoper scoper(statement); michael@0: michael@0: nsresult rv; michael@0: if (isItemAnnotation) michael@0: rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId); michael@0: else michael@0: rv = URIBinder::Bind(statement, NS_LITERAL_CSTRING("page_url"), aURI); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = statement->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), aName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = statement->Execute(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::RemovePageAnnotation(nsIURI* aURI, michael@0: const nsACString& aName) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: michael@0: nsresult rv = RemoveAnnotationInternal(aURI, 0, aName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NOTIFY_ANNOS_OBSERVERS(OnPageAnnotationRemoved(aURI, aName)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::RemoveItemAnnotation(int64_t aItemId, michael@0: const nsACString& aName) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aItemId, 1); michael@0: michael@0: nsresult rv = RemoveAnnotationInternal(nullptr, aItemId, aName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationRemoved(aItemId, aName)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::RemovePageAnnotations(nsIURI* aURI) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: michael@0: // Should this be precompiled or a getter? michael@0: nsCOMPtr statement = mDB->GetStatement( michael@0: "DELETE FROM moz_annos WHERE place_id = " michael@0: "(SELECT id FROM moz_places WHERE url = :page_url)" michael@0: ); michael@0: NS_ENSURE_STATE(statement); michael@0: mozStorageStatementScoper scoper(statement); michael@0: michael@0: nsresult rv = URIBinder::Bind(statement, NS_LITERAL_CSTRING("page_url"), aURI); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = statement->Execute(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Update observers michael@0: NOTIFY_ANNOS_OBSERVERS(OnPageAnnotationRemoved(aURI, EmptyCString())); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::RemoveItemAnnotations(int64_t aItemId) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aItemId, 1); michael@0: michael@0: // Should this be precompiled or a getter? michael@0: nsCOMPtr statement = mDB->GetStatement( michael@0: "DELETE FROM moz_items_annos WHERE item_id = :item_id" michael@0: ); michael@0: NS_ENSURE_STATE(statement); michael@0: mozStorageStatementScoper scoper(statement); michael@0: michael@0: nsresult rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = statement->Execute(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationRemoved(aItemId, EmptyCString())); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * @note If we use annotations for some standard items like GeckoFlags, it michael@0: * might be a good idea to blacklist these standard annotations from this michael@0: * copy function. michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::CopyPageAnnotations(nsIURI* aSourceURI, michael@0: nsIURI* aDestURI, michael@0: bool aOverwriteDest) michael@0: { michael@0: NS_ENSURE_ARG(aSourceURI); michael@0: NS_ENSURE_ARG(aDestURI); michael@0: michael@0: mozStorageTransaction transaction(mDB->MainConn(), false); michael@0: michael@0: nsCOMPtr sourceStmt = mDB->GetStatement( michael@0: "SELECT h.id, n.id, n.name, a2.id " michael@0: "FROM moz_places h " michael@0: "JOIN moz_annos a ON a.place_id = h.id " michael@0: "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id " michael@0: "LEFT JOIN moz_annos a2 ON a2.place_id = " michael@0: "(SELECT id FROM moz_places WHERE url = :dest_url) " michael@0: "AND a2.anno_attribute_id = n.id " michael@0: "WHERE url = :source_url" michael@0: ); michael@0: NS_ENSURE_STATE(sourceStmt); michael@0: mozStorageStatementScoper sourceScoper(sourceStmt); michael@0: michael@0: nsresult rv = URIBinder::Bind(sourceStmt, NS_LITERAL_CSTRING("source_url"), aSourceURI); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = URIBinder::Bind(sourceStmt, NS_LITERAL_CSTRING("dest_url"), aDestURI); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr copyStmt = mDB->GetStatement( michael@0: "INSERT INTO moz_annos " michael@0: "(place_id, anno_attribute_id, content, flags, expiration, " michael@0: "type, dateAdded, lastModified) " michael@0: "SELECT (SELECT id FROM moz_places WHERE url = :page_url), " michael@0: "anno_attribute_id, content, flags, expiration, type, " michael@0: ":date, :date " michael@0: "FROM moz_annos " michael@0: "WHERE place_id = :page_id " michael@0: "AND anno_attribute_id = :name_id" michael@0: ); michael@0: NS_ENSURE_STATE(copyStmt); michael@0: mozStorageStatementScoper copyScoper(copyStmt); michael@0: michael@0: bool hasResult; michael@0: while (NS_SUCCEEDED(sourceStmt->ExecuteStep(&hasResult)) && hasResult) { michael@0: int64_t sourcePlaceId = sourceStmt->AsInt64(0); michael@0: int64_t annoNameID = sourceStmt->AsInt64(1); michael@0: nsAutoCString annoName; michael@0: rv = sourceStmt->GetUTF8String(2, annoName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: int64_t annoExistsOnDest = sourceStmt->AsInt64(3); michael@0: michael@0: if (annoExistsOnDest) { michael@0: if (!aOverwriteDest) michael@0: continue; michael@0: rv = RemovePageAnnotation(aDestURI, annoName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // Copy the annotation. michael@0: mozStorageStatementScoper scoper(copyStmt); michael@0: rv = URIBinder::Bind(copyStmt, NS_LITERAL_CSTRING("page_url"), aDestURI); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = copyStmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), sourcePlaceId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = copyStmt->BindInt64ByName(NS_LITERAL_CSTRING("name_id"), annoNameID); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = copyStmt->BindInt64ByName(NS_LITERAL_CSTRING("date"), PR_Now()); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = copyStmt->Execute(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NOTIFY_ANNOS_OBSERVERS(OnPageAnnotationSet(aDestURI, annoName)); michael@0: } michael@0: michael@0: rv = transaction.Commit(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::CopyItemAnnotations(int64_t aSourceItemId, michael@0: int64_t aDestItemId, michael@0: bool aOverwriteDest) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aSourceItemId, 1); michael@0: NS_ENSURE_ARG_MIN(aDestItemId, 1); michael@0: michael@0: mozStorageTransaction transaction(mDB->MainConn(), false); michael@0: michael@0: nsCOMPtr sourceStmt = mDB->GetStatement( michael@0: "SELECT n.id, n.name, a2.id " michael@0: "FROM moz_bookmarks b " michael@0: "JOIN moz_items_annos a ON a.item_id = b.id " michael@0: "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id " michael@0: "LEFT JOIN moz_items_annos a2 ON a2.item_id = :dest_item_id " michael@0: "AND a2.anno_attribute_id = n.id " michael@0: "WHERE b.id = :source_item_id" michael@0: ); michael@0: NS_ENSURE_STATE(sourceStmt); michael@0: mozStorageStatementScoper sourceScoper(sourceStmt); michael@0: michael@0: nsresult rv = sourceStmt->BindInt64ByName(NS_LITERAL_CSTRING("source_item_id"), aSourceItemId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = sourceStmt->BindInt64ByName(NS_LITERAL_CSTRING("dest_item_id"), aDestItemId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr copyStmt = mDB->GetStatement( michael@0: "INSERT OR REPLACE INTO moz_items_annos " michael@0: "(item_id, anno_attribute_id, content, flags, expiration, " michael@0: "type, dateAdded, lastModified) " michael@0: "SELECT :dest_item_id, anno_attribute_id, content, flags, expiration, " michael@0: "type, :date, :date " michael@0: "FROM moz_items_annos " michael@0: "WHERE item_id = :source_item_id " michael@0: "AND anno_attribute_id = :name_id" michael@0: ); michael@0: NS_ENSURE_STATE(copyStmt); michael@0: mozStorageStatementScoper copyScoper(copyStmt); michael@0: michael@0: bool hasResult; michael@0: while (NS_SUCCEEDED(sourceStmt->ExecuteStep(&hasResult)) && hasResult) { michael@0: int64_t annoNameID = sourceStmt->AsInt64(0); michael@0: nsAutoCString annoName; michael@0: rv = sourceStmt->GetUTF8String(1, annoName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: int64_t annoExistsOnDest = sourceStmt->AsInt64(2); michael@0: michael@0: if (annoExistsOnDest) { michael@0: if (!aOverwriteDest) michael@0: continue; michael@0: rv = RemoveItemAnnotation(aDestItemId, annoName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // Copy the annotation. michael@0: mozStorageStatementScoper scoper(copyStmt); michael@0: rv = copyStmt->BindInt64ByName(NS_LITERAL_CSTRING("dest_item_id"), aDestItemId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = copyStmt->BindInt64ByName(NS_LITERAL_CSTRING("source_item_id"), aSourceItemId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = copyStmt->BindInt64ByName(NS_LITERAL_CSTRING("name_id"), annoNameID); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = copyStmt->BindInt64ByName(NS_LITERAL_CSTRING("date"), PR_Now()); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = copyStmt->Execute(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aDestItemId, annoName)); michael@0: } michael@0: michael@0: rv = transaction.Commit(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::AddObserver(nsIAnnotationObserver* aObserver) michael@0: { michael@0: NS_ENSURE_ARG(aObserver); michael@0: michael@0: if (mObservers.IndexOfObject(aObserver) >= 0) michael@0: return NS_ERROR_INVALID_ARG; // Already registered. michael@0: if (!mObservers.AppendObject(aObserver)) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::RemoveObserver(nsIAnnotationObserver* aObserver) michael@0: { michael@0: NS_ENSURE_ARG(aObserver); michael@0: michael@0: if (!mObservers.RemoveObject(aObserver)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsAnnotationService::HasAnnotationInternal(nsIURI* aURI, michael@0: int64_t aItemId, michael@0: const nsACString& aName, michael@0: bool* _hasAnno) michael@0: { michael@0: bool isItemAnnotation = (aItemId > 0); michael@0: nsCOMPtr stmt; michael@0: if (isItemAnnotation) { michael@0: stmt = mDB->GetStatement( michael@0: "SELECT b.id, " michael@0: "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, " michael@0: "a.id, a.dateAdded " michael@0: "FROM moz_bookmarks b " michael@0: "LEFT JOIN moz_items_annos a ON a.item_id = b.id " michael@0: "AND a.anno_attribute_id = nameid " michael@0: "WHERE b.id = :item_id" michael@0: ); michael@0: } michael@0: else { michael@0: stmt = mDB->GetStatement( michael@0: "SELECT h.id, " michael@0: "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, " michael@0: "a.id, a.dateAdded " michael@0: "FROM moz_places h " michael@0: "LEFT JOIN moz_annos a ON a.place_id = h.id " michael@0: "AND a.anno_attribute_id = nameid " michael@0: "WHERE h.url = :page_url" michael@0: ); michael@0: } michael@0: NS_ENSURE_STATE(stmt); michael@0: mozStorageStatementScoper checkAnnoScoper(stmt); michael@0: michael@0: nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), aName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (isItemAnnotation) michael@0: rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId); michael@0: else michael@0: rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool hasResult; michael@0: rv = stmt->ExecuteStep(&hasResult); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (!hasResult) { michael@0: // We are trying to get an annotation on an invalid bookmarks or michael@0: // history entry. michael@0: // Here we preserve the old behavior, returning that we don't have the michael@0: // annotation, ignoring the fact itemId is invalid. michael@0: // Otherwise we should return NS_ERROR_INVALID_ARG, but this will somehow michael@0: // break the API. In future we could want to be pickier. michael@0: *_hasAnno = false; michael@0: } michael@0: else { michael@0: int64_t annotationId = stmt->AsInt64(2); michael@0: *_hasAnno = (annotationId > 0); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * This loads the statement and steps it once so you can get data out of it. michael@0: * michael@0: * @note You have to reset the statement when you're done if this succeeds. michael@0: * @throws NS_ERROR_NOT_AVAILABLE if the annotation is not found. michael@0: */ michael@0: michael@0: nsresult michael@0: nsAnnotationService::StartGetAnnotation(nsIURI* aURI, michael@0: int64_t aItemId, michael@0: const nsACString& aName, michael@0: nsCOMPtr& aStatement) michael@0: { michael@0: bool isItemAnnotation = (aItemId > 0); michael@0: michael@0: if (isItemAnnotation) { michael@0: aStatement = mDB->GetStatement( michael@0: "SELECT a.id, a.item_id, :anno_name, a.content, a.flags, " michael@0: "a.expiration, a.type " michael@0: "FROM moz_anno_attributes n " michael@0: "JOIN moz_items_annos a ON a.anno_attribute_id = n.id " michael@0: "WHERE a.item_id = :item_id " michael@0: "AND n.name = :anno_name" michael@0: ); michael@0: } michael@0: else { michael@0: aStatement = mDB->GetStatement( michael@0: "SELECT a.id, a.place_id, :anno_name, a.content, a.flags, " michael@0: "a.expiration, a.type " michael@0: "FROM moz_anno_attributes n " michael@0: "JOIN moz_annos a ON n.id = a.anno_attribute_id " michael@0: "JOIN moz_places h ON h.id = a.place_id " michael@0: "WHERE h.url = :page_url " michael@0: "AND n.name = :anno_name" michael@0: ); michael@0: } michael@0: NS_ENSURE_STATE(aStatement); michael@0: mozStorageStatementScoper getAnnoScoper(aStatement); michael@0: michael@0: nsresult rv; michael@0: if (isItemAnnotation) michael@0: rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId); michael@0: else michael@0: rv = URIBinder::Bind(aStatement, NS_LITERAL_CSTRING("page_url"), aURI); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = aStatement->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), aName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool hasResult = false; michael@0: rv = aStatement->ExecuteStep(&hasResult); michael@0: if (NS_FAILED(rv) || !hasResult) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: // on success, DON'T reset the statement, the caller needs to read from it, michael@0: // and it is the caller's job to reset it. michael@0: getAnnoScoper.Abandon(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * This does most of the setup work needed to set an annotation, except for michael@0: * binding the the actual value and executing the statement. michael@0: * It will either update an existing annotation or insert a new one. michael@0: * michael@0: * @note The aStatement RESULT IS NOT ADDREFED. This is just one of the class michael@0: * vars, which control its scope. DO NOT RELEASE. michael@0: * The caller must take care of resetting the statement if this succeeds. michael@0: */ michael@0: nsresult michael@0: nsAnnotationService::StartSetAnnotation(nsIURI* aURI, michael@0: int64_t aItemId, michael@0: const nsACString& aName, michael@0: int32_t aFlags, michael@0: uint16_t aExpiration, michael@0: uint16_t aType, michael@0: nsCOMPtr& aStatement) michael@0: { michael@0: bool isItemAnnotation = (aItemId > 0); michael@0: michael@0: if (aExpiration == EXPIRE_SESSION) { michael@0: mHasSessionAnnotations = true; michael@0: } michael@0: michael@0: // Ensure the annotation name exists. michael@0: nsCOMPtr addNameStmt = mDB->GetStatement( michael@0: "INSERT OR IGNORE INTO moz_anno_attributes (name) VALUES (:anno_name)" michael@0: ); michael@0: NS_ENSURE_STATE(addNameStmt); michael@0: mozStorageStatementScoper scoper(addNameStmt); michael@0: michael@0: nsresult rv = addNameStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), aName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = addNameStmt->Execute(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // We have to check 2 things: michael@0: // - if the annotation already exists we should update it. michael@0: // - we should not allow setting annotations on invalid URIs or itemIds. michael@0: // This query will tell us: michael@0: // - whether the item or page exists. michael@0: // - whether the annotation already exists. michael@0: // - the nameID associated with the annotation name. michael@0: // - the id and dateAdded of the old annotation, if it exists. michael@0: nsCOMPtr stmt; michael@0: if (isItemAnnotation) { michael@0: stmt = mDB->GetStatement( michael@0: "SELECT b.id, " michael@0: "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, " michael@0: "a.id, a.dateAdded " michael@0: "FROM moz_bookmarks b " michael@0: "LEFT JOIN moz_items_annos a ON a.item_id = b.id " michael@0: "AND a.anno_attribute_id = nameid " michael@0: "WHERE b.id = :item_id" michael@0: ); michael@0: } michael@0: else { michael@0: stmt = mDB->GetStatement( michael@0: "SELECT h.id, " michael@0: "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, " michael@0: "a.id, a.dateAdded " michael@0: "FROM moz_places h " michael@0: "LEFT JOIN moz_annos a ON a.place_id = h.id " michael@0: "AND a.anno_attribute_id = nameid " michael@0: "WHERE h.url = :page_url" michael@0: ); michael@0: } michael@0: NS_ENSURE_STATE(stmt); michael@0: mozStorageStatementScoper checkAnnoScoper(stmt); michael@0: michael@0: rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), aName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (isItemAnnotation) michael@0: rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId); michael@0: else michael@0: rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool hasResult; michael@0: rv = stmt->ExecuteStep(&hasResult); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (!hasResult) { michael@0: // We are trying to create an annotation on an invalid bookmark michael@0: // or history entry. michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: int64_t fkId = stmt->AsInt64(0); michael@0: int64_t nameID = stmt->AsInt64(1); michael@0: int64_t oldAnnoId = stmt->AsInt64(2); michael@0: int64_t oldAnnoDate = stmt->AsInt64(3); michael@0: michael@0: if (isItemAnnotation) { michael@0: aStatement = mDB->GetStatement( michael@0: "INSERT OR REPLACE INTO moz_items_annos " michael@0: "(id, item_id, anno_attribute_id, content, flags, " michael@0: "expiration, type, dateAdded, lastModified) " michael@0: "VALUES (:id, :fk, :name_id, :content, :flags, " michael@0: ":expiration, :type, :date_added, :last_modified)" michael@0: ); michael@0: } michael@0: else { michael@0: aStatement = mDB->GetStatement( michael@0: "INSERT OR REPLACE INTO moz_annos " michael@0: "(id, place_id, anno_attribute_id, content, flags, " michael@0: "expiration, type, dateAdded, lastModified) " michael@0: "VALUES (:id, :fk, :name_id, :content, :flags, " michael@0: ":expiration, :type, :date_added, :last_modified)" michael@0: ); michael@0: } michael@0: NS_ENSURE_STATE(aStatement); michael@0: mozStorageStatementScoper setAnnoScoper(aStatement); michael@0: michael@0: // Don't replace existing annotations. michael@0: if (oldAnnoId > 0) { michael@0: rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), oldAnnoId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("date_added"), oldAnnoDate); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: else { michael@0: rv = aStatement->BindNullByName(NS_LITERAL_CSTRING("id")); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("date_added"), PR_Now()); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("fk"), fkId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("name_id"), nameID); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = aStatement->BindInt32ByName(NS_LITERAL_CSTRING("flags"), aFlags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = aStatement->BindInt32ByName(NS_LITERAL_CSTRING("expiration"), aExpiration); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = aStatement->BindInt32ByName(NS_LITERAL_CSTRING("type"), aType); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), PR_Now()); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // On success, leave the statement open, the caller will set the value michael@0: // and execute the statement. michael@0: setAnnoScoper.Abandon(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// nsIObserver michael@0: michael@0: NS_IMETHODIMP michael@0: nsAnnotationService::Observe(nsISupports *aSubject, michael@0: const char *aTopic, michael@0: const char16_t *aData) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread"); michael@0: michael@0: if (strcmp(aTopic, TOPIC_PLACES_SHUTDOWN) == 0) { michael@0: // Remove all session annotations, if any. michael@0: if (mHasSessionAnnotations) { michael@0: nsCOMPtr pageAnnoStmt = mDB->GetAsyncStatement( michael@0: "DELETE FROM moz_annos WHERE expiration = :expire_session" michael@0: ); michael@0: NS_ENSURE_STATE(pageAnnoStmt); michael@0: nsresult rv = pageAnnoStmt->BindInt32ByName(NS_LITERAL_CSTRING("expire_session"), michael@0: EXPIRE_SESSION); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr itemAnnoStmt = mDB->GetAsyncStatement( michael@0: "DELETE FROM moz_items_annos WHERE expiration = :expire_session" michael@0: ); michael@0: NS_ENSURE_STATE(itemAnnoStmt); michael@0: rv = itemAnnoStmt->BindInt32ByName(NS_LITERAL_CSTRING("expire_session"), michael@0: EXPIRE_SESSION); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mozIStorageBaseStatement *stmts[] = { michael@0: pageAnnoStmt.get() michael@0: , itemAnnoStmt.get() michael@0: }; michael@0: michael@0: nsCOMPtr ps; michael@0: rv = mDB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), nullptr, michael@0: getter_AddRefs(ps)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: }