* Moved changeBookmarkURI functionality from controller.js into

nsINavBookmarksService
* Added annotation and favicon handling to changeBookmarkURI
* Added brettw's copyAnnotations patch to nsIAnnotationService
* Added a GetAnnotationService() to the annotation service for faster
  intra-component access
* Added a getBookmarkFoldersTArray() to nsINavBookmarksService for easier
  intra-component memory management; updated getBookmarkFolders() to use it
* Added stub for tagRoot to nsINavBookmarksService; DO NOT USE until 329961
  has been resolved
* Quick fix for Add Bookmarks breakage caused by 329546

bug=330192
r=brettw@gmail.com
sr=bryner@bryanryner.com
This commit is contained in:
joe%retrovirus.com 2006-03-23 06:24:54 +00:00
parent 7a4858081c
commit baebbe6102
8 changed files with 300 additions and 38 deletions

View File

@ -1086,31 +1086,9 @@ var PlacesController = {
* metadata from the old URI to the new URI.
*/
changeBookmarkURI: function PC_changeBookmarkProperties(oldURI, newURI) {
this._assertURINotString(oldURI);
this._assertURINotString(newURI);
NS_ASSERT(this.bookmarks.isBookmarked(oldURI));
if (oldURI.spec == newURI.spec)
return;
var folders = this.bookmarks.getBookmarkFolders(oldURI, {});
this.bookmarks.beginUpdateBatch();
for (var i = 0; i < folders.length; i++) {
this.bookmarks.replaceItem(folders[i], oldURI, newURI);
}
this.bookmarks.setItemTitle(newURI,
this.bookmarks.getItemTitle(oldURI));
this.bookmarks.setKeywordForURI(newURI,
this.bookmarks.getKeywordForURI(oldURI));
LOG("TODO: move annotations over\n");
/*
this.annotations.copyAnnotations(oldURI, newURI, true);
this.annotations.removePageAnnotations(oldURI);
*/
this.bookmarks.endUpdateBatch();
this.bookmarks.changeBookmarkURI(oldURI, newURI);
},
get browserWindow() {
var wm =
Cc["@mozilla.org/appshell/window-mediator;1"].

View File

@ -550,7 +550,7 @@
// For all the nodes we've found, highlight the corresponding
// index in the tree.
var resultview = this.getResult();
var resultview = this.getResultView();
var selection = this.view.selection;
selection.clearSelection();
for(idx in nodes) {

View File

@ -65,7 +65,7 @@ interface nsIAnnotationObserver : nsISupports
void onAnnotationRemoved(in nsIURI aURI, in AUTF8String aName);
};
[scriptable, uuid(6c170b7c-b4d4-4068-8c5b-53827b10557c)]
[scriptable, uuid(a857c97f-7705-4376-b374-bd8799f69d51)]
interface nsIAnnotationService : nsISupports
{
/**
@ -264,6 +264,17 @@ interface nsIAnnotationService : nsISupports
*/
void removePageAnnotations(in nsIURI aURI);
/**
* Copies all annotations from the source to the destination URI. If the
* destination already has an annotation with the same name as one on the
* source, it will be overwritten if aOverwriteDest is set. Otherwise,
* the destination URIs will be preferred.
*
* All the source annotations will stay as-is. If you don't want them
* any more, use removePageAnnotations on that URI.
*/
void copyAnnotations(in nsIURI aSourceURI, in nsIURI aDestURI,
in boolean aOverwriteDest);
/**
* Adds an annotation observer. The annotation service will keep an owning

View File

@ -21,6 +21,7 @@
*
* Contributor(s):
* Brian Ryner <bryner@brianryner.com> (original author)
* Joe Hughes <joe@retrovirus.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -38,8 +39,15 @@
#include "nsISupports.idl"
%{C++
#include "nsTArray.h"
#include "prtypes.h"
%}
interface nsIURI;
[ptr] native PRInt64Array(nsTArray<PRInt64>);
/**
* Observer for bookmark changes.
*/
@ -191,7 +199,7 @@ interface nsINavBookmarkObserver : nsISupports
* folders. A URI in history can be contained in one or more such folders.
*/
[scriptable, uuid(5feca204-b735-40e8-921f-8b057eedffe2)]
[scriptable, uuid(6979da92-25d4-4553-8450-bfec2b3772db)]
interface nsINavBookmarksService : nsISupports
{
/**
@ -209,6 +217,14 @@ interface nsINavBookmarksService : nsISupports
*/
readonly attribute PRInt64 toolbarRoot;
/**
* The folder ID of the top-level folders that contain the tag "folders".
*
* NOTE: This isn't wired up yet, so don't try to use it for anything until
* bug 329961 is marked resolved.
*/
readonly attribute PRInt64 tagRoot;
/**
* Inserts a child item into the given folder. If this item already exists in
* the given folder, it will be moved to the new position.
@ -366,12 +382,32 @@ interface nsINavBookmarksService : nsISupports
*/
nsIURI getBookmarkedURIFor(in nsIURI uri);
/**
* "Changes" the URI of a bookmark. Since URIs are the unique
* identifiers of bookmarks in this system, what this really means is
* that all the metadata attached to oldURI will be copied onto newURI,
* and oldURI will be deleted from the bookmark system. If newURI
* is already bookmarked, the metadata from oldURI will overwrite
* the corresponding metadata on newURI. Any annotations on oldURI
* will be copied to newURI (a copy will remain on oldURI).
*/
void changeBookmarkURI(in nsIURI oldURI, in nsIURI newURI);
/**
* Returns the list of folder ids that contain the given URI.
*/
void getBookmarkFolders(in nsIURI uri, out PRUint32 count,
[array, retval, size_is(count)] out PRInt64 folders);
/**
* TArray version of getBookmarkFolders for ease of use in C++ code.
* Pass in a reference to a TArray; it will get cleared and filled with
* the resulting list of folder IDs.
*/
[noscript] void getBookmarkFoldersTArray(in nsIURI aURI,
in PRInt64Array aResult);
/**
* Returns the index of the given item in the given folder.
* Returns -1 if the item is not present in the folder.

View File

@ -60,6 +60,8 @@ const PRInt32 nsAnnotationService::kAnnoIndex_Expiration = 6;
static NS_DEFINE_CID(kmozStorageServiceCID, MOZ_STORAGE_SERVICE_CID);
static NS_DEFINE_CID(kmozStorageConnectionCID, MOZ_STORAGE_CONNECTION_CID);
nsAnnotationService* nsAnnotationService::gAnnotationService;
NS_IMPL_ISUPPORTS1(nsAnnotationService,
nsIAnnotationService)
@ -67,7 +69,9 @@ NS_IMPL_ISUPPORTS1(nsAnnotationService,
nsAnnotationService::nsAnnotationService()
{
NS_ASSERTION(!gAnnotationService,
"ATTEMPTING TO CREATE TWO INSTANCES OF THE ANNOTATION SERVICE!");
gAnnotationService = this;
}
@ -75,7 +79,10 @@ nsAnnotationService::nsAnnotationService()
nsAnnotationService::~nsAnnotationService()
{
NS_ASSERTION(gAnnotationService == this,
"Deleting a non-singleton annotation service");
if (gAnnotationService == this)
gAnnotationService = nsnull;
}
@ -647,6 +654,101 @@ nsAnnotationService::RemovePageAnnotations(nsIURI* aURI)
}
// nsAnnotationService::CopyAnnotations
//
// This function currently assumes there are very few annotations per
// URI and that brute-force is therefore a good strategy for intersecting
// the two sets. If this ends up not being the case, this function should
// be changed to do this more efficiently.
//
// XXX: If we use annotations for some standard items like GeckoFlags, it
// might be a good idea to blacklist these standard annotations from this
// copy function.
NS_IMETHODIMP
nsAnnotationService::CopyAnnotations(nsIURI* aSourceURI, nsIURI* aDestURI,
PRBool aOverwriteDest)
{
mozStorageTransaction transaction(mDBConn, PR_FALSE);
// source
nsTArray<nsCString> sourceNames;
nsresult rv = GetPageAnnotationNamesTArray(aSourceURI, &sourceNames);
NS_ENSURE_SUCCESS(rv, rv);
if (sourceNames.Length() == 0)
return NS_OK; // nothing to copy
// dest
nsTArray<nsCString> destNames;
rv = GetPageAnnotationNamesTArray(aDestURI, &destNames);
NS_ENSURE_SUCCESS(rv, rv);
// we assume you will only have a couple annotations max per URI
#ifdef DEBUG
if (sourceNames.Length() > 10 || destNames.Length() > 10) {
NS_WARNING("There are a lot of annotations, copying them may be inefficient.");
}
#endif
if (aOverwriteDest) {
// overwrite existing ones, remove dest dupes from DB and our list
for (PRUint32 i = 0; i < sourceNames.Length(); i ++) {
PRUint32 destIndex = destNames.IndexOf(sourceNames[i]);
if (destIndex != destNames.NoIndex) {
destNames.RemoveElementAt(destIndex);
RemoveAnnotation(aDestURI, sourceNames[i]);
}
}
} else {
// don't overwrite existing ones, remove dupes from the list of source names
for (PRUint32 i = 0; i < destNames.Length(); i ++) {
PRUint32 sourceIndex = sourceNames.IndexOf(destNames[i]);
if (sourceIndex != sourceNames.NoIndex)
sourceNames.RemoveElementAt(sourceIndex);
}
}
// given (sourceID, destID, name) this will insert a new annotation on
// source with the same values of the annotation on dest.
nsCOMPtr<mozIStorageStatement> statement;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"INSERT INTO moz_anno (page, name, mime_type, content, flags, expiration) "
"SELECT ?1, ?3, mime_type, content, flags, expiration "
"FROM moz_anno WHERE page = ?2 AND name = ?3"),
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
// Get the IDs of the pages in quesion. PERFORMANCE: This is the second time
// we do this for each page, since GetPageAnnotationNamesTArray does it when
// it gets the names. If this function requires optimization, we should only
// do this once and get the names ourselves using the IDs.
PRInt64 sourceID, destID;
nsNavHistory* history = nsNavHistory::GetHistoryService();
NS_ENSURE_TRUE(history, NS_ERROR_FAILURE);
rv = history->GetUrlIdFor(aSourceURI, &sourceID, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(sourceID, NS_ERROR_UNEXPECTED); // we should have caught this above
rv = history->GetUrlIdFor(aSourceURI, &destID, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(destID, NS_ERROR_UNEXPECTED); // we should have caught this above
// now we know to create annotations from all sources names and there won't
// be any collisions
for (PRUint32 i = 0; i < sourceNames.Length(); i ++) {
statement->BindInt64Parameter(0, sourceID);
statement->BindInt64Parameter(1, destID);
statement->BindUTF8StringParameter(2, sourceNames[i]);
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
}
transaction.Commit();
return NS_OK;
}
// nsAnnotationService::AddObserver
NS_IMETHODIMP

View File

@ -44,6 +44,7 @@
#include "nsCOMPtr.h"
#include "mozIStorageService.h"
#include "mozIStorageConnection.h"
#include "nsServiceManagerUtils.h"
class nsAnnotationService : public nsIAnnotationService
{
@ -54,6 +55,28 @@ public:
static nsresult InitTables(mozIStorageConnection* aDBConn);
/**
* Returns a cached pointer to the annotation service for consumers in the
* places directory.
*/
static nsAnnotationService* GetAnnotationService()
{
if (! gAnnotationService) {
// note that we actually have to set the service to a variable here
// because the work in do_GetService actually happens during assignment >:(
nsresult rv;
nsCOMPtr<nsIAnnotationService> serv(do_GetService("@mozilla.org/browser/annotation-service;1", &rv));
NS_ENSURE_SUCCESS(rv, nsnull);
// our constructor should have set the static variable. If it didn't,
// something is wrong.
NS_ASSERTION(gAnnotationService, "Annotation service creation failed");
}
// the service manager will keep the pointer to our service around, so
// this should always be valid even if nobody currently has a reference.
return gAnnotationService;
}
NS_DECL_ISUPPORTS
NS_DECL_NSIANNOTATIONSERVICE
@ -73,6 +96,8 @@ protected:
nsCOMArray<nsIAnnotationObserver> mObservers;
static nsAnnotationService* gAnnotationService;
static const int kAnnoIndex_ID;
static const int kAnnoIndex_Page;
static const int kAnnoIndex_Name;

View File

@ -42,9 +42,10 @@
#include "mozStorageHelper.h"
#include "nsIServiceManager.h"
#include "nsNetUtil.h"
#include "nsIAnnotationService.h"
#include "nsIRemoteContainer.h"
#include "nsUnicharUtils.h"
#include "nsFaviconService.h"
#include "nsAnnotationService.h"
const PRInt32 nsNavBookmarks::kFindBookmarksIndex_ItemChild = 0;
const PRInt32 nsNavBookmarks::kFindBookmarksIndex_FolderChild = 1;
@ -67,7 +68,7 @@ nsNavBookmarks* nsNavBookmarks::sInstance = nsnull;
#define ANNO_FOLDER_READONLY BOOKMARKS_ANNO_PREFIX "readonly"
nsNavBookmarks::nsNavBookmarks()
: mRoot(0), mBookmarksRoot(0), mToolbarRoot(0), mBatchLevel(0),
: mRoot(0), mBookmarksRoot(0), mToolbarRoot(0), mTagRoot(0), mBatchLevel(0),
mBatchHasTransaction(PR_FALSE)
{
NS_ASSERTION(!sInstance, "Multiple nsNavBookmarks instances!");
@ -385,6 +386,10 @@ nsNavBookmarks::InitRoots()
rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("toolbar"), &mToolbarRoot, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
getRootStatement->Reset();
rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("tags"), &mTagRoot, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
if (importDefaults) {
// when there is no places root, we should define the hierarchy by
// importing the default one.
@ -780,6 +785,13 @@ nsNavBookmarks::GetToolbarRoot(PRInt64 *aRoot)
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::GetTagRoot(PRInt64 *aRoot)
{
*aRoot = mTagRoot;
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::InsertItem(PRInt64 aFolder, nsIURI *aItem, PRInt32 aIndex)
{
@ -1779,28 +1791,124 @@ nsNavBookmarks::GetBookmarkedURIFor(nsIURI* aURI, nsIURI** _retval)
}
NS_IMETHODIMP
nsNavBookmarks::GetBookmarkFolders(nsIURI *aURI, PRUint32 *aCount,
PRInt64 **aFolders)
nsNavBookmarks::ChangeBookmarkURI(nsIURI *aOldURI, nsIURI *aNewURI)
{
*aCount = 0;
*aFolders = nsnull;
nsresult rv;
if (!aOldURI || !aNewURI)
return NS_ERROR_NULL_POINTER;
// This method is only meaningful if oldURI is actually bookmarked.
PRBool bookmarked = PR_FALSE;
rv = IsBookmarked(aOldURI, &bookmarked);
NS_ENSURE_SUCCESS(rv, rv);
if (!bookmarked)
return NS_ERROR_INVALID_ARG;
PRBool equal = PR_FALSE;
rv = aOldURI->Equals(aNewURI, &equal);
NS_ENSURE_SUCCESS(rv, rv);
if (equal) // no-op
return NS_OK;
// Now that we're satisfied with the quality of our input, the
// actual work starts here.
nsTArray<PRInt64> folders;
rv = GetBookmarkFoldersTArray(aOldURI, &folders);
NS_ENSURE_SUCCESS(rv, rv);
// The bookmark operations within the area in which "batch" is in scope
// will be within a batched operation. This allows the batch to be
// closed properly if we exit early due to an error condition.
nsBookmarksUpdateBatcher batch;
// in folders, replace all instances of old URI with new URI
for (PRUint32 i = 0; i < folders.Length(); i++) {
rv = ReplaceItem(folders[i], aOldURI, aNewURI);
NS_ENSURE_SUCCESS(rv, rv);
}
// copy title from old URI to new URI
nsAutoString title;
rv = GetItemTitle(aOldURI, title);
NS_ENSURE_SUCCESS(rv, rv);
if (!title.IsEmpty()) {
rv = SetItemTitle(aNewURI, title);
NS_ENSURE_SUCCESS(rv, rv);
}
// copy keyword (shortcut) from old URI to new URI
nsAutoString keyword;
rv = GetKeywordForURI(aOldURI, keyword);
NS_ENSURE_SUCCESS(rv, rv);
if (!keyword.IsEmpty()) {
rv = SetKeywordForURI(aNewURI, keyword);
NS_ENSURE_SUCCESS(rv, rv);
}
// copy annotations from old URI to new URI
nsAnnotationService* annoService =
nsAnnotationService::GetAnnotationService();
NS_ENSURE_TRUE(annoService, NS_ERROR_UNEXPECTED);
rv = annoService->CopyAnnotations(aOldURI, aNewURI, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
// associate favicon from old URI (if present) with new URI (if none already)
nsFaviconService* faviconService = nsFaviconService::GetFaviconService();
NS_ENSURE_TRUE(faviconService, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIURI> sourceFaviconURI;
rv = faviconService->GetFaviconForPage(aOldURI,
getter_AddRefs(sourceFaviconURI));
if (NS_SUCCEEDED(rv)) { // oldURI has favicon
nsCOMPtr<nsIURI> destFaviconURI;
rv = faviconService->GetFaviconForPage(aNewURI,
getter_AddRefs(destFaviconURI));
if (NS_FAILED(rv)) { // newURI has no favicon
rv = faviconService->SetFaviconUrlForPage(aNewURI, sourceFaviconURI);
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsNavBookmarks::GetBookmarkFoldersTArray(nsIURI *aURI,
nsTArray<PRInt64> *aResult)
{
mozStorageStatementScoper scope(mDBFindURIBookmarks);
mozStorageTransaction transaction(DBConn(), PR_FALSE);
nsresult rv = BindStatementURI(mDBFindURIBookmarks, 0, aURI);
NS_ENSURE_SUCCESS(rv, rv);
nsTArray<PRInt64> folders;
PRBool more;
while (NS_SUCCEEDED((rv = mDBFindURIBookmarks->ExecuteStep(&more))) && more) {
if (! folders.AppendElement(
if (! aResult->AppendElement(
mDBFindURIBookmarks->AsInt64(kFindBookmarksIndex_Parent)))
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ENSURE_SUCCESS(rv, rv);
return transaction.Commit();
}
NS_IMETHODIMP
nsNavBookmarks::GetBookmarkFolders(nsIURI *aURI, PRUint32 *aCount,
PRInt64 **aFolders)
{
*aCount = 0;
*aFolders = nsnull;
nsresult rv;
nsTArray<PRInt64> folders;
// Get the information from the DB as a TArray
rv = GetBookmarkFoldersTArray(aURI, &folders);
NS_ENSURE_SUCCESS(rv, rv);
// Copy the results into a new array for output
if (folders.Length()) {
*aFolders = NS_STATIC_CAST(PRInt64*,
nsMemory::Alloc(sizeof(PRInt64) * folders.Length()));
@ -1810,7 +1918,8 @@ nsNavBookmarks::GetBookmarkFolders(nsIURI *aURI, PRUint32 *aCount,
(*aFolders)[i] = folders[i];
}
*aCount = folders.Length();
return transaction.Commit();
return NS_OK;
}
NS_IMETHODIMP

View File

@ -116,6 +116,7 @@ private:
PRInt64 mRoot;
PRInt64 mBookmarksRoot;
PRInt64 mToolbarRoot;
PRInt64 mTagRoot;
// the level of nesting of batches, 0 when no batches are open
PRInt32 mBatchLevel;