--- a/toolkit/components/places/src/nsBookmarksHTML.cpp
+++ b/toolkit/components/places/src/nsBookmarksHTML.cpp
@@ -120,16 +120,17 @@ static NS_DEFINE_CID(kParserCID, NS_PARS
#define KEY_PLACESROOT_LOWER "places_root"
#define KEY_HREF_LOWER "href"
#define KEY_FEEDURL_LOWER "feedurl"
#define KEY_WEB_PANEL_LOWER "web_panel"
#define KEY_LASTCHARSET_LOWER "last_charset"
#define KEY_ICON_LOWER "icon"
#define KEY_ICON_URI_LOWER "icon_uri"
#define KEY_SHORTCUTURL_LOWER "shortcuturl"
+#define KEY_ID_LOWER "id"
#define LOAD_IN_SIDEBAR_ANNO NS_LITERAL_CSTRING("bookmarkProperties/loadInSidebar")
#define BOOKMARKS_MENU_ICON_URI "chrome://browser/skin/places/bookmarksMenu.png"
#define BOOKMARKS_TOOLBAR_ICON_URI "chrome://browser/skin/places/bookmarksToolbar.png"
// define to get debugging messages on console about import
//#define DEBUG_IMPORT
@@ -166,16 +167,19 @@ public:
// be nested so this will be 0 or 1.
PRInt32 mContainerNesting;
// when we find a heading tag, it actually affects the title of the NEXT
// container in the list. This stores that heading tag and whether it was
// special. 'ConsumeHeading' resets this.
ContainerType mLastContainerType;
+ // Container Id, see above
+ PRInt64 mLastContainerId;
+
// this contains the text from the last begin tag until now. It is reset
// at every begin tag. We can check it when we see a </a>, or </h3>
// to see what the text content of that node should be.
nsString mPreviousText;
// true when we hit a <dd>, which contains the description for the preceeding
// <a> tag. We can't just check for </dd> like we can for </a> or </h3>
// because if there is a sub-folder, it is actually a child of the <dd>
@@ -196,25 +200,25 @@ public:
// This is cleared whenever we hit a <h3>, so that we know NOT to save this
// with a bookmark, but to keep it until
nsCOMPtr<nsIURI> mPreviousLink;
// contains the URL of the previous livemark, so that when the link ends,
// and the livemark title is known, we can create it.
nsCOMPtr<nsIURI> mPreviousFeed;
- void ConsumeHeading(nsAString* aHeading, ContainerType* aContainerType)
+ void ConsumeHeading(nsAString* aHeading, ContainerType* aContainerType, PRInt64* aContainerId)
{
*aHeading = mPreviousText;
*aContainerType = mLastContainerType;
+ *aContainerId = mLastContainerId;
mPreviousText.Truncate(0);
}
- // Contains the id of a newly created bookmark.
- // XXXDietrich - may also be a pre-existing bookmark once we support importing Places-exported bookmarks.html files.
+ // Contains the id of an imported, or newly created bookmark.
PRInt64 mPreviousId;
};
/**
* The content sink stuff is based loosely on
*/
class BookmarkContentSink : public nsIHTMLContentSink
@@ -318,16 +322,18 @@ protected:
}
nsresult NewFrame();
nsresult PopFrame();
nsresult SetFaviconForURI(nsIURI* aPageURI, nsIURI* aFaviconURI,
const nsCString& aData);
nsresult SetFaviconForFolder(PRInt64 aFolder, const nsACString& aFavicon);
+ PRInt64 ConvertImportedIdToInternalId(const nsCString& aId);
+
#ifdef DEBUG_IMPORT
// prints spaces for indenting to the current frame depth
void PrintNesting()
{
for (PRUint32 i = 0; i < mFrames.Length(); i ++)
printf(" ");
}
#endif
@@ -560,16 +566,17 @@ void
BookmarkContentSink::HandleHeadBegin(const nsIParserNode& node)
{
BookmarkImportFrame& frame = CurFrame();
// after a heading, a previous bookmark is not applicable (for example, for
// the descriptions contained in a <dd>). Neither is any previous head type
frame.mPreviousLink = nsnull;
frame.mLastContainerType = BookmarkImportFrame::Container_Normal;
+ frame.mLastContainerId = 0;
// It is syntactically possible for a heading to appear after another heading
// but before the <dl> that encloses that folder's contents. This should not
// happen in practice, as the file will contain "<dl></dl>" sequence for
// empty containers.
//
// Just to be on the safe side, if we encounter
// <h3>FOO</h3>
@@ -594,16 +601,19 @@ BookmarkContentSink::HandleHeadBegin(con
frame.mLastContainerType = BookmarkImportFrame::Container_Toolbar;
break;
} else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_BOOKMARKSMENU_LOWER)) {
frame.mLastContainerType = BookmarkImportFrame::Container_Menu;
break;
} else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_PLACESROOT_LOWER)) {
frame.mLastContainerType = BookmarkImportFrame::Container_Places;
break;
+ } else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_ID_LOWER)) {
+ frame.mLastContainerId =
+ ConvertImportedIdToInternalId(NS_ConvertUTF16toUTF8(node.GetKeyAt(i)));
}
}
}
CurFrame().mPreviousText.Truncate(0);
}
// BookmarkContentSink::HandleHeadEnd
@@ -623,32 +633,38 @@ BookmarkContentSink::HandleHeadEnd()
//
// Handles "<a" tags by creating a new bookmark. The title of the bookmark
// will be the text content, which will be stuffed in mPreviousText for us
// and which will be saved by HandleLinkEnd
void
BookmarkContentSink::HandleLinkBegin(const nsIParserNode& node)
{
+ nsresult rv;
+
BookmarkImportFrame& frame = CurFrame();
// We need to make sure that the feed URIs from previous frames are emptied.
frame.mPreviousFeed = nsnull;
+ // We need to make sure that the bookmark id from previous frames are emptied.
+ frame.mPreviousId = 0;
+
// mPreviousText will hold our link text, clear it so that can be appended to
frame.mPreviousText.Truncate();
// get the attributes we care about
nsAutoString href;
nsAutoString feedUrl;
nsAutoString icon;
nsAutoString iconUri;
nsAutoString lastCharset;
nsAutoString keyword;
nsAutoString webPanel;
+ nsAutoString id;
PRInt32 attrCount = node.GetAttributeCount();
for (PRInt32 i = 0; i < attrCount; i ++) {
const nsAString& key = node.GetKeyAt(i);
if (key.LowerCaseEqualsLiteral(KEY_HREF_LOWER)) {
href = node.GetValueAt(i);
} else if (key.LowerCaseEqualsLiteral(KEY_FEEDURL_LOWER)) {
feedUrl = node.GetValueAt(i);
} else if (key.LowerCaseEqualsLiteral(KEY_ICON_LOWER)) {
@@ -656,25 +672,28 @@ BookmarkContentSink::HandleLinkBegin(con
} else if (key.LowerCaseEqualsLiteral(KEY_ICON_URI_LOWER)) {
iconUri = node.GetValueAt(i);
} else if (key.LowerCaseEqualsLiteral(KEY_LASTCHARSET_LOWER)) {
lastCharset = node.GetValueAt(i);
} else if (key.LowerCaseEqualsLiteral(KEY_SHORTCUTURL_LOWER)) {
keyword = node.GetValueAt(i);
} else if (key.LowerCaseEqualsLiteral(KEY_WEB_PANEL_LOWER)) {
webPanel = node.GetValueAt(i);
+ } else if (key.LowerCaseEqualsLiteral(KEY_ID_LOWER)) {
+ id = node.GetValueAt(i);
}
}
href.Trim(kWhitespace);
feedUrl.Trim(kWhitespace);
icon.Trim(kWhitespace);
iconUri.Trim(kWhitespace);
lastCharset.Trim(kWhitespace);
keyword.Trim(kWhitespace);
webPanel.Trim(kWhitespace);
+ id.Trim(kWhitespace);
// For feeds, get the feed URL. If it is invalid, it will leave mPreviousFeed
// NULL and we'll continue trying to create it as a normal bookmark.
if (! feedUrl.IsEmpty()) {
NS_NewURI(getter_AddRefs(frame.mPreviousFeed),
NS_ConvertUTF16toUTF8(feedUrl), nsnull);
}
@@ -693,25 +712,31 @@ BookmarkContentSink::HandleLinkBegin(con
nsresult rv = NS_NewURI(getter_AddRefs(frame.mPreviousLink),
href, nsnull);
if (NS_FAILED(rv) && ! frame.mPreviousFeed) {
frame.mPreviousLink = nsnull;
return; // invalid link
}
}
+ // if there's a pre-existing Places bookmark id, use it
+ frame.mPreviousId = ConvertImportedIdToInternalId(NS_ConvertUTF16toUTF8(id));
+
// if there is a feedURL, this is a livemark, which is a special case
- // that we handle in HandleLinkBegin(): don't create normal bookmarks
+ // that we handle in HandleLinkEnd(): don't create normal bookmarks
if (frame.mPreviousFeed)
return;
- // create the bookmark
- nsresult rv = mBookmarksService->InsertItem(frame.mContainerID, frame.mPreviousLink,
- mBookmarksService->DEFAULT_INDEX, &frame.mPreviousId);
- NS_ASSERTION(NS_SUCCEEDED(rv), "InsertItem failed");
+ // if no previous id (or a legacy id), create a new bookmark
+ if (frame.mPreviousId == 0) {
+ // create the bookmark
+ rv = mBookmarksService->InsertItem(frame.mContainerID, frame.mPreviousLink,
+ mBookmarksService->DEFAULT_INDEX, &frame.mPreviousId);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "InsertItem failed");
+ }
// save the favicon, ignore errors
if (! icon.IsEmpty() || ! iconUri.IsEmpty()) {
nsCOMPtr<nsIURI> iconUriObject;
NS_NewURI(getter_AddRefs(iconUriObject), iconUri);
if (! icon.IsEmpty() || iconUriObject)
SetFaviconForURI(frame.mPreviousLink, iconUriObject,
NS_ConvertUTF16toUTF8(icon));
@@ -743,39 +768,52 @@ BookmarkContentSink::HandleLinkBegin(con
//
// Saves the title for the given bookmark. This only writes the user title.
// Any previous title will be untouched. If this is a new entry, it will have
// an empty "official" title until you visit it.
void
BookmarkContentSink::HandleLinkEnd()
{
+ nsresult rv;
BookmarkImportFrame& frame = CurFrame();
frame.mPreviousText.Trim(kWhitespace);
if (frame.mPreviousFeed) {
// The bookmark is actually a livemark. Create it here.
// (It gets created here instead of in HandleLinkBegin()
// because we need to know the title before creating it.)
PRInt64 folderId;
- if (mIsImportDefaults) {
- mLivemarkService->CreateLivemarkFolderOnly(mBookmarksService,
- frame.mContainerID,
- frame.mPreviousText,
- frame.mPreviousLink,
- frame.mPreviousFeed,
- -1,
- &folderId);
+ if (frame.mPreviousId > 0) {
+ // It's a pre-existing livemark, so update its properties
+ rv = mLivemarkService->SetSiteURI(frame.mPreviousId, frame.mPreviousLink);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "SetSiteURI failed!");
+ rv = mLivemarkService->SetFeedURI(frame.mPreviousId, frame.mPreviousFeed);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "SetFeedURI failed!");
+ rv = mBookmarksService->SetFolderTitle(frame.mPreviousId, frame.mPreviousText);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "SetFolderTitle failed!");
} else {
- mLivemarkService->CreateLivemark(frame.mContainerID,
- frame.mPreviousText,
- frame.mPreviousLink,
- frame.mPreviousFeed,
- -1,
- &folderId);
+ if (mIsImportDefaults) {
+ rv = mLivemarkService->CreateLivemarkFolderOnly(mBookmarksService,
+ frame.mContainerID,
+ frame.mPreviousText,
+ frame.mPreviousLink,
+ frame.mPreviousFeed,
+ -1,
+ &folderId);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "CreateLivemarkFolderOnly failed!");
+ } else {
+ rv = mLivemarkService->CreateLivemark(frame.mContainerID,
+ frame.mPreviousText,
+ frame.mPreviousLink,
+ frame.mPreviousFeed,
+ -1,
+ &folderId);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "CreateLivemark failed!");
+ }
}
#ifdef DEBUG_IMPORT
PrintNesting();
printf("Creating livemark '%s'\n",
NS_ConvertUTF16toUTF8(frame.mPreviousText).get());
#endif
}
else if (frame.mPreviousLink) {
@@ -818,74 +856,78 @@ BookmarkContentSink::HandleSeparator()
// This is called when there is a new folder found. The folder takes the
// name from the previous frame's heading.
nsresult
BookmarkContentSink::NewFrame()
{
nsresult rv;
+ PRInt64 ourID = 0;
nsString containerName;
BookmarkImportFrame::ContainerType containerType;
- CurFrame().ConsumeHeading(&containerName, &containerType);
+ CurFrame().ConsumeHeading(&containerName, &containerType, &ourID);
PRBool updateFolder = PR_FALSE;
- PRInt64 ourID = 0;
- switch (containerType) {
- case BookmarkImportFrame::Container_Normal:
- // regular folder: use an existing folder if that name already exists
- rv = mBookmarksService->GetChildFolder(CurFrame().mContainerID,
- containerName, &ourID);
- NS_ENSURE_SUCCESS(rv, rv);
- if (! ourID) {
- // need to append a new folder
- rv = mBookmarksService->CreateFolder(CurFrame().mContainerID,
- containerName,
- mBookmarksService->DEFAULT_INDEX, &ourID);
+ if (ourID == 0) {
+ switch (containerType) {
+ case BookmarkImportFrame::Container_Normal:
+ // regular folder: use an existing folder if that name already exists
+ rv = mBookmarksService->GetChildFolder(CurFrame().mContainerID,
+ containerName, &ourID);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (ourID == 0) {
+ // need to append a new folder
+ rv = mBookmarksService->CreateFolder(CurFrame().mContainerID,
+ containerName,
+ mBookmarksService->DEFAULT_INDEX, &ourID);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ break;
+ case BookmarkImportFrame::Container_Places:
+ // places root, never reparent here, when we're building the initial
+ // hierarchy, it will only be defined at the top level
+ rv = mBookmarksService->GetPlacesRoot(&ourID);
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ case BookmarkImportFrame::Container_Menu:
+ // menu root
+ rv = mBookmarksService->GetBookmarksRoot(&ourID);
NS_ENSURE_SUCCESS(rv, rv);
- }
- break;
- case BookmarkImportFrame::Container_Places:
- // places root, never reparent here, when we're building the initial
- // hierarchy, it will only be defined at the top level
- rv = mBookmarksService->GetPlacesRoot(&ourID);
- NS_ENSURE_SUCCESS(rv, rv);
- break;
- case BookmarkImportFrame::Container_Menu:
- // menu root
- rv = mBookmarksService->GetBookmarksRoot(&ourID);
- NS_ENSURE_SUCCESS(rv, rv);
- if (mAllowRootChanges) {
- updateFolder = PR_TRUE;
- SetFaviconForFolder(ourID, NS_LITERAL_CSTRING(BOOKMARKS_MENU_ICON_URI));
- }
- break;
- case BookmarkImportFrame::Container_Toolbar:
- // get toolbar folder
- PRInt64 btf;
- rv = mBookmarksService->GetToolbarFolder(&btf);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!btf) {
- // create new folder
- rv = mBookmarksService->CreateFolder(CurFrame().mContainerID,
- containerName,
- mBookmarksService->DEFAULT_INDEX, &ourID);
+ if (mAllowRootChanges) {
+ updateFolder = PR_TRUE;
+ rv = SetFaviconForFolder(ourID, NS_LITERAL_CSTRING(BOOKMARKS_MENU_ICON_URI));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ break;
+ case BookmarkImportFrame::Container_Toolbar:
+ // get toolbar folder
+ PRInt64 bookmarkToolbarFolder;
+ rv = mBookmarksService->GetToolbarFolder(&bookmarkToolbarFolder);
NS_ENSURE_SUCCESS(rv, rv);
- // there's no toolbar folder, so make us the toolbar folder
- rv = mBookmarksService->SetToolbarFolder(ourID);
- NS_ENSURE_SUCCESS(rv, rv);
- // set favicon
- SetFaviconForFolder(ourID, NS_LITERAL_CSTRING(BOOKMARKS_TOOLBAR_ICON_URI));
- }
- else {
- ourID = btf;
- }
- break;
- default:
- NS_NOTREACHED("Unknown container type");
+ if (!bookmarkToolbarFolder) {
+ // create new folder
+ rv = mBookmarksService->CreateFolder(CurFrame().mContainerID,
+ containerName,
+ mBookmarksService->DEFAULT_INDEX, &ourID);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // there's no toolbar folder, so make us the toolbar folder
+ rv = mBookmarksService->SetToolbarFolder(ourID);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // set favicon
+ rv = SetFaviconForFolder(ourID, NS_LITERAL_CSTRING(BOOKMARKS_TOOLBAR_ICON_URI));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ ourID = bookmarkToolbarFolder;
+ }
+ break;
+ default:
+ NS_NOTREACHED("Unknown container type");
+ }
}
#ifdef DEBUG_IMPORT
PrintNesting();
printf("Folder %lld \'%s\'", ourID, NS_ConvertUTF16toUTF8(containerName).get());
#endif
if (updateFolder) {
// move the menu folder to the current position
@@ -1047,16 +1089,29 @@ BookmarkContentSink::SetFaviconForFolder
nsCOMPtr<nsIURI> faviconURI;
rv = NS_NewURI(getter_AddRefs(faviconURI), aFavicon);
NS_ENSURE_SUCCESS(rv, rv);
return faviconService->SetFaviconUrlForPage(folderURI, faviconURI);
}
+// Converts a string id (legacy rdf or contemporary) into an int id
+PRInt64
+BookmarkContentSink::ConvertImportedIdToInternalId(const nsCString& aId) {
+ PRInt64 intId = 0;
+ if (aId.IsEmpty() || nsCRT::strncasecmp("rdf:", aId.get(), 4))
+ return intId;
+ PRInt32 rv;
+ intId = aId.ToInteger(&rv);
+ if (NS_FAILED(rv))
+ intId = 0;
+ return intId;
+}
+
// SyncChannelStatus
//
// If a function returns an error, we need to set the channel status to be
// the same, but only if the channel doesn't have its own error. This returns
// the error code that should be sent to OnStopRequest.
static nsresult
@@ -1198,16 +1253,17 @@ static const char kPlacesRootAttribute[]
static const char kBookmarksRootAttribute[] = " BOOKMARKS_MENU=\"true\"";
static const char kToolbarFolderAttribute[] = " PERSONAL_TOOLBAR_FOLDER=\"true\"";
static const char kIconAttribute[] = " ICON=\"";
static const char kIconURIAttribute[] = " ICON_URI=\"";
static const char kHrefAttribute[] = " HREF=\"";
static const char kFeedURIAttribute[] = " FEEDURL=\"";
static const char kWebPanelAttribute[] = " WEB_PANEL=\"true\"";
static const char kKeywordAttribute[] = " SHORTCUTURL=\"";
+static const char kIdAttribute[] = " ID=\"";
// WriteContainerPrologue
//
// <DL><p>
//
// Goes after the container header (<H3...) but before the contents
static nsresult
@@ -1382,16 +1438,26 @@ nsNavBookmarks::WriteContainerHeader(PRI
} else if (aFolder == mBookmarksRoot) {
rv = aOutput->Write(kBookmarksRootAttribute, sizeof(kBookmarksRootAttribute)-1, &dummy);
if (NS_FAILED(rv)) return rv;
} else if (aFolder == mToolbarFolder) {
rv = aOutput->Write(kToolbarFolderAttribute, sizeof(kToolbarFolderAttribute)-1, &dummy);
if (NS_FAILED(rv)) return rv;
}
+ // id
+ rv = aOutput->Write(kIdAttribute, sizeof(kIdAttribute)-1, &dummy);
+ if (NS_FAILED(rv)) return rv;
+ nsCAutoString id;
+ id.AppendInt(aFolder);
+ rv = aOutput->Write(id.get(), id.Length(), &dummy);
+ if (NS_FAILED(rv)) return rv;
+ rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
+ if (NS_FAILED(rv)) return rv;
+
// favicon (most folders won't have one)
nsCOMPtr<nsIURI> folderURI;
rv = GetFolderURI(aFolder, getter_AddRefs(folderURI));
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString folderSpec;
rv = folderURI->GetSpec(folderSpec);
NS_ENSURE_SUCCESS(rv, rv);
rv = WriteFaviconAttribute(folderSpec, aOutput);
@@ -1471,16 +1537,26 @@ nsNavBookmarks::WriteItem(nsNavHistoryRe
rv = WriteFaviconAttribute(uri, aOutput);
if (NS_FAILED(rv)) return rv;
// get bookmark id
PRInt64 bookmarkId;
rv = aItem->GetBookmarkId(&bookmarkId);
NS_ENSURE_SUCCESS(rv, rv);
+ // write id
+ rv = aOutput->Write(kIdAttribute, sizeof(kIdAttribute)-1, &dummy);
+ if (NS_FAILED(rv)) return rv;
+ nsCAutoString id;
+ id.AppendInt(bookmarkId);
+ rv = aOutput->Write(id.get(), id.Length(), &dummy);
+ if (NS_FAILED(rv)) return rv;
+ rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
+ if (NS_FAILED(rv)) return rv;
+
// keyword (shortcuturl)
nsAutoString keyword;
rv = GetKeywordForBookmark(bookmarkId, keyword);
if (NS_FAILED(rv)) return rv;
if (!keyword.IsEmpty()) {
rv = aOutput->Write(kKeywordAttribute, sizeof(kKeywordAttribute)-1, &dummy);
if (NS_FAILED(rv)) return rv;
char* escapedKeyword = nsEscapeHTML(NS_ConvertUTF16toUTF8(keyword).get());
@@ -1585,16 +1661,26 @@ nsNavBookmarks::WriteLivemark(PRInt64 aF
rv = aOutput->Write(kHrefAttribute, sizeof(kHrefAttribute)-1, &dummy);
if (NS_FAILED(rv)) return rv;
rv = WriteEscapedUrl(siteSpec, aOutput);
if (NS_FAILED(rv)) return rv;
rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
if (NS_FAILED(rv)) return rv;
}
+ // write id
+ rv = aOutput->Write(kIdAttribute, sizeof(kIdAttribute)-1, &dummy);
+ if (NS_FAILED(rv)) return rv;
+ nsCAutoString id;
+ id.AppendInt(aFolderId);
+ rv = aOutput->Write(id.get(), id.Length(), &dummy);
+ if (NS_FAILED(rv)) return rv;
+ rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
+ if (NS_FAILED(rv)) return rv;
+
// '>'
rv = aOutput->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy);
if (NS_FAILED(rv)) return rv;
// title
rv = WriteContainerTitle(aFolderId, aOutput);
if (NS_FAILED(rv)) return rv;