зеркало из https://github.com/mozilla/gecko-dev.git
Added bookmarks.html importing, automatic places root init (bug 318057) r=bryner
GetChildFolder on bug 314553, r=bryner
This commit is contained in:
Родитель
bf8762c86d
Коммит
e68dcf4384
|
@ -19,3 +19,4 @@ classic.jar:
|
|||
en-US.jar:
|
||||
locale/browser/places/places.dtd (locale/places.dtd)
|
||||
locale/browser/places/places.properties (locale/places.properties)
|
||||
locale/browser/places/default_places.html (locale/default_places.html)
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE NETSCAPE-Bookmark-file-1>
|
||||
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
|
||||
<TITLE>Places</TITLE>
|
||||
<H1 PLACES_ROOT="true">Places</H1>
|
||||
<DL><p>
|
||||
<DT><A HREF="place:beginTime=0&beginTimeRef=1&endTime=0&endTimeRef=2&sort=4&type=1">Viewed today</A>
|
||||
<DD>Show web pages visited today
|
||||
<DT><A HREF="place:beginTime=-518400000000&beginTimeRef=1&endTime=0&endTimeRef=2&group=2&sort=4&type=1">Viewed past week</A>
|
||||
<DD>Show web pages visited in the past 7 days
|
||||
<DT><A HREF="place:group=2&sort=1&type=1">All pages viewed</A>
|
||||
<DD>Show all web pages visited
|
||||
<DT><H3 PERSONAL_TOOLBAR_FOLDER="true">Bookmarks Toolbar</H3>
|
||||
<DD>Add bookmarks to this folder to see them displayed on the Bookmarks Toolbar
|
||||
<DL><p>
|
||||
</DL><p>
|
||||
<DT><H3 BOOKMARKS_MENU="true">Bookmarks Menu</H3>
|
||||
<DL><p>
|
||||
<DT><H3>Firefox and Mozilla Links</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="http://start.mozilla.org/firefox/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" LAST_CHARSET="ISO-8859-1">Firefox Start Page</A>
|
||||
<DT><A HREF="http://www.mozilla.org/products/firefox/central.html" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" LAST_CHARSET="ISO-8859-1">Firefox Central</A>
|
||||
<DT><A HREF="http://addons.mozilla.org/?application=firefox" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" LAST_CHARSET="UTF-8">Themes and Extensions</A>
|
||||
<DT><A HREF="http://getfirefox.com/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" LAST_CHARSET="ISO-8859-1">Firefox Product Page</A>
|
||||
<DT><A HREF="http://www.mozilla.org/" LAST_VISIT="1133213523" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" LAST_CHARSET="UTF-8">The Mozilla web site</A>
|
||||
<DT><A HREF="http://www.mozillazine.org/" ICON="data:image/x-icon;base64,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" LAST_CHARSET="ISO-8859-1">MozillaZine</A>
|
||||
<DT><A HREF="http://www.mozillastore.com/" ICON="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A1NTU/9TU1P/U1NT/1NTU/9TU1P/U1NT/1NTU/9TU1P/U1NT/////AP///wD///8A////AP///wD///8Ag4OD/4ODg/+Dg4P/g4OD/4ODg/+Dg4P/g4OD/4ODg/+Dg4P/1NTU/9TU1P////8A////AP///wD///8Ag4OD/83Nzf/09PT/8/Pz//Pz8//w8PD/7e3t/+np6f/n5+f/wcHB/4ODg//U1NT/////AP///wD///8A////AIODg//19fX/9fX1//T09P/09PT/8vLy/+7u7v/q6ur/6Ojo/+Tk5P+Dg4P/1NTU/////wD///8A////AP///wCDg4P/9vb2//b29v/19fX/9fX1//T09P/w8PD/7Ozs/+np6f/l5eX/g4OD/9TU1P////8A////AP///wD///8Ag4OD//f39//39/f/9vb2//b29v/19fX/8vLy/+3t7f/p6en/5ubm/4ODg//U1NT/////AP///wD///8A////AIODg//4+Pj/+Pj4/2t2x//39/f/T1y+//Pz8/92f8n/3d7m/+fn5/+Dg4P/1NTU/////wD///8A////ANTU1P+Dg4P/+vr6//r6+v8YKav/srjh/xgpq/+wtd7/GCmr/+3t7f/o6Oj/g4OD/9TU1P/U1NT/1NTU/4ODg/+Dg4P/g4OD//v7+//6+vr/QlC6/2x3yP+JkdL/a3XH/0JQuv/u7u7/6urq/4ODg/+Dg4P/g4OD/9TU1P+Dg4P//Pz8/6urq//8/Pz/+/v7/1Bdv/80Q7X/7e72/zRDtf9QXb//7+/v/+vr6/+kpKT/5+fn/4ODg//U1NT/g4OD//z8/P/S0tL//Pz8//z8/P9QXb//X2rE//z8/P9fasT/UF2///Hx8f/t7e3/xcXF/+jo6P+Dg4P/1NTU/4ODg//9/f3//f39//39/f/9/f3//f39//39/f/9/f3/+vr6//f39//z8/P/7u7u/+vr6//p6en/g4OD/9TU1P+Dg4P//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//z8/P/4+Pj/9PT0//Dw8P/t7e3/6+vr/4ODg/////8A////AIODg/////////////////+Dg4P/g4OD/4ODg/+Dg4P/g4OD//b29v/y8vL/7+/v/4ODg/////8A////AP///wD///8Ag4OD/4ODg/+Dg4P/////AP///wD///8A////AP///wCDg4P/g4OD/4ODg/////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A8AcAAOADAADAAwAAwAMAAMADAADAAwAAwAMAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAIADAADHxwAA//8AAA==" LAST_CHARSET="ISO-8859-1">Mozilla Store</A>
|
||||
<DT><A HREF="http://www.spreadfirefox.com/" LAST_CHARSET="UTF-8">Get Involved - Help spread Firefox!</A>
|
||||
</DL><p>
|
||||
</DL><p>
|
||||
</DL><p>
|
|
@ -236,6 +236,15 @@ interface nsINavBookmarksService : nsISupports
|
|||
*/
|
||||
void moveFolder(in PRInt64 folder, in PRInt64 newParent, in PRInt32 index);
|
||||
|
||||
/**
|
||||
* Returns the ID of a child folder with the given name. This does not
|
||||
* recurse, you have to give it an immediate sibling of the given folder.
|
||||
* If the given subfolder doesn't exist, it will return 0.
|
||||
* @param folder Parent folder whose children we will search
|
||||
* @param subFolder Name of the folder to search for in folder
|
||||
*/
|
||||
PRInt64 getChildFolder(in PRInt64 folder, in AString subFolder);
|
||||
|
||||
/**
|
||||
* Set the history/bookmark title for a URI. The new title will be used
|
||||
* anywhere the URI is shown in bookmarks or history.
|
||||
|
@ -299,4 +308,16 @@ interface nsINavBookmarksService : nsISupports
|
|||
* done changing. Should match beginUpdateBatch or bad things will happen.
|
||||
*/
|
||||
void endUpdateBatch();
|
||||
|
||||
|
||||
/**
|
||||
* Loads the given bookmarks.html file and merges it with the current
|
||||
* bookmarks hierarchy.
|
||||
*/
|
||||
void importBookmarksHTML(in nsIURI url);
|
||||
|
||||
/**
|
||||
* Saves the current bookmarks hierarchy to a bookmarks.html file.
|
||||
*/
|
||||
void exportBookmarksHTML(in nsIURI url);
|
||||
};
|
||||
|
|
|
@ -64,6 +64,8 @@ REQUIRES = xpcom \
|
|||
autocomplete \
|
||||
storage \
|
||||
uconv \
|
||||
htmlparser \
|
||||
content \
|
||||
txmgr \
|
||||
$(NULL)
|
||||
|
||||
|
@ -72,6 +74,7 @@ LOCAL_INCLUDES = -I$(srcdir)/../../build
|
|||
CPPSRCS = \
|
||||
nsAnnoProtocolHandler.cpp \
|
||||
nsAnnotationService.cpp \
|
||||
nsBookmarksHTML.cpp \
|
||||
nsNavHistory.cpp \
|
||||
nsNavHistoryQuery.cpp \
|
||||
nsNavHistoryResult.cpp \
|
||||
|
|
|
@ -0,0 +1,735 @@
|
|||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla History System
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Google Inc.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2005
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Brett Wilson <brettw@gmail.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
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/**
|
||||
* Importer/exporter between the mozStorage-based bookmarks and the old-style
|
||||
* "bookmarks.html"
|
||||
*
|
||||
* Format:
|
||||
*
|
||||
* Primary heading := h1
|
||||
* Old version used this to set attributes on the bookmarks RDF root, such
|
||||
* as the last modified date. We only use H1 to check for the attribute
|
||||
* PLACES_ROOT, which tells us that this hierarchy root is the places root.
|
||||
* For backwards compatability, if we don't find this, we assume that the
|
||||
* hierarchy is rooted at the bookmarks menu.
|
||||
* Heading := any heading other than h1
|
||||
* Old version used this to set attributes on the current container. We only
|
||||
* care about the content of the heading container, which contains the title
|
||||
* of the bookmark container.
|
||||
* Bookmark := a
|
||||
* HREF is the destination of the bookmark
|
||||
* LAST_CHARSET should be stored as an annotation (FIXME TODO) so that the
|
||||
* next time we go to that page we remember the user's preference.
|
||||
* ICON should be stored in the annotation service (FIXME TODO)
|
||||
* Text of the <a> container is the name of the bookmark
|
||||
* Ignored: ADD_DATE, LAST_VISIT, LAST_MODIFIED, ID
|
||||
* Bookmark comment := dd
|
||||
* This affects the previosly added bookmark
|
||||
* Separator := hr
|
||||
* Insert a separator into the current container
|
||||
* The folder hierarchy is defined by <dl>/<ul>/<menu> (the old importing code
|
||||
* handles all these cases, when we write, use <dl>).
|
||||
*/
|
||||
|
||||
#include "nsBrowserCompsCID.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIAnnotationService.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIHTMLContentSink.h"
|
||||
#include "nsNavBookmarks.h"
|
||||
#include "nsIParser.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsNavHistory.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsParserCIID.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "mozStorageHelper.h"
|
||||
|
||||
static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
|
||||
|
||||
#define KEY_TOOLBARFOLDER_LOWER "personal_toolbar_folder"
|
||||
#define KEY_BOOKMARKSMENU_LOWER "bookmarks_menu"
|
||||
#define KEY_PLACESROOT_LOWER "places_root"
|
||||
#define KEY_HREF_LOWER "href"
|
||||
#define KEY_LASTCHARSET_LOWER "last_charset"
|
||||
#define KEY_ICON_LOWER "icon"
|
||||
|
||||
static const char kWhitespace[] = " \r\n\t\b";
|
||||
|
||||
class BookmarkImportFrame
|
||||
{
|
||||
public:
|
||||
BookmarkImportFrame(PRInt64 aID) :
|
||||
mContainerID(aID),
|
||||
mContainerNesting(0),
|
||||
mLastContainerType(Container_Normal),
|
||||
mInDescription(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
enum ContainerType { Container_Normal,
|
||||
Container_Menu,
|
||||
Container_Toolbar };
|
||||
|
||||
PRInt64 mContainerID;
|
||||
|
||||
// How many <dl>s have been nested. Each frame/container should start
|
||||
// with a heading, and is then followed by a <dl>, <ul>, or <menu>. When
|
||||
// that list is complete, then it is the end of this container and we need
|
||||
// to pop back up one level for new items. If we never get an open tag for
|
||||
// one of these things, we should assume that the container is empty and
|
||||
// that things we find should be siblings of it. Normally, these <dl>s won't
|
||||
// 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;
|
||||
|
||||
// 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>
|
||||
// because the tag is never explicitly closed. If this is true and we see a
|
||||
// new open tag, that means to commit the description to the previous
|
||||
// bookmark.
|
||||
//
|
||||
// Additional weirdness happens when the previous <dt> tag contains a <h3>:
|
||||
// this means there is a new folder with the given description, and whose
|
||||
// children are contained in the following <dl> list.
|
||||
//
|
||||
// This is handled in OpenContainer(), which commits previous text if
|
||||
// necessary.
|
||||
PRBool mInDescription;
|
||||
|
||||
// contains the URL of the previous bookmark created. This is used so that
|
||||
// when we encounter a <dd>, we know what bookmark to associate the text with.
|
||||
// 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;
|
||||
|
||||
|
||||
void ConsumeHeading(nsAString* aHeading, ContainerType* aContainerType)
|
||||
{
|
||||
*aHeading = mPreviousText;
|
||||
*aContainerType = mLastContainerType;
|
||||
mPreviousText.Truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The content sink stuff is based loosely on
|
||||
*/
|
||||
class BookmarkContentSink : public nsIHTMLContentSink
|
||||
{
|
||||
public:
|
||||
nsresult Init(PRBool aAllowRootChanges,
|
||||
nsINavBookmarksService* bookmarkService);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIContentSink (superclass of nsIHTMLContentSink)
|
||||
NS_IMETHOD WillBuildModel() { return NS_OK; }
|
||||
NS_IMETHOD DidBuildModel() { return NS_OK; }
|
||||
NS_IMETHOD WillInterrupt() { return NS_OK; }
|
||||
NS_IMETHOD WillResume() { return NS_OK; }
|
||||
NS_IMETHOD SetParser(nsIParser* aParser) { return NS_OK; }
|
||||
virtual void FlushPendingNotifications(mozFlushType aType) { }
|
||||
NS_IMETHOD SetDocumentCharset(nsACString& aCharset) { return NS_OK; }
|
||||
virtual nsISupports *GetTarget() { return nsnull; }
|
||||
|
||||
// nsIHTMLContentSink
|
||||
NS_IMETHOD OpenHead() { return NS_OK; }
|
||||
NS_IMETHOD BeginContext(PRInt32 aPosition) { return NS_OK; }
|
||||
NS_IMETHOD EndContext(PRInt32 aPosition) { return NS_OK; }
|
||||
NS_IMETHOD IsEnabled(PRInt32 aTag, PRBool* aReturn)
|
||||
{ *aReturn = PR_TRUE; return NS_OK; }
|
||||
NS_IMETHOD WillProcessTokens() { return NS_OK; }
|
||||
NS_IMETHOD DidProcessTokens() { return NS_OK; }
|
||||
NS_IMETHOD WillProcessAToken() { return NS_OK; }
|
||||
NS_IMETHOD DidProcessAToken() { return NS_OK; }
|
||||
NS_IMETHOD OpenContainer(const nsIParserNode& aNode);
|
||||
NS_IMETHOD CloseContainer(const nsHTMLTag aTag);
|
||||
NS_IMETHOD AddLeaf(const nsIParserNode& aNode);
|
||||
NS_IMETHOD AddComment(const nsIParserNode& aNode) { return NS_OK; }
|
||||
NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode) { return NS_OK; }
|
||||
NS_IMETHOD AddDocTypeDecl(const nsIParserNode& aNode) { return NS_OK; }
|
||||
NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode) { return NS_OK; }
|
||||
NS_IMETHOD_(PRBool) IsFormOnStack() { return PR_FALSE; }
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsINavBookmarksService> mBookmarksService;
|
||||
nsCOMPtr<nsINavHistory> mHistoryService;
|
||||
nsCOMPtr<nsIAnnotationService> mAnnotationService;
|
||||
|
||||
// if set, we will move root items to where we find them. This should be
|
||||
// set when we are loading the default places html file, and should be
|
||||
// unset when doing normal imports so that, for example, the toolbar folder
|
||||
// will be a child of the menu in old bookmarks.html, and we don't want
|
||||
// to reparent it on import.
|
||||
PRBool mAllowRootChanges;
|
||||
|
||||
void HandleContainerBegin(const nsIParserNode& node);
|
||||
void HandleContainerEnd();
|
||||
void HandleHead1Begin(const nsIParserNode& node);
|
||||
void HandleHeadBegin(const nsIParserNode& node);
|
||||
void HandleHeadEnd();
|
||||
void HandleLinkBegin(const nsIParserNode& node);
|
||||
void HandleLinkEnd();
|
||||
|
||||
// This is a list of frames. We really want a recursive parser, but the HTML
|
||||
// parser gives us tags as a stream. This implements all the state on a stack
|
||||
// so we can get the recursive information we need. Use "CurFrame" to get the
|
||||
// top "stack frame" with the current state in it.
|
||||
nsTArray<BookmarkImportFrame> mFrames;
|
||||
BookmarkImportFrame& CurFrame()
|
||||
{
|
||||
NS_ASSERTION(mFrames.Length() > 0, "Asking for frame when there are none!");
|
||||
return mFrames[mFrames.Length() - 1];
|
||||
}
|
||||
nsresult NewFrame();
|
||||
nsresult PopFrame();
|
||||
};
|
||||
|
||||
|
||||
// BookmarkContentSink::Init
|
||||
//
|
||||
// Note that the bookmark service pointer is passed in. We can not create
|
||||
// the bookmark service from here because this can be called from bookmark
|
||||
// service creation, making a weird reentrant loop.
|
||||
|
||||
nsresult
|
||||
BookmarkContentSink::Init(PRBool aAllowRootChanges,
|
||||
nsINavBookmarksService* bookmarkService)
|
||||
{
|
||||
nsresult rv;
|
||||
mBookmarksService = bookmarkService;
|
||||
mHistoryService = do_GetService(NS_NAVHISTORY_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mAnnotationService = do_GetService(NS_ANNOTATIONSERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mAllowRootChanges = aAllowRootChanges;
|
||||
|
||||
// initialize the root frame with the menu root
|
||||
PRInt64 menuRoot;
|
||||
rv = mBookmarksService->GetBookmarksRoot(&menuRoot);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (! mFrames.AppendElement(BookmarkImportFrame(menuRoot)))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMPL_ISUPPORTS2(BookmarkContentSink,
|
||||
nsIContentSink,
|
||||
nsIHTMLContentSink);
|
||||
|
||||
// nsIContentSink **************************************************************
|
||||
|
||||
NS_IMETHODIMP
|
||||
BookmarkContentSink::OpenContainer(const nsIParserNode& aNode)
|
||||
{
|
||||
// see the comment for the definition of mInDescription. Basically, we commit
|
||||
// any text in mPreviousText to the description of the node/folder if there
|
||||
// is any.
|
||||
BookmarkImportFrame& frame = CurFrame();
|
||||
if (frame.mInDescription) {
|
||||
frame.mPreviousText.Trim(kWhitespace); // important!
|
||||
if (! frame.mPreviousText.IsEmpty()) {
|
||||
// FIXME: This description should be stored as an annotation on the URL
|
||||
// or folder. We should probably not overwrite existing annotations.
|
||||
frame.mPreviousText.Truncate(0);
|
||||
}
|
||||
frame.mInDescription = PR_FALSE;
|
||||
}
|
||||
|
||||
switch(aNode.GetNodeType()) {
|
||||
case eHTMLTag_h1:
|
||||
HandleHead1Begin(aNode);
|
||||
break;
|
||||
case eHTMLTag_h2:
|
||||
case eHTMLTag_h3:
|
||||
case eHTMLTag_h4:
|
||||
case eHTMLTag_h5:
|
||||
case eHTMLTag_h6:
|
||||
HandleHeadBegin(aNode);
|
||||
break;
|
||||
case eHTMLTag_a:
|
||||
HandleLinkBegin(aNode);
|
||||
break;
|
||||
case eHTMLTag_dl:
|
||||
case eHTMLTag_ul:
|
||||
case eHTMLTag_menu:
|
||||
HandleContainerBegin(aNode);
|
||||
break;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
BookmarkContentSink::CloseContainer(const nsHTMLTag aTag)
|
||||
{
|
||||
switch (aTag) {
|
||||
case eHTMLTag_dl:
|
||||
case eHTMLTag_ul:
|
||||
case eHTMLTag_menu:
|
||||
HandleContainerEnd();
|
||||
break;
|
||||
case eHTMLTag_dt:
|
||||
break;
|
||||
case eHTMLTag_h1:
|
||||
// ignore
|
||||
break;
|
||||
case eHTMLTag_h2:
|
||||
case eHTMLTag_h3:
|
||||
case eHTMLTag_h4:
|
||||
case eHTMLTag_h5:
|
||||
case eHTMLTag_h6:
|
||||
HandleHeadEnd();
|
||||
break;
|
||||
case eHTMLTag_a:
|
||||
HandleLinkEnd();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
BookmarkContentSink::AddLeaf(const nsIParserNode& aNode)
|
||||
{
|
||||
// save any text we find
|
||||
if (aNode.GetNodeType() == eHTMLTag_text) {
|
||||
CurFrame().mPreviousText += aNode.GetText();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// BookmarkContentSink::HandleContainerBegin
|
||||
|
||||
void
|
||||
BookmarkContentSink::HandleContainerBegin(const nsIParserNode& node)
|
||||
{
|
||||
CurFrame().mContainerNesting ++;
|
||||
}
|
||||
|
||||
|
||||
// BookmarkContentSink::HandleContainerEnd
|
||||
//
|
||||
// Our "indent" count has decreased, and when we hit 0 that means that this
|
||||
// container is complete and we need to pop back to the outer frame. Never
|
||||
// pop the toplevel frame
|
||||
|
||||
void
|
||||
BookmarkContentSink::HandleContainerEnd()
|
||||
{
|
||||
BookmarkImportFrame& frame = CurFrame();
|
||||
if (frame.mContainerNesting > 0)
|
||||
frame.mContainerNesting --;
|
||||
if (mFrames.Length() > 1 && frame.mContainerNesting == 0)
|
||||
PopFrame();
|
||||
}
|
||||
|
||||
|
||||
// BookmarkContentSink::HandleHead1Begin
|
||||
//
|
||||
// Handles <H1>. We check for the attribute PLACES_ROOT and reset the
|
||||
// container id if it's found. Otherwise, the default bookmark menu
|
||||
// root is assumed and imported things will go into the bookmarks menu.
|
||||
|
||||
void
|
||||
BookmarkContentSink::HandleHead1Begin(const nsIParserNode& node)
|
||||
{
|
||||
PRInt32 attrCount = node.GetAttributeCount();
|
||||
for (PRInt32 i = 0; i < attrCount; i ++) {
|
||||
if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_PLACESROOT_LOWER)) {
|
||||
if (mFrames.Length() > 1) {
|
||||
NS_WARNING("Trying to set the places root from the middle of the hierarchy. "
|
||||
"This can only be set at the beginning.");
|
||||
return;
|
||||
}
|
||||
PRInt64 mPlacesRoot;
|
||||
mBookmarksService->GetPlacesRoot(&mPlacesRoot);
|
||||
CurFrame().mContainerID = mPlacesRoot;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// BookmarkContentSink::HandleHeadBegin
|
||||
//
|
||||
// Called for h2,h3,h4,h5,h6. This just stores the correct information in
|
||||
// the current frame; the actual new frame corresponding to the container
|
||||
// associated with the heading will be created when the tag has been closed
|
||||
// and we know the title (we don't know to create a new folder or to merge
|
||||
// with an existing one until we have the title).
|
||||
|
||||
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;
|
||||
|
||||
// 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>
|
||||
// <h3>BAR</h3>
|
||||
// <dl>...content 1...</dl>
|
||||
// <dl>...content 2...</dl>
|
||||
// we'll pop the stack when we find the h3 for BAR, treating that as an
|
||||
// implicit ending of the FOO container. The output will be FOO and BAR as
|
||||
// siblings. If there's another <dl> following (as in "content 2"), those
|
||||
// items will be treated as further siblings of FOO and BAR
|
||||
if (frame.mContainerNesting == 0)
|
||||
PopFrame();
|
||||
|
||||
// We have to check for some attributes to see if this is a "special"
|
||||
// folder, which will have different creation rules when the end tag is
|
||||
// processed.
|
||||
PRInt32 attrCount = node.GetAttributeCount();
|
||||
frame.mLastContainerType = BookmarkImportFrame::Container_Normal;
|
||||
for (PRInt32 i = 0; i < attrCount; i ++) {
|
||||
if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_TOOLBARFOLDER_LOWER)) {
|
||||
frame.mLastContainerType = BookmarkImportFrame::Container_Toolbar;
|
||||
break;
|
||||
} else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_BOOKMARKSMENU_LOWER)) {
|
||||
frame.mLastContainerType = BookmarkImportFrame::Container_Menu;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CurFrame().mPreviousText.Truncate(0);
|
||||
}
|
||||
|
||||
|
||||
// BookmarkContentSink::HandleHeadEnd
|
||||
//
|
||||
// Creates the new frame for this heading now that we know the name of the
|
||||
// container (tokens since the heading open tag will have been placed in
|
||||
// mPreviousText).
|
||||
|
||||
void
|
||||
BookmarkContentSink::HandleHeadEnd()
|
||||
{
|
||||
NewFrame();
|
||||
}
|
||||
|
||||
|
||||
// BookmarkContentSink::HandleLinkBegin
|
||||
//
|
||||
// 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)
|
||||
{
|
||||
BookmarkImportFrame& frame = CurFrame();
|
||||
|
||||
// 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 icon;
|
||||
nsAutoString lastCharset;
|
||||
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_ICON_LOWER)) {
|
||||
icon = node.GetValueAt(i);
|
||||
} else if (key.LowerCaseEqualsLiteral(KEY_LASTCHARSET_LOWER)) {
|
||||
lastCharset = node.GetValueAt(i);
|
||||
}
|
||||
}
|
||||
href.Trim(kWhitespace);
|
||||
icon.Trim(kWhitespace);
|
||||
lastCharset.Trim(kWhitespace);
|
||||
|
||||
// ignore <a> tags that have no href: we don't know what to do with them
|
||||
if (href.IsEmpty()) {
|
||||
frame.mPreviousLink = nsnull;
|
||||
return;
|
||||
}
|
||||
|
||||
// save this so the link text and descriptions can be associated with it
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(frame.mPreviousLink),
|
||||
NS_ConvertUTF16toUTF8(href), nsnull);
|
||||
if (NS_FAILED(rv)) {
|
||||
frame.mPreviousLink = nsnull;
|
||||
return; // invalid link
|
||||
}
|
||||
|
||||
mBookmarksService->InsertItem(frame.mContainerID, frame.mPreviousLink, -1);
|
||||
|
||||
// FIXME: save the favicon
|
||||
// FIXME: save the last charset
|
||||
}
|
||||
|
||||
|
||||
// BookmarkContentSink::HandleLinkEnd
|
||||
//
|
||||
// Saves the title for the given bookmark. This will overwrite an existing
|
||||
// title in the history, which may not be the right thing since we'd like to
|
||||
// keep the original title in there if we already had it, and only save the
|
||||
// custom title as an annotation. Doing this shouldn't affect anything,
|
||||
// however.
|
||||
|
||||
void
|
||||
BookmarkContentSink::HandleLinkEnd()
|
||||
{
|
||||
BookmarkImportFrame& frame = CurFrame();
|
||||
frame.mPreviousText.Trim(kWhitespace);
|
||||
if (! frame.mPreviousText.IsEmpty() && frame.mPreviousLink) {
|
||||
nsCOMPtr<nsIGlobalHistory2> globalHistory =
|
||||
do_QueryInterface(mHistoryService);
|
||||
globalHistory->SetPageTitle(frame.mPreviousLink, frame.mPreviousText);
|
||||
mAnnotationService->SetAnnotationString(frame.mPreviousLink,
|
||||
nsCString(nsNavHistory::kAnnotationTitle), frame.mPreviousText, 0, 0);
|
||||
}
|
||||
frame.mPreviousText.Truncate(0);
|
||||
}
|
||||
|
||||
|
||||
// BookmarkContentSink::NewFrame
|
||||
//
|
||||
// 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;
|
||||
|
||||
nsString containerName;
|
||||
BookmarkImportFrame::ContainerType containerType;
|
||||
CurFrame().ConsumeHeading(&containerName, &containerType);
|
||||
|
||||
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, -1, &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;
|
||||
break;
|
||||
case BookmarkImportFrame::Container_Toolbar:
|
||||
// toolbar root
|
||||
rv = mBookmarksService->GetToolbarRoot(&ourID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (mAllowRootChanges)
|
||||
updateFolder = PR_TRUE;
|
||||
break;
|
||||
default:
|
||||
NS_NOTREACHED("Unknown container type");
|
||||
}
|
||||
|
||||
if (updateFolder) {
|
||||
// move the menu/toolbar folder to the current position
|
||||
mBookmarksService->MoveFolder(ourID, CurFrame().mContainerID, -1);
|
||||
mBookmarksService->SetFolderTitle(ourID, containerName);
|
||||
}
|
||||
|
||||
if (! mFrames.AppendElement(BookmarkImportFrame(ourID)))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// BookmarkContentSink::PopFrame
|
||||
//
|
||||
|
||||
nsresult
|
||||
BookmarkContentSink::PopFrame()
|
||||
{
|
||||
// we must always have one frame
|
||||
if (mFrames.Length() <= 1) {
|
||||
NS_NOTREACHED("Trying to complete more bookmark folders than you started");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mFrames.RemoveElementAt(mFrames.Length() - 1);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
SyncChannelStatus(nsIChannel* channel, nsresult status)
|
||||
{
|
||||
nsresult channelStatus;
|
||||
channel->GetStatus(&channelStatus);
|
||||
if (NS_FAILED(channelStatus))
|
||||
return channelStatus;
|
||||
|
||||
if (NS_SUCCEEDED(status))
|
||||
return NS_OK; // caller and the channel are happy
|
||||
|
||||
// channel was OK, but caller wasn't: set the channel state
|
||||
channel->Cancel(status);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
// nsNavBookmarks::ImportBookmarksHTML
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavBookmarks::ImportBookmarksHTML(nsIURI* aURL)
|
||||
{
|
||||
// this version is exposed on the interface and disallows changing of roots
|
||||
return ImportBookmarksHTMLInternal(aURL, PR_FALSE);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsNavBookmarks::ImportBookmarksHTMLInternal(nsIURI* aURL,
|
||||
PRBool aAllowRootChanges)
|
||||
{
|
||||
// wrap the import in a transaction to make it faster
|
||||
mozStorageTransaction transaction(DBConn(), PR_FALSE);
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
BookmarkContentSink* sink = new BookmarkContentSink;
|
||||
NS_ENSURE_TRUE(sink, NS_ERROR_OUT_OF_MEMORY);
|
||||
rv = sink->Init(aAllowRootChanges, this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
parser->SetContentSink(sink);
|
||||
|
||||
// channel: note we have to set the content type or the default "unknown" type
|
||||
// will confuse the parser
|
||||
nsCOMPtr<nsIIOService> ioservice = do_GetIOService(&rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = ioservice->NewChannelFromURI(aURL, getter_AddRefs(channel));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = channel->SetContentType(NS_LITERAL_CSTRING("text/html"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// streams
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
rv = channel->Open(getter_AddRefs(stream));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIInputStream> bufferedstream;
|
||||
rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedstream), stream, 4096);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// init parser
|
||||
rv = parser->Parse(aURL, nsnull, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// feed the parser the data
|
||||
// Note: on error, we always need to set the channel's status to be the
|
||||
// same, and to always call OnStopRequest with the channel error.
|
||||
nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = listener->OnStartRequest(channel, nsnull);
|
||||
rv = SyncChannelStatus(channel, rv);
|
||||
while(NS_SUCCEEDED(rv))
|
||||
{
|
||||
PRUint32 available;
|
||||
rv = bufferedstream->Available(&available);
|
||||
if (rv == NS_BASE_STREAM_CLOSED) {
|
||||
rv = NS_OK;
|
||||
available = 0;
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
channel->Cancel(rv);
|
||||
break;
|
||||
}
|
||||
if (! available)
|
||||
break; // blocking input stream has none available when done
|
||||
|
||||
rv = listener->OnDataAvailable(channel, nsnull, bufferedstream, 0, available);
|
||||
rv = SyncChannelStatus(channel, rv);
|
||||
if (NS_FAILED(rv))
|
||||
break;
|
||||
}
|
||||
listener->OnStopRequest(channel, nsnull, rv);
|
||||
transaction.Commit();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavBookmarks::ExportBookmarksHTML(nsIURI* aURL)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
|
@ -36,6 +36,7 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsNavBookmarks.h"
|
||||
#include "nsNavHistory.h"
|
||||
#include "mozStorageHelper.h"
|
||||
|
@ -92,25 +93,31 @@ nsNavBookmarks::Init()
|
|||
mozStorageTransaction transaction(dbConn, PR_FALSE);
|
||||
|
||||
PRBool exists = PR_FALSE;
|
||||
dbConn->TableExists(NS_LITERAL_CSTRING("moz_bookmarks_assoc"), &exists);
|
||||
dbConn->TableExists(NS_LITERAL_CSTRING("moz_bookmarks"), &exists);
|
||||
if (!exists) {
|
||||
rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_bookmarks_assoc (item_child INTEGER, folder_child INTEGER, parent INTEGER, position INTEGER)"));
|
||||
rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_bookmarks (item_child INTEGER, folder_child INTEGER, parent INTEGER, position INTEGER)"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
dbConn->TableExists(NS_LITERAL_CSTRING("moz_bookmarks_containers"), &exists);
|
||||
dbConn->TableExists(NS_LITERAL_CSTRING("moz_bookmarks_folders"), &exists);
|
||||
if (!exists) {
|
||||
rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_bookmarks_containers (id INTEGER PRIMARY KEY, name LONGVARCHAR)"));
|
||||
rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_bookmarks_folders (id INTEGER PRIMARY KEY, name LONGVARCHAR)"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT id, name FROM moz_bookmarks_containers WHERE id = ?1"),
|
||||
dbConn->TableExists(NS_LITERAL_CSTRING("moz_bookmarks_roots"), &exists);
|
||||
if (!exists) {
|
||||
rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_bookmarks_roots (root_name VARCHAR(16) UNIQUE, folder_id INTEGER)"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT id, name FROM moz_bookmarks_folders WHERE id = ?1"),
|
||||
getter_AddRefs(mDBGetFolderInfo));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
{
|
||||
nsCOMPtr<mozIStorageStatement> statement;
|
||||
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT folder_child FROM moz_bookmarks_assoc WHERE parent IS NULL"),
|
||||
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT folder_child FROM moz_bookmarks WHERE parent IS NULL"),
|
||||
getter_AddRefs(statement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -132,88 +139,24 @@ nsNavBookmarks::Init()
|
|||
getter_AddRefs(mBundle));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (mRoot) {
|
||||
// Locate the bookmarks and toolbar folders
|
||||
buffer.AssignLiteral("SELECT folder_child FROM moz_bookmarks_assoc a JOIN moz_bookmarks_containers c ON a.folder_child = c.id WHERE c.name = ?1 AND a.parent = ");
|
||||
buffer.AppendInt(mRoot);
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> locateChild;
|
||||
rv = dbConn->CreateStatement(buffer, getter_AddRefs(locateChild));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsXPIDLString bookmarksMenuName;
|
||||
mBundle->GetStringFromName(NS_LITERAL_STRING("bookmarksMenuName").get(),
|
||||
getter_Copies(bookmarksMenuName));
|
||||
rv = locateChild->BindStringParameter(0, bookmarksMenuName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
PRBool results;
|
||||
rv = locateChild->ExecuteStep(&results);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (results) {
|
||||
mBookmarksRoot = locateChild->AsInt64(0);
|
||||
}
|
||||
rv = locateChild->Reset();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsXPIDLString bookmarksToolbarName;
|
||||
mBundle->GetStringFromName(NS_LITERAL_STRING("bookmarksToolbarName").get(),
|
||||
getter_Copies(bookmarksToolbarName));
|
||||
rv = locateChild->BindStringParameter(0, bookmarksToolbarName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = locateChild->ExecuteStep(&results);
|
||||
if (results) {
|
||||
mToolbarRoot = locateChild->AsInt64(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mRoot) {
|
||||
// Create the root Places container
|
||||
rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks_containers (name) VALUES (NULL)"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = dbConn->GetLastInsertRowID(&mRoot);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ASSERTION(mRoot != 0, "row id must be non-zero!");
|
||||
|
||||
buffer.AssignLiteral("INSERT INTO moz_bookmarks_assoc (folder_child) VALUES(");
|
||||
buffer.AppendInt(mRoot);
|
||||
buffer.AppendLiteral(")");
|
||||
|
||||
rv = dbConn->ExecuteSimpleSQL(buffer);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
if (!mBookmarksRoot) {
|
||||
nsXPIDLString bookmarksMenuName;
|
||||
mBundle->GetStringFromName(NS_LITERAL_STRING("bookmarksMenuName").get(),
|
||||
getter_Copies(bookmarksMenuName));
|
||||
CreateFolder(mRoot, bookmarksMenuName, 0, &mBookmarksRoot);
|
||||
NS_ASSERTION(mBookmarksRoot != 0, "row id must be non-zero!");
|
||||
}
|
||||
if (!mToolbarRoot) {
|
||||
nsXPIDLString bookmarksToolbarName;
|
||||
mBundle->GetStringFromName(NS_LITERAL_STRING("bookmarksToolbarName").get(),
|
||||
getter_Copies(bookmarksToolbarName));
|
||||
CreateFolder(mRoot, bookmarksToolbarName, 1, &mToolbarRoot);
|
||||
NS_ASSERTION(mToolbarRoot != 0, "row id must be non-zero!");
|
||||
}
|
||||
|
||||
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT a.* FROM moz_bookmarks_assoc a, moz_history h WHERE h.url = ?1 AND a.item_child = h.id"),
|
||||
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT a.* FROM moz_bookmarks a, moz_history h WHERE h.url = ?1 AND a.item_child = h.id"),
|
||||
getter_AddRefs(mDBFindURIBookmarks));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Construct a result where the first columns exactly match those returned by
|
||||
// mDBGetVisitPageInfo, and additionally contains columns for position,
|
||||
// item_child, and folder_child from moz_bookmarks_assoc. This selects only
|
||||
// item_child, and folder_child from moz_bookmarks. This selects only
|
||||
// _item_ children which are in moz_history.
|
||||
NS_NAMED_LITERAL_CSTRING(selectItemChildren,
|
||||
"SELECT h.id, h.url, h.title, h.visit_count, MAX(fullv.visit_date), h.rev_host, a.position, a.item_child, a.folder_child, null FROM moz_bookmarks_assoc a JOIN moz_history h ON a.item_child = h.id LEFT JOIN moz_historyvisit v ON h.id = v.page_id LEFT JOIN moz_historyvisit fullv ON h.id = fullv.page_id WHERE a.parent = ?1 AND a.position >= ?2 AND a.position <= ?3 GROUP BY h.id");
|
||||
"SELECT h.id, h.url, h.title, h.visit_count, MAX(fullv.visit_date), h.rev_host, a.position, a.item_child, a.folder_child, null FROM moz_bookmarks a JOIN moz_history h ON a.item_child = h.id LEFT JOIN moz_historyvisit v ON h.id = v.page_id LEFT JOIN moz_historyvisit fullv ON h.id = fullv.page_id WHERE a.parent = ?1 AND a.position >= ?2 AND a.position <= ?3 GROUP BY h.id");
|
||||
|
||||
// Construct a result where the first columns are padded out to the width
|
||||
// of mDBGetVisitPageInfo, containing additional columns for position,
|
||||
// item_child, and folder_child from moz_bookmarks_assoc, and name from
|
||||
// moz_bookmarks_containers. This selects only _folder_ children which are
|
||||
// in moz_bookmarks_containers.
|
||||
// item_child, and folder_child from moz_bookmarks, and name from
|
||||
// moz_bookmarks_folders. This selects only _folder_ children which are
|
||||
// in moz_bookmarks_folders.
|
||||
NS_NAMED_LITERAL_CSTRING(selectFolderChildren,
|
||||
"SELECT null, null, null, null, null, null, a.position, a.item_child, a.folder_child, c.name FROM moz_bookmarks_assoc a JOIN moz_bookmarks_containers c ON c.id = a.folder_child WHERE a.parent = ?1 AND a.position >= ?2 AND a.position <= ?3");
|
||||
"SELECT null, null, null, null, null, null, a.position, a.item_child, a.folder_child, c.name FROM moz_bookmarks a JOIN moz_bookmarks_folders c ON c.id = a.folder_child WHERE a.parent = ?1 AND a.position >= ?2 AND a.position <= ?3");
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(orderByPosition, " ORDER BY a.position");
|
||||
|
||||
|
@ -229,10 +172,13 @@ nsNavBookmarks::Init()
|
|||
getter_AddRefs(mDBGetChildren));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT COUNT(*) FROM moz_bookmarks_assoc WHERE parent = ?1"),
|
||||
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT COUNT(*) FROM moz_bookmarks WHERE parent = ?1"),
|
||||
getter_AddRefs(mDBFolderCount));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = InitRoots();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return transaction.Commit();
|
||||
}
|
||||
|
||||
|
@ -254,6 +200,131 @@ RenumberItemsArray::~RenumberItemsArray()
|
|||
}
|
||||
}
|
||||
|
||||
// nsNavBookmarks::InitRoots
|
||||
//
|
||||
// This locates and creates if necessary the root items in the bookmarks
|
||||
// folder hierarchy. These items are stored in a special roots table that
|
||||
// maps short predefined names to folder IDs.
|
||||
//
|
||||
// Normally, these folders will exist already and we will save their IDs
|
||||
// which are exposed through the bookmark service interface.
|
||||
//
|
||||
// If the root does not exist, a folder is created for it and the ID is
|
||||
// saved in the root table. No user-visible name is given to these folders
|
||||
// and they have no parent or other attributes.
|
||||
//
|
||||
// These attributes are set when the default_places.html file is imported.
|
||||
// It defines the hierarchy, and has special attributes that tell us when
|
||||
// a folder is one of our well-known roots. We then insert the root in the
|
||||
// defined point in the hierarchy and set its attributes from this.
|
||||
//
|
||||
// This should be called as the last part of the init process so that
|
||||
// all of the statements are set up and the service is ready to use.
|
||||
|
||||
nsresult
|
||||
nsNavBookmarks::InitRoots()
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<mozIStorageStatement> getRootStatement;
|
||||
rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING("SELECT folder_id FROM moz_bookmarks_roots WHERE root_name = ?1"),
|
||||
getter_AddRefs(getRootStatement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool importDefaults = PR_FALSE;
|
||||
rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("places"), &mRoot, &importDefaults);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
getRootStatement->Reset();
|
||||
rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("menu"), &mBookmarksRoot, nsnull);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
getRootStatement->Reset();
|
||||
rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("toolbar"), &mToolbarRoot, nsnull);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (importDefaults) {
|
||||
// when there is no places root, we should define the hierarchy by
|
||||
// importing the default one.
|
||||
nsCOMPtr<nsIURI> defaultPlaces;
|
||||
rv = NS_NewURI(getter_AddRefs(defaultPlaces),
|
||||
NS_LITERAL_CSTRING("chrome://browser/locale/places/default_places.html"),
|
||||
nsnull);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = ImportBookmarksHTMLInternal(defaultPlaces, PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// migrate the user's old bookmarks
|
||||
// FIXME: move somewhere else to some profile migrator thingy
|
||||
nsCOMPtr<nsIFile> bookmarksFile;
|
||||
rv = NS_GetSpecialDirectory(NS_APP_BOOKMARKS_50_FILE,
|
||||
getter_AddRefs(bookmarksFile));
|
||||
PRBool bookmarksFileExists;
|
||||
rv = bookmarksFile->Exists(&bookmarksFileExists);
|
||||
if (NS_SUCCEEDED(rv) && bookmarksFileExists) {
|
||||
nsCOMPtr<nsIIOService> ioservice = do_GetService(
|
||||
"@mozilla.org/network/io-service;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIURI> bookmarksFileURI;
|
||||
rv = ioservice->NewFileURI(bookmarksFile,
|
||||
getter_AddRefs(bookmarksFileURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = ImportBookmarksHTMLInternal(bookmarksFileURI, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// nsNavBookmarks::CreateRoot
|
||||
//
|
||||
// This gets or creates a root folder of the given type. aWasCreated
|
||||
// (optional) is true if the folder had to be created, false if we just used
|
||||
// an old one. The statement that gets a folder ID from a root name is
|
||||
// passed in so the DB only needs to parse the statement once, and we don't
|
||||
// have to have a global for this. Creation is less optimized because it
|
||||
// happens rarely.
|
||||
|
||||
nsresult
|
||||
nsNavBookmarks::CreateRoot(mozIStorageStatement* aGetRootStatement,
|
||||
const nsCString& name, PRInt64* aID,
|
||||
PRBool* aWasCreated)
|
||||
{
|
||||
PRBool hasResult = PR_FALSE;
|
||||
nsresult rv = aGetRootStatement->BindUTF8StringParameter(0, name);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = aGetRootStatement->ExecuteStep(&hasResult);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (hasResult) {
|
||||
if (aWasCreated)
|
||||
*aWasCreated = PR_FALSE;
|
||||
rv = aGetRootStatement->GetInt64(0, aID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ASSERTION(*aID != 0, "Root is 0 for some reason, folders can't have 0 ID");
|
||||
return NS_OK;
|
||||
}
|
||||
if (aWasCreated)
|
||||
*aWasCreated = PR_TRUE;
|
||||
|
||||
// create folder with no name or attributes
|
||||
nsCOMPtr<mozIStorageStatement> insertStatement;
|
||||
rv = CreateFolder(0, NS_LITERAL_STRING(""), -1, aID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// save root ID
|
||||
rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks_roots (root_name,folder_id) VALUES (?1, ?2)"),
|
||||
getter_AddRefs(insertStatement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = insertStatement->BindUTF8StringParameter(0, name);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = insertStatement->BindInt64Parameter(1, *aID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = insertStatement->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsNavBookmarks::AdjustIndices(PRInt64 aFolder,
|
||||
PRInt32 aStartIndex, PRInt32 aEndIndex,
|
||||
|
@ -263,7 +334,7 @@ nsNavBookmarks::AdjustIndices(PRInt64 aFolder,
|
|||
mozStorageTransaction transaction(dbConn, PR_FALSE);
|
||||
|
||||
nsCAutoString buffer;
|
||||
buffer.AssignLiteral("UPDATE moz_bookmarks_assoc SET position = position + ");
|
||||
buffer.AssignLiteral("UPDATE moz_bookmarks SET position = position + ");
|
||||
buffer.AppendInt(aDelta);
|
||||
buffer.AppendLiteral(" WHERE parent = ");
|
||||
buffer.AppendInt(aFolder);
|
||||
|
@ -396,7 +467,7 @@ nsNavBookmarks::InsertItem(PRInt64 aFolder, nsIURI *aItem, PRInt32 aIndex)
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCAutoString buffer;
|
||||
buffer.AssignLiteral("INSERT INTO moz_bookmarks_assoc (item_child, parent, position) VALUES (");
|
||||
buffer.AssignLiteral("INSERT INTO moz_bookmarks (item_child, parent, position) VALUES (");
|
||||
buffer.AppendInt(childID);
|
||||
buffer.AppendLiteral(", ");
|
||||
buffer.AppendInt(aFolder);
|
||||
|
@ -433,7 +504,7 @@ nsNavBookmarks::RemoveItem(PRInt64 aFolder, nsIURI *aItem)
|
|||
PRInt32 childIndex;
|
||||
nsCAutoString buffer;
|
||||
{
|
||||
buffer.AssignLiteral("SELECT position FROM moz_bookmarks_assoc WHERE parent = ");
|
||||
buffer.AssignLiteral("SELECT position FROM moz_bookmarks WHERE parent = ");
|
||||
buffer.AppendInt(aFolder);
|
||||
buffer.AppendLiteral(" AND item_child = ");
|
||||
buffer.AppendInt(childID);
|
||||
|
@ -451,7 +522,7 @@ nsNavBookmarks::RemoveItem(PRInt64 aFolder, nsIURI *aItem)
|
|||
childIndex = results ? statement->AsInt32(0) : -1;
|
||||
}
|
||||
|
||||
buffer.AssignLiteral("DELETE FROM moz_bookmarks_assoc WHERE parent = ");
|
||||
buffer.AssignLiteral("DELETE FROM moz_bookmarks WHERE parent = ");
|
||||
buffer.AppendInt(aFolder);
|
||||
buffer.AppendLiteral(" AND item_child = ");
|
||||
buffer.AppendInt(childID);
|
||||
|
@ -529,7 +600,7 @@ nsNavBookmarks::CreateFolder(PRInt64 aParent, const nsAString &aName,
|
|||
|
||||
{
|
||||
nsCOMPtr<mozIStorageStatement> statement;
|
||||
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks_containers (name) VALUES (?1)"),
|
||||
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks_folders (name) VALUES (?1)"),
|
||||
getter_AddRefs(statement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -545,7 +616,7 @@ nsNavBookmarks::CreateFolder(PRInt64 aParent, const nsAString &aName,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCAutoString buffer;
|
||||
buffer.AssignLiteral("INSERT INTO moz_bookmarks_assoc (folder_child, parent, position) VALUES (");
|
||||
buffer.AssignLiteral("INSERT INTO moz_bookmarks (folder_child, parent, position) VALUES (");
|
||||
buffer.AppendInt(child);
|
||||
buffer.AppendLiteral(", ");
|
||||
buffer.AppendInt(aParent);
|
||||
|
@ -574,7 +645,7 @@ nsNavBookmarks::RemoveFolder(PRInt64 aFolder)
|
|||
mozStorageTransaction transaction(dbConn, PR_FALSE);
|
||||
|
||||
nsCAutoString buffer;
|
||||
buffer.AssignLiteral("SELECT parent, position FROM moz_bookmarks_assoc WHERE folder_child = ");
|
||||
buffer.AssignLiteral("SELECT parent, position FROM moz_bookmarks WHERE folder_child = ");
|
||||
buffer.AppendInt(aFolder);
|
||||
|
||||
nsresult rv;
|
||||
|
@ -614,19 +685,20 @@ nsNavBookmarks::RemoveFolder(PRInt64 aFolder)
|
|||
}
|
||||
|
||||
// Now remove the remaining items
|
||||
buffer.AssignLiteral("DELETE FROM moz_bookmarks_assoc WHERE parent = ");
|
||||
buffer.AssignLiteral("DELETE FROM moz_bookmarks WHERE parent = ");
|
||||
buffer.AppendInt(aFolder);
|
||||
rv = dbConn->ExecuteSimpleSQL(buffer);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
buffer.AssignLiteral("DELETE FROM moz_bookmarks WHERE folder_child = ");
|
||||
// Remove the folder from its parent
|
||||
buffer.AssignLiteral("DELETE FROM moz_bookmarks_assoc WHERE folder_child = ");
|
||||
buffer.AssignLiteral("DELETE FROM moz_bookmarks WHERE folder_child = ");
|
||||
buffer.AppendInt(aFolder);
|
||||
rv = dbConn->ExecuteSimpleSQL(buffer);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// And remove the folder from the folder table
|
||||
buffer.AssignLiteral("DELETE FROM moz_bookmarks_containers WHERE id = ");
|
||||
buffer.AssignLiteral("DELETE FROM moz_bookmarks_folders WHERE id = ");
|
||||
buffer.AppendInt(aFolder);
|
||||
rv = dbConn->ExecuteSimpleSQL(buffer);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -652,7 +724,7 @@ nsNavBookmarks::MoveFolder(PRInt64 aFolder, PRInt64 aNewParent, PRInt32 aIndex)
|
|||
mozStorageTransaction transaction(dbConn, PR_FALSE);
|
||||
|
||||
nsCAutoString buffer;
|
||||
buffer.AssignLiteral("SELECT parent, position FROM moz_bookmarks_assoc WHERE folder_child = ");
|
||||
buffer.AssignLiteral("SELECT parent, position FROM moz_bookmarks WHERE folder_child = ");
|
||||
buffer.AppendInt(aFolder);
|
||||
|
||||
nsresult rv;
|
||||
|
@ -678,7 +750,7 @@ nsNavBookmarks::MoveFolder(PRInt64 aFolder, PRInt64 aNewParent, PRInt32 aIndex)
|
|||
|
||||
PRInt32 newIndex;
|
||||
if (aIndex == -1) {
|
||||
newIndex = FolderCount(parent);
|
||||
newIndex = FolderCount(aNewParent);
|
||||
// If the parent remains the same, then the folder is really being moved
|
||||
// to count - 1 (since it's being removed from the old position)
|
||||
if (parent == aNewParent) {
|
||||
|
@ -686,6 +758,13 @@ nsNavBookmarks::MoveFolder(PRInt64 aFolder, PRInt64 aNewParent, PRInt32 aIndex)
|
|||
}
|
||||
} else {
|
||||
newIndex = aIndex;
|
||||
|
||||
if (parent == aNewParent && newIndex > oldIndex) {
|
||||
// when an item is being moved lower in the same folder, the new index
|
||||
// refers to the index before it was removed. Removal causes everything
|
||||
// to shift up.
|
||||
--newIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (aNewParent == parent && newIndex == oldIndex) {
|
||||
|
@ -707,7 +786,7 @@ nsNavBookmarks::MoveFolder(PRInt64 aFolder, PRInt64 aNewParent, PRInt32 aIndex)
|
|||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
buffer.AssignLiteral("UPDATE moz_bookmarks_assoc SET parent = ");
|
||||
buffer.AssignLiteral("UPDATE moz_bookmarks SET parent = ");
|
||||
buffer.AppendInt(aNewParent);
|
||||
buffer.AppendLiteral(", position = ");
|
||||
buffer.AppendInt(newIndex);
|
||||
|
@ -728,6 +807,35 @@ nsNavBookmarks::MoveFolder(PRInt64 aFolder, PRInt64 aNewParent, PRInt32 aIndex)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavBookmarks::GetChildFolder(PRInt64 aFolder, const nsAString& aSubFolder,
|
||||
PRInt64* _result)
|
||||
{
|
||||
// note: we allow empty folder names
|
||||
nsresult rv;
|
||||
if (aFolder == 0)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
// If this gets used a lot, we'll want a precompiled statement
|
||||
nsCOMPtr<mozIStorageStatement> statement;
|
||||
rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING("SELECT c.id FROM moz_bookmarks a JOIN moz_bookmarks_folders c ON a.folder_child = c.id WHERE a.parent = ?1 AND c.name = ?2"),
|
||||
getter_AddRefs(statement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
statement->BindInt64Parameter(0, aFolder);
|
||||
statement->BindStringParameter(1, aSubFolder);
|
||||
|
||||
PRBool hasResult = PR_FALSE;
|
||||
rv = statement->ExecuteStep(&hasResult);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (! hasResult) {
|
||||
// item not found
|
||||
*_result = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return statement->GetInt64(0, _result);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavBookmarks::SetItemTitle(nsIURI *aURI, const nsAString &aTitle)
|
||||
|
@ -760,7 +868,7 @@ NS_IMETHODIMP
|
|||
nsNavBookmarks::SetFolderTitle(PRInt64 aFolder, const nsAString &aTitle)
|
||||
{
|
||||
nsCOMPtr<mozIStorageStatement> statement;
|
||||
nsresult rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING("UPDATE moz_bookmarks_containers SET title = ?2 WHERE id = ?1"),
|
||||
nsresult rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING("UPDATE moz_bookmarks_folders SET name = ?2 WHERE id = ?1"),
|
||||
getter_AddRefs(statement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
|
|
@ -71,6 +71,11 @@ private:
|
|||
|
||||
~nsNavBookmarks();
|
||||
|
||||
nsresult InitRoots();
|
||||
nsresult CreateRoot(mozIStorageStatement* aGetRootStatement,
|
||||
const nsCString& name, PRInt64* aID,
|
||||
PRBool* aWasCreated);
|
||||
|
||||
nsresult AdjustIndices(PRInt64 aFolder,
|
||||
PRInt32 aStartIndex, PRInt32 aEndIndex,
|
||||
PRInt32 aDelta);
|
||||
|
@ -80,6 +85,7 @@ private:
|
|||
nsNavHistoryResultNode **aNode);
|
||||
PRInt32 FolderCount(PRInt64 aFolder);
|
||||
|
||||
// remove me when there is better query initialization
|
||||
nsNavHistory* History() { return nsNavHistory::GetHistoryService(); }
|
||||
|
||||
mozIStorageStatement* DBGetURLPageInfo()
|
||||
|
@ -113,4 +119,8 @@ private:
|
|||
nsCOMPtr<mozIStorageStatement> mDBFolderCount;
|
||||
|
||||
nsCOMPtr<nsIStringBundle> mBundle;
|
||||
|
||||
// in nsBookmarksHTML
|
||||
nsresult ImportBookmarksHTMLInternal(nsIURI* aURL,
|
||||
PRBool aAllowRootChanges);
|
||||
};
|
||||
|
|
|
@ -196,6 +196,12 @@ const PRInt32 nsNavHistory::kAutoCompleteIndex_Typed = 3;
|
|||
static nsDataHashtable<nsStringHashKey, int>* gTldTypes;
|
||||
static const char* gQuitApplicationMessage = "quit-application";
|
||||
|
||||
// annotation names
|
||||
const char nsNavHistory::kAnnotationTitle[] = "history/title";
|
||||
const char nsNavHistory::kAnnotationFavIconName[] = "history/iconurl";
|
||||
const char nsNavHistory::kAnnotationFavIconData[] = "history/icondata";
|
||||
const char nsNavHistory::kAnnotationPreviousEncoding[] = "history/encoding";
|
||||
|
||||
nsIAtom* nsNavHistory::sMenuRootAtom = nsnull;
|
||||
nsIAtom* nsNavHistory::sToolbarRootAtom = nsnull;
|
||||
|
||||
|
|
|
@ -454,6 +454,12 @@ public:
|
|||
aOptions);
|
||||
}
|
||||
|
||||
// well-known annotations used by the history and bookmarks systems
|
||||
static const char kAnnotationTitle[];
|
||||
static const char kAnnotationFavIconName[];
|
||||
static const char kAnnotationFavIconData[];
|
||||
static const char kAnnotationPreviousEncoding[];
|
||||
|
||||
private:
|
||||
~nsNavHistory();
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче