LCOV - code coverage report
Current view: directory - toolkit/components/places - nsPlacesImportExportService.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1065 895 84.0 %
Date: 2012-06-02 Functions: 74 65 87.8 %

       1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Mozilla History System
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Google Inc.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2005
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Brett Wilson <brettw@gmail.com>
      24                 :  *   Dietrich Ayala <dietrich@mozilla.com>
      25                 :  *   Drew Willcoxon <adw@mozilla.com>
      26                 :  *   Marco Bonardo <mak77@bonardo.net>
      27                 :  *
      28                 :  * Alternatively, the contents of this file may be used under the terms of
      29                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      30                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      31                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      32                 :  * of those above. If you wish to allow use of your version of this file only
      33                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      34                 :  * use your version of this file under the terms of the MPL, indicate your
      35                 :  * decision by deleting the provisions above and replace them with the notice
      36                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      37                 :  * the provisions above, a recipient may use your version of this file under
      38                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      39                 :  *
      40                 :  * ***** END LICENSE BLOCK ***** */
      41                 : 
      42                 : /**
      43                 :  * Importer/exporter between the mozStorage-based bookmarks and the old-style
      44                 :  * "bookmarks.html"
      45                 :  *
      46                 :  * Format:
      47                 :  *
      48                 :  * Primary heading := h1
      49                 :  *   Old version used this to set attributes on the bookmarks RDF root, such
      50                 :  *   as the last modified date. We only use H1 to check for the attribute
      51                 :  *   PLACES_ROOT, which tells us that this hierarchy root is the places root.
      52                 :  *   For backwards compatibility, if we don't find this, we assume that the
      53                 :  *   hierarchy is rooted at the bookmarks menu.
      54                 :  * Heading := any heading other than h1
      55                 :  *   Old version used this to set attributes on the current container. We only
      56                 :  *   care about the content of the heading container, which contains the title
      57                 :  *   of the bookmark container.
      58                 :  * Bookmark := a
      59                 :  *   HREF is the destination of the bookmark
      60                 :  *   FEEDURL is the URI of the RSS feed if this is a livemark.
      61                 :  *   LAST_CHARSET is stored as an annotation so that the next time we go to
      62                 :  *     that page we remember the user's preference.
      63                 :  *   WEB_PANEL is set to "true" if the bookmark should be loaded in the sidebar.
      64                 :  *   ICON will be stored in the favicon service
      65                 :  *   ICON_URI is new for places bookmarks.html, it refers to the original
      66                 :  *     URI of the favicon so we don't have to make up favicon URLs.
      67                 :  *   Text of the <a> container is the name of the bookmark
      68                 :  *   Ignored: LAST_VISIT, ID (writing out non-RDF IDs can confuse Firefox 2)
      69                 :  * Bookmark comment := dd
      70                 :  *   This affects the previosly added bookmark
      71                 :  * Separator := hr
      72                 :  *   Insert a separator into the current container
      73                 :  * The folder hierarchy is defined by <dl>/<ul>/<menu> (the old importing code
      74                 :  *     handles all these cases, when we write, use <dl>).
      75                 :  *
      76                 :  * Overall design
      77                 :  * --------------
      78                 :  *
      79                 :  * We need to emulate a recursive parser. A "Bookmark import frame" is created
      80                 :  * corresponding to each folder we encounter. These are arranged in a stack,
      81                 :  * and contain all the state we need to keep track of.
      82                 :  *
      83                 :  * A frame is created when we find a heading, which defines a new container.
      84                 :  * The frame also keeps track of the nesting of <DL>s, (in well-formed
      85                 :  * bookmarks files, these will have a 1-1 correspondence with frames, but we
      86                 :  * try to be a little more flexible here). When the nesting count decreases
      87                 :  * to 0, then we know a frame is complete and to pop back to the previous
      88                 :  * frame.
      89                 :  *
      90                 :  * Note that a lot of things happen when tags are CLOSED because we need to
      91                 :  * get the text from the content of the tag. For example, link and heading tags
      92                 :  * both require the content (= title) before actually creating it.
      93                 :  */
      94                 : 
      95                 : #include "nsPlacesImportExportService.h"
      96                 : #include "nsNetUtil.h"
      97                 : #include "nsParserCIID.h"
      98                 : #include "nsUnicharUtils.h"
      99                 : #include "nsAppDirectoryServiceDefs.h"
     100                 : #include "nsDirectoryServiceUtils.h"
     101                 : #include "nsToolkitCompsCID.h"
     102                 : #include "nsIHTMLContentSink.h"
     103                 : #include "nsIParser.h"
     104                 : #include "prprf.h"
     105                 : #include "nsIObserverService.h"
     106                 : #include "nsISupportsPrimitives.h"
     107                 : #include "nsPlacesMacros.h"
     108                 : #include "mozilla/Util.h"
     109                 : #include "Helpers.h"
     110                 : 
     111                 : using namespace mozilla;
     112                 : using namespace mozilla::places;
     113                 : 
     114                 : static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
     115                 : 
     116                 : #define KEY_TOOLBARFOLDER_LOWER "personal_toolbar_folder"
     117                 : #define KEY_BOOKMARKSMENU_LOWER "bookmarks_menu"
     118                 : #define KEY_UNFILEDFOLDER_LOWER "unfiled_bookmarks_folder"
     119                 : #define KEY_PLACESROOT_LOWER "places_root"
     120                 : #define KEY_HREF_LOWER "href"
     121                 : #define KEY_FEEDURL_LOWER "feedurl"
     122                 : #define KEY_WEB_PANEL_LOWER "web_panel"
     123                 : #define KEY_LASTCHARSET_LOWER "last_charset"
     124                 : #define KEY_ICON_LOWER "icon"
     125                 : #define KEY_ICON_URI_LOWER "icon_uri"
     126                 : #define KEY_SHORTCUTURL_LOWER "shortcuturl"
     127                 : #define KEY_POST_DATA_LOWER "post_data"
     128                 : #define KEY_NAME_LOWER "name"
     129                 : #define KEY_MICSUM_GEN_URI_LOWER "micsum_gen_uri"
     130                 : #define KEY_DATE_ADDED_LOWER "add_date"
     131                 : #define KEY_LAST_MODIFIED_LOWER "last_modified"
     132                 : #define KEY_GENERATED_TITLE_LOWER "generated_title"
     133                 : 
     134                 : #define LOAD_IN_SIDEBAR_ANNO NS_LITERAL_CSTRING("bookmarkProperties/loadInSidebar")
     135                 : #define DESCRIPTION_ANNO NS_LITERAL_CSTRING("bookmarkProperties/description")
     136                 : #define POST_DATA_ANNO NS_LITERAL_CSTRING("bookmarkProperties/POSTData")
     137                 : 
     138                 : #define BOOKMARKS_MENU_ICON_URI "chrome://browser/skin/places/bookmarksMenu.png"
     139                 : 
     140                 : // The RESTORE_*_NSIOBSERVER_TOPIC #defines should match the constants of the
     141                 : // same names in toolkit/components/places/src/utils.js
     142                 : #define RESTORE_BEGIN_NSIOBSERVER_TOPIC "bookmarks-restore-begin"
     143                 : #define RESTORE_SUCCESS_NSIOBSERVER_TOPIC "bookmarks-restore-success"
     144                 : #define RESTORE_FAILED_NSIOBSERVER_TOPIC "bookmarks-restore-failed"
     145                 : #define RESTORE_NSIOBSERVER_DATA NS_LITERAL_STRING("html")
     146                 : #define RESTORE_INITIAL_NSIOBSERVER_DATA NS_LITERAL_STRING("html-initial")
     147                 : 
     148                 : #define LMANNO_FEEDURI "livemark/feedURI"
     149                 : #define LMANNO_SITEURI "livemark/siteURI"
     150                 : 
     151                 : // define to get debugging messages on console about import/export
     152                 : //#define DEBUG_IMPORT
     153                 : //#define DEBUG_EXPORT
     154                 : 
     155                 : #if defined(XP_WIN) || defined(XP_OS2)
     156                 : #define NS_LINEBREAK "\015\012"
     157                 : #else
     158                 : #define NS_LINEBREAK "\012"
     159                 : #endif
     160                 : 
     161                 : class nsIOutputStream;
     162                 : static const char kWhitespace[] = " \r\n\t\b";
     163                 : static nsresult WriteEscapedUrl(const nsCString &aString, nsIOutputStream* aOutput);
     164                 : 
     165                 : class BookmarkImportFrame
     166             267 : {
     167                 : public:
     168              89 :   BookmarkImportFrame(PRInt64 aID) :
     169                 :       mContainerID(aID),
     170                 :       mContainerNesting(0),
     171                 :       mLastContainerType(Container_Normal),
     172                 :       mInDescription(false),
     173                 :       mPreviousId(0),
     174                 :       mPreviousDateAdded(0),
     175              89 :       mPreviousLastModifiedDate(0)
     176                 :   {
     177              89 :   }
     178                 : 
     179                 :   enum ContainerType { Container_Normal,
     180                 :                        Container_Places,
     181                 :                        Container_Menu,
     182                 :                        Container_Toolbar,
     183                 :                        Container_Unfiled};
     184                 : 
     185                 :   PRInt64 mContainerID;
     186                 : 
     187                 :   // How many <dl>s have been nested. Each frame/container should start
     188                 :   // with a heading, and is then followed by a <dl>, <ul>, or <menu>. When
     189                 :   // that list is complete, then it is the end of this container and we need
     190                 :   // to pop back up one level for new items. If we never get an open tag for
     191                 :   // one of these things, we should assume that the container is empty and
     192                 :   // that things we find should be siblings of it. Normally, these <dl>s won't
     193                 :   // be nested so this will be 0 or 1.
     194                 :   PRInt32 mContainerNesting;
     195                 : 
     196                 :   // when we find a heading tag, it actually affects the title of the NEXT
     197                 :   // container in the list. This stores that heading tag and whether it was
     198                 :   // special. 'ConsumeHeading' resets this.
     199                 :   ContainerType mLastContainerType;
     200                 : 
     201                 :   // this contains the text from the last begin tag until now. It is reset
     202                 :   // at every begin tag. We can check it when we see a </a>, or </h3>
     203                 :   // to see what the text content of that node should be.
     204                 :   nsString mPreviousText;
     205                 : 
     206                 :   // true when we hit a <dd>, which contains the description for the preceding
     207                 :   // <a> tag. We can't just check for </dd> like we can for </a> or </h3>
     208                 :   // because if there is a sub-folder, it is actually a child of the <dd>
     209                 :   // because the tag is never explicitly closed. If this is true and we see a
     210                 :   // new open tag, that means to commit the description to the previous
     211                 :   // bookmark.
     212                 :   //
     213                 :   // Additional weirdness happens when the previous <dt> tag contains a <h3>:
     214                 :   // this means there is a new folder with the given description, and whose
     215                 :   // children are contained in the following <dl> list.
     216                 :   //
     217                 :   // This is handled in OpenContainer(), which commits previous text if
     218                 :   // necessary.
     219                 :   bool mInDescription;
     220                 : 
     221                 :   // contains the URL of the previous bookmark created. This is used so that
     222                 :   // when we encounter a <dd>, we know what bookmark to associate the text with.
     223                 :   // This is cleared whenever we hit a <h3>, so that we know NOT to save this
     224                 :   // with a bookmark, but to keep it until 
     225                 :   nsCOMPtr<nsIURI> mPreviousLink;
     226                 : 
     227                 :   // contains the URL of the previous livemark, so that when the link ends,
     228                 :   // and the livemark title is known, we can create it.
     229                 :   nsCOMPtr<nsIURI> mPreviousFeed;
     230                 : 
     231              61 :   void ConsumeHeading(nsAString* aHeading, ContainerType* aContainerType)
     232                 :   {
     233              61 :     *aHeading = mPreviousText;
     234              61 :     *aContainerType = mLastContainerType;
     235              61 :     mPreviousText.Truncate();
     236              61 :   }
     237                 : 
     238                 :   // Contains the id of an imported, or newly created bookmark.
     239                 :   PRInt64 mPreviousId;
     240                 : 
     241                 :   // Contains the date-added and last-modified-date of an imported item.
     242                 :   // Used to override the values set by insertBookmark, createFolder, etc.
     243                 :   PRTime mPreviousDateAdded;
     244                 :   PRTime mPreviousLastModifiedDate;
     245                 : };
     246                 : 
     247                 : /**
     248                 :  * Copied from nsEscape.cpp, which requires internal string API.
     249                 :  */
     250                 : char*
     251             124 : nsEscapeHTML(const char* string)
     252                 : {
     253                 :   /* XXX Hardcoded max entity len. The +1 is for the trailing null. */
     254             124 :   char* escaped = nsnull;
     255             124 :   PRUint32 len = strlen(string);
     256             124 :   if (len >= (PR_UINT32_MAX / 6))
     257               0 :     return nsnull;
     258                 : 
     259             124 :   escaped = (char*)NS_Alloc((len * 6) + 1);
     260             124 :   if (escaped) {
     261             124 :     char* ptr = escaped;
     262            2294 :     for (; *string != '\0'; string++) {
     263            2170 :       switch(*string) {
     264                 :         case '<':
     265               0 :           *ptr++ = '&';
     266               0 :           *ptr++ = 'l';
     267               0 :           *ptr++ = 't';
     268               0 :           *ptr++ = ';';
     269               0 :           break;
     270                 :         case '>':
     271               0 :           *ptr++ = '&';
     272               0 :           *ptr++ = 'g';
     273               0 :           *ptr++ = 't';
     274               0 :           *ptr++ = ';';
     275               0 :           break;
     276                 :         case '&':
     277               5 :           *ptr++ = '&';
     278               5 :           *ptr++ = 'a';
     279               5 :           *ptr++ = 'm';
     280               5 :           *ptr++ = 'p';
     281               5 :           *ptr++ = ';';
     282               5 :           break;
     283                 :         case '"':
     284               0 :           *ptr++ = '&';
     285               0 :           *ptr++ = 'q';
     286               0 :           *ptr++ = 'u';
     287               0 :           *ptr++ = 'o';
     288               0 :           *ptr++ = 't';
     289               0 :           *ptr++ = ';';
     290               0 :           break;
     291                 :         case '\'':
     292               0 :           *ptr++ = '&';
     293               0 :           *ptr++ = '#';
     294               0 :           *ptr++ = '3';
     295               0 :           *ptr++ = '9';
     296               0 :           *ptr++ = ';';
     297               0 :           break;
     298                 :         default:
     299            2165 :           *ptr++ = *string;
     300                 :       }
     301                 :     }
     302             124 :     *ptr = '\0';
     303                 :   }
     304             124 :   return escaped;
     305                 : }
     306                 : 
     307              22 : PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsPlacesImportExportService, gImportExportService)
     308                 : 
     309             285 : NS_IMPL_ISUPPORTS2(nsPlacesImportExportService, nsIPlacesImportExportService,
     310                 :                    nsINavHistoryBatchCallback)
     311                 : 
     312                 : 
     313              11 : nsPlacesImportExportService::nsPlacesImportExportService()
     314                 : {
     315              11 :   NS_ASSERTION(!gImportExportService,
     316                 :                "Attempting to create two instances of the service!");
     317              11 :   gImportExportService = this;
     318              11 : }
     319                 : 
     320              33 : nsPlacesImportExportService::~nsPlacesImportExportService()
     321                 : {
     322              11 :   NS_ASSERTION(gImportExportService == this,
     323                 :                "Deleting a non-singleton instance of the service");
     324              11 :   if (gImportExportService == this)
     325              11 :     gImportExportService = nsnull;
     326              44 : }
     327                 : 
     328                 : nsresult
     329              11 : nsPlacesImportExportService::Init()
     330                 : {
     331                 :   // Be sure to call EnsureServiceState() before using services.
     332              11 :   mHistoryService = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
     333              11 :   NS_ENSURE_TRUE(mHistoryService, NS_ERROR_OUT_OF_MEMORY);
     334              11 :   mFaviconService = do_GetService(NS_FAVICONSERVICE_CONTRACTID);
     335              11 :   NS_ENSURE_TRUE(mFaviconService, NS_ERROR_OUT_OF_MEMORY);
     336              11 :   mAnnotationService = do_GetService(NS_ANNOTATIONSERVICE_CONTRACTID);
     337              11 :   NS_ENSURE_TRUE(mAnnotationService, NS_ERROR_OUT_OF_MEMORY);
     338              11 :   mBookmarksService = do_GetService(NS_NAVBOOKMARKSSERVICE_CONTRACTID);
     339              11 :   NS_ENSURE_TRUE(mBookmarksService, NS_ERROR_OUT_OF_MEMORY);
     340              11 :   mLivemarkService = do_GetService(NS_LIVEMARKSERVICE_CONTRACTID);
     341              11 :   NS_ENSURE_TRUE(mLivemarkService, NS_ERROR_OUT_OF_MEMORY);
     342              11 :   return NS_OK;
     343                 : }
     344                 : 
     345                 : /**
     346                 :  * The content sink stuff is based loosely on nsIHTMLContentSink.
     347                 :  */
     348                 : class BookmarkContentSink : public nsIHTMLContentSink
     349              28 : {
     350                 : public:
     351                 :   BookmarkContentSink();
     352                 : 
     353                 :   nsresult Init(bool aAllowRootChanges,
     354                 :                 PRInt64 aFolder,
     355                 :                 bool aIsImportDefaults);
     356                 : 
     357                 :   NS_DECL_ISUPPORTS
     358                 : 
     359                 :   // nsIContentSink (superclass of nsIHTMLContentSink)
     360              53 :   NS_IMETHOD WillParse() { return NS_OK; }
     361              25 :   NS_IMETHOD WillInterrupt() { return NS_OK; }
     362              53 :   NS_IMETHOD WillResume() { return NS_OK; }
     363              28 :   NS_IMETHOD SetParser(nsParserBase* aParser) { return NS_OK; }
     364               0 :   virtual void FlushPendingNotifications(mozFlushType aType) { }
     365              25 :   NS_IMETHOD SetDocumentCharset(nsACString& aCharset) { return NS_OK; }
     366               0 :   virtual nsISupports *GetTarget() { return nsnull; }
     367                 : 
     368                 :   // nsIHTMLContentSink
     369              25 :   NS_IMETHOD OpenHead() { return NS_OK; }
     370               0 :   NS_IMETHOD BeginContext(PRInt32 aPosition) { return NS_OK; }
     371               0 :   NS_IMETHOD EndContext(PRInt32 aPosition) { return NS_OK; }
     372             112 :   NS_IMETHOD IsEnabled(PRInt32 aTag, bool* aReturn)
     373             112 :     { *aReturn = true; return NS_OK; }
     374               0 :   NS_IMETHOD DidProcessTokens() { return NS_OK; }
     375               0 :   NS_IMETHOD WillProcessAToken() { return NS_OK; }
     376            2390 :   NS_IMETHOD DidProcessAToken() { return NS_OK; }
     377                 :   NS_IMETHOD OpenContainer(const nsIParserNode& aNode);
     378                 :   NS_IMETHOD CloseContainer(const nsHTMLTag aTag);
     379                 :   NS_IMETHOD AddLeaf(const nsIParserNode& aNode);
     380               0 :   NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode) { return NS_OK; }
     381                 : 
     382                 : protected:
     383                 :   nsCOMPtr<nsINavBookmarksService> mBookmarksService;
     384                 :   nsCOMPtr<nsINavHistoryService> mHistoryService;
     385                 :   nsCOMPtr<nsIAnnotationService> mAnnotationService;
     386                 :   nsCOMPtr<mozIAsyncLivemarks> mLivemarkService;
     387                 : 
     388                 :   // If set, we will move root items to from their existing position
     389                 :   // in the hierarchy, to where we find them in the bookmarks file
     390                 :   // being imported. This should be set when we are loading 
     391                 :   // the default places html file, and should be unset when doing
     392                 :   // normal imports so that root folders will not get moved when
     393                 :   // importing bookmarks.html files.
     394                 :   bool mAllowRootChanges;
     395                 : 
     396                 :   // If set, this is an import of initial bookmarks.html content,
     397                 :   // so we don't want to kick off HTTP traffic
     398                 :   // and we want the imported personal toolbar folder
     399                 :   // to be set as the personal toolbar folder. (If not set
     400                 :   // we will treat it as a normal folder.)
     401                 :   bool mIsImportDefaults;
     402                 : 
     403                 :   // If a folder was specified to import into, then ignore flags to put
     404                 :   // bookmarks in the bookmarks menu or toolbar and keep them inside
     405                 :   // the folder.
     406                 :   bool mFolderSpecified;
     407                 : 
     408                 :   void HandleContainerBegin(const nsIParserNode& node);
     409                 :   void HandleContainerEnd();
     410                 :   void HandleHead1Begin(const nsIParserNode& node);
     411                 :   void HandleHeadBegin(const nsIParserNode& node);
     412                 :   void HandleHeadEnd();
     413                 :   void HandleLinkBegin(const nsIParserNode& node);
     414                 :   void HandleLinkEnd();
     415                 :   void HandleSeparator(const nsIParserNode& node);
     416                 : 
     417                 :   // This is a list of frames. We really want a recursive parser, but the HTML
     418                 :   // parser gives us tags as a stream. This implements all the state on a stack
     419                 :   // so we can get the recursive information we need. Use "CurFrame" to get the
     420                 :   // top "stack frame" with the current state in it.
     421                 :   nsTArray<BookmarkImportFrame> mFrames;
     422            2167 :   BookmarkImportFrame& CurFrame()
     423                 :   {
     424            2167 :     NS_ASSERTION(mFrames.Length() > 0, "Asking for frame when there are none!");
     425            2167 :     return mFrames[mFrames.Length() - 1];
     426                 :   }
     427              94 :   BookmarkImportFrame& PreviousFrame()
     428                 :   {
     429              94 :     NS_ASSERTION(mFrames.Length() > 1, "Asking for frame when there are not enough!");
     430              94 :     return mFrames[mFrames.Length() - 2];
     431                 :   }
     432                 :   nsresult NewFrame();
     433                 :   nsresult PopFrame();
     434                 : 
     435                 :   nsresult SetFaviconForURI(nsIURI* aPageURI, nsIURI* aFaviconURI,
     436                 :                             const nsString& aData);
     437                 : 
     438                 :   PRTime ConvertImportedDateToInternalDate(const nsACString& aDate);
     439                 : 
     440                 : #ifdef DEBUG_IMPORT
     441                 :   // prints spaces for indenting to the current frame depth
     442                 :   void PrintNesting()
     443                 :   {
     444                 :     for (PRUint32 i = 0; i < mFrames.Length(); i ++)
     445                 :       printf("  ");
     446                 :   }
     447                 : #endif
     448                 : };
     449                 : 
     450                 : 
     451              28 : BookmarkContentSink::BookmarkContentSink() : mFrames(16)
     452                 : {
     453              28 : }
     454                 : 
     455                 : 
     456                 : nsresult
     457              28 : BookmarkContentSink::Init(bool aAllowRootChanges,
     458                 :                           PRInt64 aFolder,
     459                 :                           bool aIsImportDefaults)
     460                 : {
     461              28 :   mBookmarksService = do_GetService(NS_NAVBOOKMARKSSERVICE_CONTRACTID);
     462              28 :   NS_ENSURE_TRUE(mBookmarksService, NS_ERROR_OUT_OF_MEMORY);
     463              28 :   mHistoryService = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
     464              28 :   NS_ENSURE_TRUE(mHistoryService, NS_ERROR_OUT_OF_MEMORY);
     465              28 :   mAnnotationService = do_GetService(NS_ANNOTATIONSERVICE_CONTRACTID);
     466              28 :   NS_ENSURE_TRUE(mAnnotationService, NS_ERROR_OUT_OF_MEMORY);
     467              28 :   mLivemarkService = do_GetService(NS_LIVEMARKSERVICE_CONTRACTID);
     468              28 :   NS_ENSURE_TRUE(mLivemarkService, NS_ERROR_OUT_OF_MEMORY);
     469                 : 
     470              28 :   mAllowRootChanges = aAllowRootChanges;
     471              28 :   mIsImportDefaults = aIsImportDefaults;
     472                 : 
     473                 :   // initialize the root frame with the menu root
     474                 :   PRInt64 menuRoot;
     475                 :   nsresult rv;
     476              28 :   if (aFolder == 0) {
     477              24 :     rv = mBookmarksService->GetBookmarksMenuFolder(&menuRoot);
     478              24 :     NS_ENSURE_SUCCESS(rv, rv);
     479              24 :     mFolderSpecified = false;
     480                 :   }
     481                 :   else {
     482               4 :     menuRoot = aFolder;
     483               4 :     mFolderSpecified = true;
     484                 :   }
     485              28 :   if (!mFrames.AppendElement(BookmarkImportFrame(menuRoot)))
     486               0 :     return NS_ERROR_OUT_OF_MEMORY;
     487                 : 
     488              28 :   return NS_OK;
     489                 : }
     490                 : 
     491                 : 
     492             728 : NS_IMPL_ISUPPORTS2(BookmarkContentSink,
     493                 :                    nsIContentSink,
     494                 :                    nsIHTMLContentSink)
     495                 : 
     496                 : 
     497                 : NS_IMETHODIMP
     498             825 : BookmarkContentSink::OpenContainer(const nsIParserNode& aNode)
     499                 : {
     500             825 :   switch(aNode.GetNodeType()) {
     501                 :     case eHTMLTag_h1:
     502              25 :       HandleHead1Begin(aNode);
     503              25 :       break;
     504                 :     case eHTMLTag_h2:
     505                 :     case eHTMLTag_h3:
     506                 :     case eHTMLTag_h4:
     507                 :     case eHTMLTag_h5:
     508                 :     case eHTMLTag_h6:
     509              61 :       HandleHeadBegin(aNode);
     510              61 :       break;
     511                 :     case eHTMLTag_a:
     512             147 :       HandleLinkBegin(aNode);
     513             147 :       break;
     514                 :     case eHTMLTag_dl:
     515                 :     case eHTMLTag_ul:
     516                 :     case eHTMLTag_menu:
     517              86 :       HandleContainerBegin(aNode);
     518              86 :       break;
     519                 :     case eHTMLTag_dd:
     520              49 :       CurFrame().mInDescription = true;
     521              49 :       break;
     522                 :   }
     523             825 :   return NS_OK;
     524                 : }
     525                 : 
     526                 : 
     527                 : NS_IMETHODIMP
     528             850 : BookmarkContentSink::CloseContainer(const nsHTMLTag aTag)
     529                 : {
     530             850 :   BookmarkImportFrame& frame = CurFrame();
     531                 : 
     532                 :   // see the comment for the definition of mInDescription. Basically, we commit
     533                 :   // any text in mPreviousText to the description of the node/folder if there
     534                 :   // is any.
     535             850 :   if (frame.mInDescription) {
     536              49 :     frame.mPreviousText.Trim(kWhitespace); // important!
     537              49 :     if (!frame.mPreviousText.IsEmpty()) {
     538                 : 
     539              49 :       PRInt64 itemId = !frame.mPreviousLink ? frame.mContainerID
     540              49 :                                             : frame.mPreviousId;
     541                 :                     
     542              49 :       bool hasDescription = false;
     543              49 :       nsresult rv = mAnnotationService->ItemHasAnnotation(itemId,
     544              49 :                                                           DESCRIPTION_ANNO,
     545              49 :                                                           &hasDescription);
     546              49 :       if (NS_SUCCEEDED(rv) && !hasDescription) {
     547              70 :         mAnnotationService->SetItemAnnotationString(itemId, DESCRIPTION_ANNO,
     548                 :                                                     frame.mPreviousText, 0,
     549              35 :                                                     nsIAnnotationService::EXPIRE_NEVER);
     550                 :       }
     551              49 :       frame.mPreviousText.Truncate();
     552                 : 
     553                 :       // Set last-modified a 2nd time for all items with descriptions
     554                 :       // we need to set last-modified as the *last* step in processing 
     555                 :       // any item type in the bookmarks.html file, so that we do
     556                 :       // not overwrite the imported value. for items without descriptions, 
     557                 :       // setting this value after setting the item title is that 
     558                 :       // last point at which we can save this value before it gets reset.
     559                 :       // for items with descriptions, it must set after that point.
     560                 :       // however, at the point at which we set the title, there's no way 
     561                 :       // to determine if there will be a description following, 
     562                 :       // so we need to set the last-modified-date at both places.
     563                 : 
     564                 :       PRTime lastModified;
     565              49 :       if (!frame.mPreviousLink) {
     566              33 :         lastModified = PreviousFrame().mPreviousLastModifiedDate;
     567                 :       } else {
     568              16 :         lastModified = frame.mPreviousLastModifiedDate;
     569                 :       }
     570                 : 
     571              49 :       if (itemId > 0 && lastModified > 0) {
     572              40 :         rv = mBookmarksService->SetItemLastModified(itemId, lastModified);
     573              40 :         NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemLastModified failed");
     574                 :       }
     575                 :     }
     576              49 :     frame.mInDescription = false;
     577                 :   }
     578                 : 
     579             850 :   switch (aTag) {
     580                 :     case eHTMLTag_dl:
     581                 :     case eHTMLTag_ul:
     582                 :     case eHTMLTag_menu:
     583              86 :       HandleContainerEnd();
     584              86 :       break;
     585                 :     case eHTMLTag_dt:
     586             208 :       break;
     587                 :     case eHTMLTag_h1:
     588                 :       // ignore
     589              25 :       break;
     590                 :     case eHTMLTag_h2:
     591                 :     case eHTMLTag_h3:
     592                 :     case eHTMLTag_h4:
     593                 :     case eHTMLTag_h5:
     594                 :     case eHTMLTag_h6:
     595              61 :       HandleHeadEnd();
     596              61 :       break;
     597                 :     case eHTMLTag_a:
     598             147 :       HandleLinkEnd();
     599             147 :       break;
     600                 :     default:
     601             323 :       break;
     602                 :   }
     603             850 :   return NS_OK;
     604                 : }
     605                 : 
     606                 : 
     607                 : // BookmarkContentSink::AddLeaf
     608                 : //
     609                 : //    XXX on the branch, we should be calling CollectSkippedContent as in
     610                 : //    nsHTMLFragmentContentSink.cpp:AddLeaf when we encounter title, script,
     611                 : //    style, or server tags. Apparently if we don't, we'll leak the next DOM
     612                 : //    node. However, this requires that we keep a reference to the parser we'll
     613                 : //    introduce a circular reference because it has a reference to us.
     614                 : //
     615                 : //    This is annoying to fix and these elements are not allowed in bookmarks
     616                 : //    files anyway. So if somebody tries to import a crazy bookmarks file, it
     617                 : //    will leak a little bit.
     618                 : 
     619                 : NS_IMETHODIMP
     620            1142 : BookmarkContentSink::AddLeaf(const nsIParserNode& aNode)
     621                 : {
     622            1142 :   switch (aNode.GetNodeType()) {
     623                 :   case eHTMLTag_text:
     624                 :     // save any text we find
     625             306 :     CurFrame().mPreviousText += aNode.GetText();
     626             306 :     break;
     627                 :   case eHTMLTag_entity: {
     628               0 :     nsAutoString tmp;
     629               0 :     PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp);
     630               0 :     if (unicode < 0) {
     631                 :       // invalid entity - just use the text of it
     632               0 :       CurFrame().mPreviousText += aNode.GetText();
     633                 :     } else {
     634               0 :       CurFrame().mPreviousText.Append(unicode);
     635                 :     }
     636                 :     break;
     637                 :   }
     638                 :   case eHTMLTag_whitespace:
     639             281 :     CurFrame().mPreviousText.Append(PRUnichar(' '));
     640             281 :     break;
     641                 :   case eHTMLTag_hr:
     642               0 :     HandleSeparator(aNode);
     643               0 :     break;
     644                 :   }
     645                 : 
     646            1142 :   return NS_OK;
     647                 : }
     648                 : 
     649                 : 
     650                 : void
     651              86 : BookmarkContentSink::HandleContainerBegin(const nsIParserNode& node)
     652                 : {
     653              86 :   CurFrame().mContainerNesting ++;
     654              86 : }
     655                 : 
     656                 : 
     657                 : // BookmarkContentSink::HandleContainerEnd
     658                 : //
     659                 : //    Our "indent" count has decreased, and when we hit 0 that means that this
     660                 : //    container is complete and we need to pop back to the outer frame. Never
     661                 : //    pop the toplevel frame
     662                 : 
     663                 : void
     664              86 : BookmarkContentSink::HandleContainerEnd()
     665                 : {
     666              86 :   BookmarkImportFrame& frame = CurFrame();
     667              86 :   if (frame.mContainerNesting > 0)
     668              86 :     frame.mContainerNesting --;
     669              86 :   if (mFrames.Length() > 1 && frame.mContainerNesting == 0) {
     670                 :     // we also need to re-set the imported last-modified date here. Otherwise
     671                 :     // the addition of items will override the imported field.
     672              61 :     BookmarkImportFrame& prevFrame = PreviousFrame();
     673              61 :     if (prevFrame.mPreviousLastModifiedDate > 0) {
     674              45 :       (void)mBookmarksService->SetItemLastModified(frame.mContainerID,
     675              45 :                                                    prevFrame.mPreviousLastModifiedDate);
     676                 :     }
     677              61 :     PopFrame();
     678                 :   }
     679              86 : }
     680                 : 
     681                 : 
     682                 : // BookmarkContentSink::HandleHead1Begin
     683                 : //
     684                 : //    Handles <H1>. We check for the attribute PLACES_ROOT and reset the
     685                 : //    container id if it's found. Otherwise, the default bookmark menu
     686                 : //    root is assumed and imported things will go into the bookmarks menu.
     687                 : 
     688                 : void
     689              25 : BookmarkContentSink::HandleHead1Begin(const nsIParserNode& node)
     690                 : {
     691              25 :   PRInt32 attrCount = node.GetAttributeCount();
     692              29 :   for (PRInt32 i = 0; i < attrCount; i ++) {
     693               4 :     if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_PLACESROOT_LOWER)) {
     694               0 :       if (mFrames.Length() > 1) {
     695                 :         NS_WARNING("Trying to set the places root from the middle of the hierarchy. "
     696               0 :                    "This can only be set at the beginning.");
     697               0 :         return;
     698                 :       }
     699                 : 
     700                 :       PRInt64 placesRoot;
     701               0 :       DebugOnly<nsresult> rv = mBookmarksService->GetPlacesRoot(&placesRoot);
     702               0 :       NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "could not get placesRoot");
     703               0 :       CurFrame().mContainerID = placesRoot;
     704                 :       break;
     705                 :     }
     706                 :   }
     707                 : }
     708                 : 
     709                 : 
     710                 : // BookmarkContentSink::HandleHeadBegin
     711                 : //
     712                 : //    Called for h2,h3,h4,h5,h6. This just stores the correct information in
     713                 : //    the current frame; the actual new frame corresponding to the container
     714                 : //    associated with the heading will be created when the tag has been closed
     715                 : //    and we know the title (we don't know to create a new folder or to merge
     716                 : //    with an existing one until we have the title).
     717                 : 
     718                 : void
     719              61 : BookmarkContentSink::HandleHeadBegin(const nsIParserNode& node)
     720                 : {
     721              61 :   BookmarkImportFrame& frame = CurFrame();
     722                 : 
     723                 :   // after a heading, a previous bookmark is not applicable (for example, for
     724                 :   // the descriptions contained in a <dd>). Neither is any previous head type
     725              61 :   frame.mPreviousLink = nsnull;
     726              61 :   frame.mLastContainerType = BookmarkImportFrame::Container_Normal;
     727                 : 
     728                 :   // It is syntactically possible for a heading to appear after another heading
     729                 :   // but before the <dl> that encloses that folder's contents.  This should not
     730                 :   // happen in practice, as the file will contain "<dl></dl>" sequence for
     731                 :   // empty containers.
     732                 :   //
     733                 :   // Just to be on the safe side, if we encounter
     734                 :   //   <h3>FOO</h3>
     735                 :   //   <h3>BAR</h3>
     736                 :   //   <dl>...content 1...</dl>
     737                 :   //   <dl>...content 2...</dl>
     738                 :   // we'll pop the stack when we find the h3 for BAR, treating that as an
     739                 :   // implicit ending of the FOO container. The output will be FOO and BAR as
     740                 :   // siblings. If there's another <dl> following (as in "content 2"), those
     741                 :   // items will be treated as further siblings of FOO and BAR
     742              61 :   if (frame.mContainerNesting == 0)
     743               0 :     PopFrame();
     744                 : 
     745                 :   // We have to check for some attributes to see if this is a "special"
     746                 :   // folder, which will have different creation rules when the end tag is
     747                 :   // processed.
     748              61 :   PRInt32 attrCount = node.GetAttributeCount();
     749              61 :   frame.mLastContainerType = BookmarkImportFrame::Container_Normal;
     750             158 :   for (PRInt32 i = 0; i < attrCount; ++i) {
     751             126 :     if (!mFolderSpecified) {
     752             108 :       if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_TOOLBARFOLDER_LOWER)) {
     753              20 :         if (mIsImportDefaults)
     754              20 :           frame.mLastContainerType = BookmarkImportFrame::Container_Toolbar;
     755              20 :         break;
     756                 :       }
     757              88 :       else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_BOOKMARKSMENU_LOWER)) {
     758               0 :         if (mIsImportDefaults)
     759               0 :           frame.mLastContainerType = BookmarkImportFrame::Container_Menu;
     760               0 :         break;
     761                 :       }
     762              88 :       else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_UNFILEDFOLDER_LOWER)) {
     763               9 :         if (mIsImportDefaults)
     764               9 :           frame.mLastContainerType = BookmarkImportFrame::Container_Unfiled;
     765               9 :         break;
     766                 :       }
     767              79 :       else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_PLACESROOT_LOWER)) {
     768               0 :         if (mIsImportDefaults)
     769               0 :           frame.mLastContainerType = BookmarkImportFrame::Container_Places;
     770               0 :         break;
     771                 :       }
     772                 :     }
     773                 : 
     774              97 :     if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_DATE_ADDED_LOWER)) {
     775                 :       frame.mPreviousDateAdded =
     776              37 :         ConvertImportedDateToInternalDate(NS_ConvertUTF16toUTF8(node.GetValueAt(i)));
     777                 :     }
     778              60 :     else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_LAST_MODIFIED_LOWER)) {
     779                 :       frame.mPreviousLastModifiedDate =
     780              41 :         ConvertImportedDateToInternalDate(NS_ConvertUTF16toUTF8(node.GetValueAt(i)));
     781                 :     }
     782                 :   }
     783              61 :   CurFrame().mPreviousText.Truncate();
     784              61 : }
     785                 : 
     786                 : 
     787                 : // BookmarkContentSink::HandleHeadEnd
     788                 : //
     789                 : //    Creates the new frame for this heading now that we know the name of the
     790                 : //    container (tokens since the heading open tag will have been placed in
     791                 : //    mPreviousText).
     792                 : 
     793                 : void
     794              61 : BookmarkContentSink::HandleHeadEnd()
     795                 : {
     796              61 :   NewFrame();
     797              61 : }
     798                 : 
     799                 : 
     800                 : // BookmarkContentSink::HandleLinkBegin
     801                 : //
     802                 : //    Handles "<a" tags by creating a new bookmark. The title of the bookmark
     803                 : //    will be the text content, which will be stuffed in mPreviousText for us
     804                 : //    and which will be saved by HandleLinkEnd
     805                 : 
     806                 : void
     807             147 : BookmarkContentSink::HandleLinkBegin(const nsIParserNode& node)
     808                 : {
     809             147 :   BookmarkImportFrame& frame = CurFrame();
     810                 : 
     811                 :   // Make sure that the feed URIs from previous frames are emptied.
     812             147 :   frame.mPreviousFeed = nsnull;
     813                 :   // Make sure that the bookmark id from previous frames are emptied.
     814             147 :   frame.mPreviousId = 0;
     815                 :   // mPreviousText will hold link text, clear it.
     816             147 :   frame.mPreviousText.Truncate();
     817                 : 
     818                 :   // Get the attributes we care about.
     819             294 :   nsAutoString href;
     820             294 :   nsAutoString feedUrl;
     821             294 :   nsAutoString icon;
     822             294 :   nsAutoString iconUri;
     823             294 :   nsAutoString lastCharset;
     824             294 :   nsAutoString keyword;
     825             294 :   nsAutoString postData;
     826             294 :   nsAutoString webPanel;
     827             294 :   nsAutoString micsumGenURI;
     828             294 :   nsAutoString generatedTitle;
     829             294 :   nsAutoString dateAdded;
     830             294 :   nsAutoString lastModified;
     831                 : 
     832             147 :   PRInt32 attrCount = node.GetAttributeCount();
     833             690 :   for (PRInt32 i = 0; i < attrCount; i++) {
     834             543 :     const nsAString& key = node.GetKeyAt(i);
     835            1086 :     nsAutoString value(node.GetValueAt(i));
     836             543 :     value.Trim(kWhitespace);
     837                 : 
     838             543 :     if (key.LowerCaseEqualsLiteral(KEY_HREF_LOWER))
     839             147 :       href = value;
     840             396 :     else if (key.LowerCaseEqualsLiteral(KEY_FEEDURL_LOWER))
     841              11 :       feedUrl = value;
     842             385 :     else if (key.LowerCaseEqualsLiteral(KEY_ICON_LOWER))
     843              81 :       icon = value;
     844             304 :     else if (key.LowerCaseEqualsLiteral(KEY_ICON_URI_LOWER))
     845              36 :       iconUri = value;
     846             268 :     else if (key.LowerCaseEqualsLiteral(KEY_LASTCHARSET_LOWER))
     847              11 :       lastCharset = value;
     848             257 :     else if (key.LowerCaseEqualsLiteral(KEY_SHORTCUTURL_LOWER))
     849              11 :       keyword = value;
     850             246 :     else if (key.LowerCaseEqualsLiteral(KEY_POST_DATA_LOWER))
     851              11 :       postData = value;
     852             235 :     else if (key.LowerCaseEqualsLiteral(KEY_WEB_PANEL_LOWER))
     853              11 :       webPanel = value;
     854             224 :     else if (key.LowerCaseEqualsLiteral(KEY_MICSUM_GEN_URI_LOWER))
     855               0 :       micsumGenURI = value;
     856             224 :     else if (key.LowerCaseEqualsLiteral(KEY_GENERATED_TITLE_LOWER))
     857               0 :       generatedTitle = value;
     858             224 :     else if (key.LowerCaseEqualsLiteral(KEY_DATE_ADDED_LOWER))
     859              80 :       dateAdded = value;
     860             144 :     else if (key.LowerCaseEqualsLiteral(KEY_LAST_MODIFIED_LOWER))
     861              84 :       lastModified = value;
     862                 :   }
     863                 : 
     864                 :   // For feeds, get the feed URL.  If it is invalid, mPreviousFeed will be
     865                 :   // NULL and we'll create it as a normal bookmark.
     866             147 :   if (!feedUrl.IsEmpty()) {
     867              11 :     NS_NewURI(getter_AddRefs(frame.mPreviousFeed),
     868              22 :               NS_ConvertUTF16toUTF8(feedUrl), nsnull);
     869                 :   }
     870                 : 
     871                 :   // Ignore <a> tags that have no href.
     872             147 :   if (href.IsEmpty()) {
     873               0 :     frame.mPreviousLink = nsnull;
     874                 :     // The exception is for feeds, where the href is an optional component
     875                 :     // indicating the source web site.
     876               0 :     if (!frame.mPreviousFeed)
     877                 :       return;
     878                 :   }
     879                 :   else {
     880                 :     // Save the address if it's valid.  Note that we ignore errors if this is a
     881                 :     // feed since href is optional for them.
     882             147 :     nsresult rv = NS_NewURI(getter_AddRefs(frame.mPreviousLink), href, nsnull);
     883             147 :     if (NS_FAILED(rv) && !frame.mPreviousFeed) {
     884               1 :       frame.mPreviousLink = nsnull;
     885                 :       return;
     886                 :     }
     887                 :   }
     888                 : 
     889                 :   // Save bookmark's last modified date.
     890             146 :   if (!lastModified.IsEmpty()) {
     891                 :     frame.mPreviousLastModifiedDate =
     892              84 :       ConvertImportedDateToInternalDate(NS_ConvertUTF16toUTF8(lastModified));
     893                 :   }
     894                 : 
     895                 :   // If this is a live bookmark, we will handle it in HandleLinkEnd(), so we
     896                 :   // can skip bookmark creation.
     897             146 :   if (frame.mPreviousFeed)
     898                 :     return;
     899                 : 
     900                 :   // Create the bookmark.  The title is unknown for now, we will set it later.
     901             135 :   nsresult rv = mBookmarksService->InsertBookmark(frame.mContainerID,
     902                 :                                                   frame.mPreviousLink,
     903             135 :                                                   mBookmarksService->DEFAULT_INDEX,
     904             135 :                                                   EmptyCString(),
     905             405 :                                                   &frame.mPreviousId);
     906             135 :   if (NS_FAILED(rv)) {
     907                 :     // If inserting bookmark failed, there's nothing more we can do.
     908               0 :     NS_WARNING("InserBookmark failed");
     909                 :     return;
     910                 :   }
     911                 :   
     912                 :   // Set the date added value, if we have it.
     913             135 :   if (!dateAdded.IsEmpty()) {
     914                 :     PRTime convertedDateAdded =
     915              80 :       ConvertImportedDateToInternalDate(NS_ConvertUTF16toUTF8(dateAdded));
     916              80 :     if (convertedDateAdded) {
     917              80 :       rv = mBookmarksService->SetItemDateAdded(frame.mPreviousId, convertedDateAdded);
     918              80 :       NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemDateAdded failed");
     919                 :     }
     920                 :   }
     921                 : 
     922                 :   // Save the favicon.
     923             135 :   if (!icon.IsEmpty() || !iconUri.IsEmpty()) {
     924             162 :     nsCOMPtr<nsIURI> iconUriObject;
     925              81 :     rv = NS_NewURI(getter_AddRefs(iconUriObject), iconUri);
     926              81 :     if (!icon.IsEmpty() || NS_SUCCEEDED(rv)) {
     927              81 :       rv = SetFaviconForURI(frame.mPreviousLink, iconUriObject, icon);
     928              81 :       if (NS_FAILED(rv)) {
     929               0 :         nsCAutoString warnMsg;
     930               0 :         warnMsg.Append("Bookmarks Import: unable to set favicon '");
     931               0 :         warnMsg.Append(NS_ConvertUTF16toUTF8(iconUri));
     932               0 :         warnMsg.Append("' for page '");
     933               0 :         nsCAutoString spec;
     934               0 :         rv = frame.mPreviousLink->GetSpec(spec);
     935               0 :         if (NS_SUCCEEDED(rv))
     936               0 :           warnMsg.Append(spec);
     937               0 :         warnMsg.Append("'");
     938               0 :         NS_WARNING(warnMsg.get());
     939                 :       }
     940                 :     }
     941                 :   }
     942                 : 
     943                 :   // Save the keyword.
     944             135 :   if (!keyword.IsEmpty()) {
     945              11 :     rv = mBookmarksService->SetKeywordForBookmark(frame.mPreviousId, keyword);
     946              11 :     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetKeywordForBookmark failed");
     947              11 :     if (NS_SUCCEEDED(rv) && !postData.IsEmpty()) {
     948              11 :       rv = mAnnotationService->SetItemAnnotationString(frame.mPreviousId,
     949              11 :                                                        POST_DATA_ANNO,
     950                 :                                                        postData, 0,
     951              11 :                                                        nsIAnnotationService::EXPIRE_NEVER);
     952              11 :       NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemAnnotationString failed");
     953                 :     }
     954                 :   }
     955                 : 
     956                 :   // Set load-in-sidebar annotation for the bookmark.
     957             135 :   if (webPanel.LowerCaseEqualsLiteral("true")) {
     958                 : 
     959              11 :     rv = mAnnotationService->SetItemAnnotationInt32(frame.mPreviousId,
     960              11 :                                                     LOAD_IN_SIDEBAR_ANNO,
     961                 :                                                     1, 0,
     962              11 :                                                     nsIAnnotationService::EXPIRE_NEVER);
     963              11 :     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemAnnotationInt32 failed");
     964                 :   }
     965                 : 
     966                 :   // Import last charset.
     967             135 :   if (!lastCharset.IsEmpty()) {
     968              11 :     rv = mHistoryService->SetCharsetForURI(frame.mPreviousLink,lastCharset);
     969              11 :     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "setCharsetForURI failed");
     970                 :   }
     971                 : }
     972                 : 
     973                 : 
     974                 : // BookmarkContentSink::HandleLinkEnd
     975                 : //
     976                 : //    Saves the title for the given bookmark. This only writes the user title.
     977                 : //    Any previous title will be untouched. If this is a new entry, it will have
     978                 : //    an empty "official" title until you visit it.
     979                 : 
     980                 : void
     981             147 : BookmarkContentSink::HandleLinkEnd()
     982                 : {
     983                 :   nsresult rv;
     984             147 :   BookmarkImportFrame& frame = CurFrame();
     985             147 :   frame.mPreviousText.Trim(kWhitespace);
     986                 : 
     987             147 :   if (frame.mPreviousFeed) {
     988                 :     // The is a live bookmark.  We create it here since in HandleLinkBegin we
     989                 :     // don't know the title.
     990                 :     jsval livemark = livemarkInfoToJSVal(
     991              11 :       0, EmptyCString(), frame.mPreviousText, frame.mContainerID,
     992              11 :       mBookmarksService->DEFAULT_INDEX, frame.mPreviousFeed, frame.mPreviousLink
     993              33 :     );
     994                 : 
     995                 :     // Create the live bookmark.
     996              11 :     rv = mLivemarkService->AddLivemark(livemark, nsnull);
     997              11 :     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "AddLivemark failed!");
     998                 : 
     999                 : #ifdef DEBUG_IMPORT
    1000                 :     PrintNesting();
    1001                 :     printf("Created livemark '%s'\n",
    1002                 :            NS_ConvertUTF16toUTF8(frame.mPreviousText).get());
    1003                 : #endif
    1004                 :   }
    1005             136 :   else if (frame.mPreviousLink) {
    1006                 :     // This is a common bookmark.
    1007                 : #ifdef DEBUG_IMPORT
    1008                 :     PrintNesting();
    1009                 :     printf("Created bookmark '%s' %lld\n",
    1010                 :            NS_ConvertUTF16toUTF8(frame.mPreviousText).get(), frame.mPreviousId);
    1011                 : #endif
    1012             135 :     rv = mBookmarksService->SetItemTitle(frame.mPreviousId,
    1013             135 :                                          NS_ConvertUTF16toUTF8(frame.mPreviousText));
    1014             135 :     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemTitle failed");
    1015                 :   }
    1016                 : 
    1017                 :   // Set last modified date as the last change.
    1018             147 :   if (frame.mPreviousId > 0 && frame.mPreviousLastModifiedDate > 0) {
    1019              81 :     rv = mBookmarksService->SetItemLastModified(frame.mPreviousId,
    1020              81 :                                                 frame.mPreviousLastModifiedDate);
    1021              81 :     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemLastModified failed");
    1022                 :     // Note: don't clear mPreviousLastModifiedDate, because if this item has a
    1023                 :     // description, we'll need to set it again.
    1024                 :   }
    1025                 : 
    1026             147 :   frame.mPreviousText.Truncate();
    1027             147 : }
    1028                 : 
    1029                 : 
    1030                 : // BookmarkContentSink::HandleSeparator
    1031                 : //
    1032                 : //    Inserts a separator into the current container
    1033                 : void
    1034               0 : BookmarkContentSink::HandleSeparator(const nsIParserNode& aNode)
    1035                 : {
    1036               0 :   BookmarkImportFrame& frame = CurFrame();
    1037                 : 
    1038                 :   // create the separator
    1039                 : 
    1040                 : #ifdef DEBUG_IMPORT
    1041                 :   PrintNesting();
    1042                 :   printf("--------\n");
    1043                 : #endif
    1044                 : 
    1045               0 :   nsresult rv = mBookmarksService->InsertSeparator(frame.mContainerID,
    1046               0 :                                                    mBookmarksService->DEFAULT_INDEX,
    1047               0 :                                                    &frame.mPreviousId);
    1048               0 :   if (NS_FAILED(rv)) {
    1049               0 :     NS_WARNING("InsertSeparator failed");
    1050               0 :     return;
    1051                 :   }
    1052                 :   // Import separator title if set.
    1053                 :   // Note that Places does not use separator titles, nor backup/restore them.
    1054               0 :   PRInt32 attrCount = aNode.GetAttributeCount();
    1055               0 :   for (PRInt32 i = 0; i < attrCount; i ++) {
    1056               0 :     const nsAString& key = aNode.GetKeyAt(i);
    1057                 : 
    1058               0 :     if (key.LowerCaseEqualsLiteral(KEY_NAME_LOWER)) {
    1059               0 :       nsAutoString name;
    1060               0 :       name = aNode.GetValueAt(i);
    1061               0 :       name.Trim(kWhitespace);
    1062               0 :       if (!name.IsEmpty()) {
    1063               0 :         rv = mBookmarksService->SetItemTitle(frame.mPreviousId,
    1064               0 :                                              NS_ConvertUTF16toUTF8(name));
    1065               0 :         NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemTitle failed");
    1066                 :       }
    1067                 :     }
    1068                 :   }
    1069                 : 
    1070                 :   // Note: we do not need to import ADD_DATE or LAST_MODIFIED for separators
    1071                 :   // because pre-Places bookmarks does not support them.
    1072                 :   // and we can't write them out because attributes other than NAME
    1073                 :   // will make Firefox 2.x crash/hang due to bug #381129
    1074                 : }
    1075                 : 
    1076                 : 
    1077                 : // BookmarkContentSink::NewFrame
    1078                 : //
    1079                 : //    This is called when there is a new folder found. The folder takes the
    1080                 : //    name from the previous frame's heading.
    1081                 : 
    1082                 : nsresult
    1083              61 : BookmarkContentSink::NewFrame()
    1084                 : {
    1085                 :   nsresult rv;
    1086                 : 
    1087              61 :   PRInt64 ourID = 0;
    1088             122 :   nsString containerName;
    1089                 :   BookmarkImportFrame::ContainerType containerType;
    1090              61 :   BookmarkImportFrame& frame = CurFrame();
    1091              61 :   frame.ConsumeHeading(&containerName, &containerType);
    1092                 : 
    1093              61 :   bool updateFolder = false;
    1094                 :   
    1095              61 :   switch (containerType) {
    1096                 :     case BookmarkImportFrame::Container_Normal:
    1097                 :       // append a new folder
    1098              64 :       rv = mBookmarksService->CreateFolder(CurFrame().mContainerID,
    1099              32 :                                            NS_ConvertUTF16toUTF8(containerName),
    1100              32 :                                            mBookmarksService->DEFAULT_INDEX, 
    1101              96 :                                            &ourID);
    1102              32 :       NS_ENSURE_SUCCESS(rv, rv);
    1103              32 :       break;
    1104                 :     case BookmarkImportFrame::Container_Places:
    1105                 :       // places root, never reparent here, when we're building the initial
    1106                 :       // hierarchy, it will only be defined at the top level
    1107               0 :       rv = mBookmarksService->GetPlacesRoot(&ourID);
    1108               0 :       NS_ENSURE_SUCCESS(rv, rv);
    1109               0 :       break;
    1110                 :     case BookmarkImportFrame::Container_Menu:
    1111                 :       // menu folder
    1112               0 :       rv = mBookmarksService->GetBookmarksMenuFolder(&ourID);
    1113               0 :       NS_ENSURE_SUCCESS(rv, rv);
    1114               0 :       if (mAllowRootChanges)
    1115               0 :         updateFolder = true;
    1116               0 :       break;
    1117                 :     case BookmarkImportFrame::Container_Unfiled:
    1118                 :       // unfiled bookmarks folder
    1119               9 :       rv = mBookmarksService->GetUnfiledBookmarksFolder(&ourID);
    1120               9 :       NS_ENSURE_SUCCESS(rv, rv);
    1121               9 :       if (mAllowRootChanges)
    1122               0 :         updateFolder = true;
    1123               9 :       break;
    1124                 :     case BookmarkImportFrame::Container_Toolbar:
    1125                 :       // get toolbar folder
    1126              20 :       rv = mBookmarksService->GetToolbarFolder(&ourID);
    1127              20 :       NS_ENSURE_SUCCESS(rv, rv);
    1128                 :       
    1129              20 :       break;
    1130                 :     default:
    1131               0 :       NS_NOTREACHED("Unknown container type");
    1132                 :   }
    1133                 : 
    1134                 : #ifdef DEBUG_IMPORT
    1135                 :   PrintNesting();
    1136                 :   printf("Folder %lld \'%s\'", ourID, NS_ConvertUTF16toUTF8(containerName).get());
    1137                 : #endif
    1138                 : 
    1139              61 :   if (updateFolder) {
    1140                 :     // move the menu folder to the current position
    1141               0 :     rv = mBookmarksService->MoveItem(ourID, CurFrame().mContainerID, -1);
    1142               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1143               0 :     rv = mBookmarksService->SetItemTitle(ourID, NS_ConvertUTF16toUTF8(containerName));
    1144               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1145                 : #ifdef DEBUG_IMPORT
    1146                 :     printf(" [reparenting]");
    1147                 : #endif
    1148                 :   }
    1149                 : 
    1150                 : #ifdef DEBUG_IMPORT
    1151                 :   printf("\n");
    1152                 : #endif
    1153                 : 
    1154              61 :   if (frame.mPreviousDateAdded > 0) {
    1155              37 :     rv = mBookmarksService->SetItemDateAdded(ourID, frame.mPreviousDateAdded);
    1156              37 :     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemDateAdded failed");
    1157              37 :     frame.mPreviousDateAdded = 0;
    1158                 :   }
    1159              61 :   if (frame.mPreviousLastModifiedDate > 0) {
    1160              45 :     rv = mBookmarksService->SetItemLastModified(ourID, frame.mPreviousLastModifiedDate);
    1161              45 :     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemLastModified failed");
    1162                 :     // don't clear last-modified, in case there's a description
    1163                 :   }
    1164                 : 
    1165              61 :   frame.mPreviousId = ourID;
    1166                 : 
    1167              61 :   if (!mFrames.AppendElement(BookmarkImportFrame(ourID)))
    1168               0 :     return NS_ERROR_OUT_OF_MEMORY;
    1169                 : 
    1170              61 :   return NS_OK;
    1171                 : }
    1172                 : 
    1173                 : 
    1174                 : nsresult
    1175              61 : BookmarkContentSink::PopFrame()
    1176                 : {
    1177                 :   // we must always have one frame
    1178              61 :   if (mFrames.Length() <= 1) {
    1179               0 :     NS_NOTREACHED("Trying to complete more bookmark folders than you started");
    1180               0 :     return NS_ERROR_FAILURE;
    1181                 :   }
    1182              61 :   mFrames.RemoveElementAt(mFrames.Length() - 1);
    1183              61 :   return NS_OK;
    1184                 : }
    1185                 : 
    1186                 : 
    1187                 : // BookmarkContentSink::SetFaviconForURI
    1188                 : //
    1189                 : //    aData is a string that is a data URI for the favicon. Our job is to
    1190                 : //    decode it and store it in the favicon service.
    1191                 : //
    1192                 : //    When aIconURI is non-null, we will use that as the URI of the favicon
    1193                 : //    when storing in the favicon service.
    1194                 : //
    1195                 : //    When aIconURI is null, we have to make up a URI for this favicon so that
    1196                 : //    it can be stored in the service. The real one will be set the next time
    1197                 : //    the user visits the page. Our made up one should get expired when the
    1198                 : //    page no longer references it.
    1199                 : nsresult
    1200              81 : BookmarkContentSink::SetFaviconForURI(nsIURI* aPageURI, nsIURI* aIconURI,
    1201                 :                                       const nsString& aData)
    1202                 : {
    1203                 :   nsresult rv;
    1204                 :   static PRUint32 serialNumber = 0; // for made-up favicon URIs
    1205                 : 
    1206                 :   nsCOMPtr<nsIFaviconService> faviconService =
    1207             162 :     do_GetService(NS_FAVICONSERVICE_CONTRACTID);
    1208              81 :   NS_ENSURE_TRUE(faviconService, NS_ERROR_OUT_OF_MEMORY);
    1209                 : 
    1210                 :   // if the input favicon URI is a chrome: URI, then we just save it and don't
    1211                 :   // worry about data
    1212              81 :   if (aIconURI) {
    1213              72 :     nsCString faviconScheme;
    1214              36 :     aIconURI->GetScheme(faviconScheme);
    1215              36 :     if (faviconScheme.EqualsLiteral("chrome")) {
    1216               0 :       return faviconService->SetFaviconUrlForPage(aPageURI, aIconURI);
    1217                 :     }
    1218                 :   }
    1219                 : 
    1220                 :   // some bookmarks have placeholder URIs that contain just "data:"
    1221                 :   // ignore these
    1222              81 :   if (aData.Length() <= 5)
    1223               1 :     return NS_OK;
    1224                 : 
    1225             160 :   nsCOMPtr<nsIURI> faviconURI;
    1226              80 :   if (aIconURI) {
    1227              35 :     faviconURI = aIconURI;
    1228                 :   }
    1229                 :   else {
    1230                 :     // make up favicon URL
    1231              90 :     nsCAutoString faviconSpec;
    1232              45 :     faviconSpec.AssignLiteral("http://www.mozilla.org/2005/made-up-favicon/");
    1233              45 :     faviconSpec.AppendInt(serialNumber);
    1234              45 :     faviconSpec.AppendLiteral("-");
    1235                 :     char buf[32];
    1236              45 :     PR_snprintf(buf, sizeof(buf), "%lld", PR_Now());
    1237              45 :     faviconSpec.Append(buf);
    1238              45 :     rv = NS_NewURI(getter_AddRefs(faviconURI), faviconSpec);
    1239              45 :     if (NS_FAILED(rv)) {
    1240               0 :       nsCAutoString warnMsg;
    1241               0 :       warnMsg.Append("Bookmarks Import: Unable to make up new favicon '");
    1242               0 :       warnMsg.Append(faviconSpec);
    1243               0 :       warnMsg.Append("' for page '");
    1244               0 :       nsCAutoString spec;
    1245               0 :       rv = aPageURI->GetSpec(spec);
    1246               0 :       if (NS_SUCCEEDED(rv))
    1247               0 :         warnMsg.Append(spec);
    1248               0 :       warnMsg.Append("'");
    1249               0 :       NS_WARNING(warnMsg.get());
    1250               0 :       return NS_OK;
    1251                 :     }
    1252              90 :     serialNumber++;
    1253                 :   }
    1254                 : 
    1255                 :   // save the favicon data
    1256                 :   // This could fail if the favicon is bigger than defined limit, in such a
    1257                 :   // case data will not be saved to the db but we will still continue.
    1258              80 :   (void) faviconService->SetFaviconDataFromDataURL(faviconURI, aData, 0);
    1259                 : 
    1260              80 :   rv = faviconService->SetFaviconUrlForPage(aPageURI, faviconURI);
    1261              80 :   NS_ENSURE_SUCCESS(rv, rv);
    1262                 : 
    1263              80 :   return NS_OK; 
    1264                 : }
    1265                 : 
    1266                 : 
    1267                 : // Converts a string date in seconds to an int date in microseconds
    1268                 : PRTime
    1269             242 : BookmarkContentSink::ConvertImportedDateToInternalDate(const nsACString& aDate) {
    1270             242 :   PRTime convertedDate = 0;
    1271             242 :   if (!aDate.IsEmpty()) {
    1272                 :     nsresult rv;
    1273             242 :     convertedDate = PromiseFlatCString(aDate).ToInteger(&rv);
    1274             242 :     if (NS_SUCCEEDED(rv)) {
    1275             242 :       convertedDate *= 1000000; // in bookmarks.html this value is in seconds, not microseconds
    1276                 :     }
    1277                 :     else {
    1278               0 :       convertedDate = 0;
    1279                 :     }
    1280                 :   }
    1281             242 :   return convertedDate;
    1282                 : }
    1283                 : 
    1284                 : 
    1285                 : // SyncChannelStatus
    1286                 : //
    1287                 : //    If a function returns an error, we need to set the channel status to be
    1288                 : //    the same, but only if the channel doesn't have its own error. This returns
    1289                 : //    the error code that should be sent to OnStopRequest.
    1290                 : static nsresult
    1291              53 : SyncChannelStatus(nsIChannel* channel, nsresult status)
    1292                 : {
    1293                 :   nsresult channelStatus;
    1294              53 :   channel->GetStatus(&channelStatus);
    1295              53 :   if (NS_FAILED(channelStatus))
    1296               0 :     return channelStatus;
    1297                 : 
    1298              53 :   if (NS_SUCCEEDED(status))
    1299              53 :     return NS_OK; // caller and the channel are happy
    1300                 : 
    1301                 :   // channel was OK, but caller wasn't: set the channel state
    1302               0 :   channel->Cancel(status);
    1303               0 :   return status;
    1304                 : }
    1305                 : 
    1306                 : 
    1307                 : static char kFileIntro[] =
    1308                 :     "<!DOCTYPE NETSCAPE-Bookmark-file-1>" NS_LINEBREAK
    1309                 :     // Note: we write bookmarks in UTF-8
    1310                 :     "<!-- This is an automatically generated file." NS_LINEBREAK
    1311                 :     "     It will be read and overwritten." NS_LINEBREAK
    1312                 :     "     DO NOT EDIT! -->" NS_LINEBREAK
    1313                 :     "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">" NS_LINEBREAK
    1314                 :     "<TITLE>Bookmarks</TITLE>" NS_LINEBREAK;
    1315                 : static const char kRootIntro[] = "<H1";
    1316                 : static const char kCloseRootH1[] = "</H1>" NS_LINEBREAK NS_LINEBREAK;
    1317                 : 
    1318                 : static const char kBookmarkIntro[] = "<DL><p>" NS_LINEBREAK;
    1319                 : static const char kBookmarkClose[] = "</DL><p>" NS_LINEBREAK;
    1320                 : static const char kContainerIntro[] = "<DT><H3";
    1321                 : static const char kContainerClose[] = "</H3>" NS_LINEBREAK;
    1322                 : static const char kItemOpen[] = "<DT><A";
    1323                 : static const char kItemClose[] = "</A>" NS_LINEBREAK;
    1324                 : static const char kSeparator[] = "<HR";
    1325                 : static const char kQuoteStr[] = "\"";
    1326                 : static const char kCloseAngle[] = ">";
    1327                 : static const char kIndent[] = "    ";
    1328                 : static const char kDescriptionIntro[] = "<DD>";
    1329                 : static const char kDescriptionClose[] = NS_LINEBREAK;
    1330                 : 
    1331                 : static const char kPlacesRootAttribute[] = " PLACES_ROOT=\"true\"";
    1332                 : static const char kBookmarksRootAttribute[] = " BOOKMARKS_MENU=\"true\"";
    1333                 : static const char kToolbarFolderAttribute[] = " PERSONAL_TOOLBAR_FOLDER=\"true\"";
    1334                 : static const char kUnfiledBookmarksFolderAttribute[] = " UNFILED_BOOKMARKS_FOLDER=\"true\"";
    1335                 : static const char kIconAttribute[] = " ICON=\"";
    1336                 : static const char kIconURIAttribute[] = " ICON_URI=\"";
    1337                 : static const char kHrefAttribute[] = " HREF=\"";
    1338                 : static const char kFeedURIAttribute[] = " FEEDURL=\"";
    1339                 : static const char kWebPanelAttribute[] = " WEB_PANEL=\"true\"";
    1340                 : static const char kKeywordAttribute[] = " SHORTCUTURL=\"";
    1341                 : static const char kPostDataAttribute[] = " POST_DATA=\"";
    1342                 : static const char kNameAttribute[] = " NAME=\"";
    1343                 : static const char kMicsumGenURIAttribute[]    = " MICSUM_GEN_URI=\"";
    1344                 : static const char kDateAddedAttribute[] = " ADD_DATE=\"";
    1345                 : static const char kLastModifiedAttribute[] = " LAST_MODIFIED=\"";
    1346                 : static const char kLastCharsetAttribute[] = " LAST_CHARSET=\"";
    1347                 : 
    1348                 : 
    1349                 : // WriteContainerPrologue
    1350                 : //
    1351                 : //    <DL><p>
    1352                 : //
    1353                 : //    Goes after the container header (<H3...) but before the contents
    1354                 : static nsresult
    1355              32 : WriteContainerPrologue(const nsACString& aIndent, nsIOutputStream* aOutput)
    1356                 : {
    1357                 :   PRUint32 dummy;
    1358              32 :   nsresult rv = aOutput->Write(PromiseFlatCString(aIndent).get(), aIndent.Length(), &dummy);
    1359              32 :   NS_ENSURE_SUCCESS(rv, rv);
    1360              32 :   rv = aOutput->Write(kBookmarkIntro, sizeof(kBookmarkIntro)-1, &dummy);
    1361              32 :   NS_ENSURE_SUCCESS(rv, rv);
    1362              32 :   return NS_OK;
    1363                 : }
    1364                 : 
    1365                 : 
    1366                 : // WriteContainerEpilogue
    1367                 : //
    1368                 : //    </DL><p>
    1369                 : //
    1370                 : //    Goes after the container contents to close the container
    1371                 : static nsresult
    1372              32 : WriteContainerEpilogue(const nsACString& aIndent, nsIOutputStream* aOutput)
    1373                 : {
    1374                 :   PRUint32 dummy;
    1375              32 :   nsresult rv = aOutput->Write(PromiseFlatCString(aIndent).get(), aIndent.Length(), &dummy);
    1376              32 :   NS_ENSURE_SUCCESS(rv, rv);
    1377              32 :   rv = aOutput->Write(kBookmarkClose, sizeof(kBookmarkClose)-1, &dummy);
    1378              32 :   NS_ENSURE_SUCCESS(rv, rv);
    1379              32 :   return NS_OK;
    1380                 : }
    1381                 : 
    1382                 : 
    1383                 : // WriteFaviconAttribute
    1384                 : //
    1385                 : //    This writes the 'ICON="data:asdlfkjas;ldkfja;skdljfasdf"' attribute for
    1386                 : //    an item. We special-case chrome favicon URIs by just writing the chrome:
    1387                 : //    URI.
    1388                 : static nsresult
    1389              56 : WriteFaviconAttribute(const nsACString& aURI, nsIOutputStream* aOutput)
    1390                 : {
    1391                 :   PRUint32 dummy;
    1392                 : 
    1393                 :   // if favicon uri is invalid we skip the attribute silently, to avoid
    1394                 :   // creating a corrupt file.
    1395             112 :   nsCOMPtr<nsIURI> uri;
    1396              56 :   nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI);
    1397              56 :   if (NS_FAILED(rv)) {
    1398               0 :     nsCAutoString warnMsg;
    1399               0 :     warnMsg.Append("Bookmarks Export: Found invalid favicon '");
    1400               0 :     warnMsg.Append(aURI);
    1401               0 :     warnMsg.Append("'");
    1402               0 :     NS_WARNING(warnMsg.get());
    1403               0 :     return NS_OK;
    1404                 :   }
    1405                 : 
    1406                 :   // get favicon
    1407             112 :   nsCOMPtr<nsIFaviconService> faviconService = do_GetService(NS_FAVICONSERVICE_CONTRACTID, &rv);
    1408              56 :   NS_ENSURE_SUCCESS(rv, rv);
    1409             112 :   nsCOMPtr<nsIURI> faviconURI;
    1410              56 :   rv = faviconService->GetFaviconForPage(uri, getter_AddRefs(faviconURI));
    1411              56 :   if (rv == NS_ERROR_NOT_AVAILABLE)
    1412              30 :     return NS_OK; // no favicon
    1413              26 :   NS_ENSURE_SUCCESS(rv, rv); // anything else is error
    1414                 : 
    1415              52 :   nsCAutoString faviconScheme;
    1416              52 :   nsCAutoString faviconSpec;
    1417              26 :   rv = faviconURI->GetSpec(faviconSpec);
    1418              26 :   NS_ENSURE_SUCCESS(rv, rv);
    1419              26 :   rv = faviconURI->GetScheme(faviconScheme);
    1420              26 :   NS_ENSURE_SUCCESS(rv, rv);
    1421                 : 
    1422                 :   // write favicon URI: 'ICON_URI="..."'
    1423              26 :   rv = aOutput->Write(kIconURIAttribute, sizeof(kIconURIAttribute)-1, &dummy);
    1424              26 :   NS_ENSURE_SUCCESS(rv, rv);
    1425              26 :   rv = WriteEscapedUrl(faviconSpec, aOutput);
    1426              26 :   NS_ENSURE_SUCCESS(rv, rv);
    1427              26 :   rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
    1428              26 :   NS_ENSURE_SUCCESS(rv, rv);
    1429                 : 
    1430              26 :   if (!faviconScheme.EqualsLiteral("chrome")) {
    1431                 :     // only store data for non-chrome URIs
    1432                 : 
    1433              52 :     nsAutoString faviconContents;
    1434              26 :     rv = faviconService->GetFaviconDataAsDataURL(faviconURI, faviconContents);
    1435              26 :     NS_ENSURE_SUCCESS(rv, rv);
    1436              26 :     if (faviconContents.Length() > 0) {
    1437              25 :       rv = aOutput->Write(kIconAttribute, sizeof(kIconAttribute)-1, &dummy);
    1438              25 :       NS_ENSURE_SUCCESS(rv, rv);
    1439              50 :       NS_ConvertUTF16toUTF8 utf8Favicon(faviconContents);
    1440              25 :       rv = aOutput->Write(utf8Favicon.get(), utf8Favicon.Length(), &dummy);
    1441              25 :       NS_ENSURE_SUCCESS(rv, rv);
    1442              25 :       rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
    1443              25 :       NS_ENSURE_SUCCESS(rv, rv);
    1444                 :     }
    1445                 :   }
    1446              26 :   return NS_OK;
    1447                 : }
    1448                 : 
    1449                 : 
    1450                 : // WriteDateAttribute
    1451                 : //
    1452                 : //    This writes the '{attr value=}"{time in seconds}"' attribute for
    1453                 : //    an item.
    1454                 : static nsresult
    1455             156 : WriteDateAttribute(const char aAttributeStart[], PRInt32 aLength, PRTime aAttributeValue, nsIOutputStream* aOutput)
    1456                 : {
    1457                 :   // write attribute start
    1458                 :   PRUint32 dummy;
    1459             156 :   nsresult rv = aOutput->Write(aAttributeStart, aLength, &dummy);
    1460             156 :   NS_ENSURE_SUCCESS(rv, rv);
    1461                 : 
    1462                 :   // in bookmarks.html this value is in seconds, not microseconds
    1463             156 :   aAttributeValue /= 1000000; 
    1464                 : 
    1465                 :   // write attribute value
    1466                 :   char dateInSeconds[32];
    1467             156 :   PR_snprintf(dateInSeconds, sizeof(dateInSeconds), "%lld", aAttributeValue);
    1468             156 :   rv = aOutput->Write(dateInSeconds, strlen(dateInSeconds), &dummy);
    1469             156 :   NS_ENSURE_SUCCESS(rv, rv);
    1470             156 :   rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
    1471             156 :   NS_ENSURE_SUCCESS(rv, rv);
    1472                 : 
    1473             156 :   return NS_OK;
    1474                 : }
    1475                 : 
    1476                 : 
    1477                 : // nsPlacesImportExportService::WriteContainer
    1478                 : //
    1479                 : //    Writes out all the necessary parts of a bookmarks folder.
    1480                 : nsresult
    1481              22 : nsPlacesImportExportService::WriteContainer(nsINavHistoryResultNode* aFolder,
    1482                 :                                             const nsACString& aIndent,
    1483                 :                                             nsIOutputStream* aOutput)
    1484                 : {
    1485              22 :   nsresult rv = WriteContainerHeader(aFolder, aIndent, aOutput);
    1486              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1487              22 :   rv = WriteContainerPrologue(aIndent, aOutput);
    1488              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1489              22 :   rv = WriteContainerContents(aFolder, aIndent, aOutput);
    1490              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1491              22 :   rv = WriteContainerEpilogue(aIndent, aOutput);
    1492              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1493              22 :   return NS_OK;
    1494                 : }
    1495                 : 
    1496                 : 
    1497                 : // nsPlacesImportExportService::WriteContainerHeader
    1498                 : //
    1499                 : //    This writes '<DL><H3>Title</H3>'
    1500                 : //    Remember folders can also have favicons, which we put in the H3 tag
    1501                 : nsresult
    1502              22 : nsPlacesImportExportService::WriteContainerHeader(nsINavHistoryResultNode* aFolder,
    1503                 :                                                   const nsACString& aIndent,
    1504                 :                                                   nsIOutputStream* aOutput)
    1505                 : {
    1506                 :   PRUint32 dummy;
    1507                 :   nsresult rv;
    1508                 : 
    1509                 :   // indent
    1510              22 :   if (!aIndent.IsEmpty()) {
    1511              22 :     rv = aOutput->Write(PromiseFlatCString(aIndent).get(), aIndent.Length(), &dummy);
    1512              22 :     NS_ENSURE_SUCCESS(rv, rv);
    1513                 :   }
    1514                 : 
    1515                 :   // "<DL H3"
    1516              22 :   rv = aOutput->Write(kContainerIntro, sizeof(kContainerIntro)-1, &dummy);
    1517              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1518                 : 
    1519                 :   // get folder id
    1520                 :   PRInt64 folderId;
    1521              22 :   rv = aFolder->GetItemId(&folderId);
    1522              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1523                 : 
    1524                 :   // write ADD_DATE
    1525              22 :   PRTime dateAdded = 0;
    1526              22 :   rv = aFolder->GetDateAdded(&dateAdded);
    1527              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1528                 : 
    1529              22 :   if (dateAdded) {
    1530              22 :     rv = WriteDateAttribute(kDateAddedAttribute, sizeof(kDateAddedAttribute)-1, dateAdded, aOutput);
    1531              22 :     NS_ENSURE_SUCCESS(rv, rv);
    1532                 :   }
    1533                 : 
    1534                 :   // write LAST_MODIFIED
    1535              22 :   PRTime lastModified = 0;
    1536              22 :   rv = aFolder->GetLastModified(&lastModified);
    1537              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1538                 : 
    1539              22 :   if (lastModified) {
    1540              22 :     rv = WriteDateAttribute(kLastModifiedAttribute, sizeof(kLastModifiedAttribute)-1, lastModified, aOutput);
    1541              22 :     NS_ENSURE_SUCCESS(rv, rv);
    1542                 :   }
    1543                 : 
    1544                 :   PRInt64 placesRoot;
    1545              22 :   rv = mBookmarksService->GetPlacesRoot(&placesRoot);
    1546              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1547                 : 
    1548                 :   PRInt64 bookmarksMenuFolder;
    1549              22 :   rv = mBookmarksService->GetBookmarksMenuFolder(&bookmarksMenuFolder);
    1550              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1551                 : 
    1552                 :   PRInt64 toolbarFolder;
    1553              22 :   rv = mBookmarksService->GetToolbarFolder(&toolbarFolder);
    1554              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1555                 : 
    1556                 :   PRInt64 unfiledBookmarksFolder;
    1557              22 :   rv = mBookmarksService->GetUnfiledBookmarksFolder(&unfiledBookmarksFolder);
    1558              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1559                 : 
    1560                 :   // " PERSONAL_TOOLBAR_FOLDER="true"", etc.
    1561              22 :   if (folderId == placesRoot) {
    1562               0 :     rv = aOutput->Write(kPlacesRootAttribute, sizeof(kPlacesRootAttribute)-1, &dummy);
    1563               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1564                 :   }
    1565              22 :   else if (folderId == bookmarksMenuFolder) {
    1566               0 :     rv = aOutput->Write(kBookmarksRootAttribute, sizeof(kBookmarksRootAttribute)-1, &dummy);
    1567               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1568                 :   }
    1569              22 :   else if (folderId == unfiledBookmarksFolder) {
    1570               5 :     rv = aOutput->Write(kUnfiledBookmarksFolderAttribute, sizeof(kUnfiledBookmarksFolderAttribute)-1, &dummy);
    1571               5 :     NS_ENSURE_SUCCESS(rv, rv);
    1572                 :   }
    1573              17 :   else if (folderId == toolbarFolder) {
    1574               7 :     rv = aOutput->Write(kToolbarFolderAttribute, sizeof(kToolbarFolderAttribute)-1, &dummy);
    1575               7 :     NS_ENSURE_SUCCESS(rv, rv);
    1576                 :   }
    1577                 : 
    1578                 :   // ">"
    1579              22 :   rv = aOutput->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy);
    1580              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1581                 : 
    1582                 :   // title
    1583              22 :   rv = WriteTitle(aFolder, aOutput);
    1584              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1585                 : 
    1586                 :   // "</H3>\n"
    1587              22 :   rv = aOutput->Write(kContainerClose, sizeof(kContainerClose)-1, &dummy);
    1588              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1589                 : 
    1590                 :   // description
    1591              22 :   rv = WriteDescription(folderId, nsINavBookmarksService::TYPE_FOLDER, aOutput);
    1592              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1593                 : 
    1594              22 :   return rv;
    1595                 : }
    1596                 : 
    1597                 : 
    1598                 : // nsPlacesImportExportService::WriteTitle
    1599                 : //
    1600                 : //    Retrieves, escapes and writes the title to the stream.
    1601                 : nsresult
    1602              37 : nsPlacesImportExportService::WriteTitle(nsINavHistoryResultNode* aItem,
    1603                 :                                         nsIOutputStream* aOutput)
    1604                 : {
    1605                 :   // XXX Bug 381767 - support titles for separators
    1606              37 :   PRUint32 type = 0;
    1607              37 :   nsresult rv = aItem->GetType(&type);
    1608              37 :   NS_ENSURE_SUCCESS(rv, rv);
    1609              37 :   if (type == nsINavHistoryResultNode::RESULT_TYPE_SEPARATOR)
    1610               0 :     return NS_ERROR_INVALID_ARG;
    1611                 : 
    1612              74 :   nsCAutoString title;
    1613              37 :   rv = aItem->GetTitle(title);
    1614              37 :   NS_ENSURE_SUCCESS(rv, rv);
    1615                 : 
    1616              37 :   char* escapedTitle = nsEscapeHTML(title.get());
    1617              37 :   if (escapedTitle) {
    1618                 :     PRUint32 dummy;
    1619              37 :     rv = aOutput->Write(escapedTitle, strlen(escapedTitle), &dummy);
    1620              37 :     nsMemory::Free(escapedTitle);
    1621              37 :     NS_ENSURE_SUCCESS(rv, rv);
    1622                 :   }
    1623              37 :   return NS_OK;
    1624                 : }
    1625                 : 
    1626                 : 
    1627                 : // nsPlacesImportExportService::WriteDescription
    1628                 : //
    1629                 : //  Write description out for all item types.
    1630                 : nsresult
    1631              83 : nsPlacesImportExportService::WriteDescription(PRInt64 aItemId, PRInt32 aType,
    1632                 :                                               nsIOutputStream* aOutput)
    1633                 : {
    1634              83 :   bool hasDescription = false;
    1635              83 :   nsresult rv = mAnnotationService->ItemHasAnnotation(aItemId,
    1636              83 :                                                       DESCRIPTION_ANNO,
    1637              83 :                                                       &hasDescription);
    1638              83 :   if (NS_FAILED(rv) || !hasDescription)
    1639              67 :     return rv;
    1640                 : 
    1641              32 :   nsAutoString description;
    1642              32 :   rv = mAnnotationService->GetItemAnnotationString(aItemId, DESCRIPTION_ANNO,
    1643              16 :                                                    description);
    1644              16 :   NS_ENSURE_SUCCESS(rv, rv);
    1645                 : 
    1646              16 :   char* escapedDesc = nsEscapeHTML(NS_ConvertUTF16toUTF8(description).get());
    1647              16 :   if (escapedDesc) {
    1648                 :     PRUint32 dummy;
    1649              16 :     rv = aOutput->Write(kDescriptionIntro, sizeof(kDescriptionIntro)-1, &dummy);
    1650              16 :     if (NS_FAILED(rv)) {
    1651               0 :       nsMemory::Free(escapedDesc);
    1652               0 :       return rv;
    1653                 :     }
    1654              16 :     rv = aOutput->Write(escapedDesc, strlen(escapedDesc), &dummy);
    1655              16 :     nsMemory::Free(escapedDesc);
    1656              16 :     NS_ENSURE_SUCCESS(rv, rv);
    1657              16 :     rv = aOutput->Write(kDescriptionClose, sizeof(kDescriptionClose)-1, &dummy);
    1658              16 :     NS_ENSURE_SUCCESS(rv, rv);
    1659                 :   }
    1660              16 :   return NS_OK;
    1661                 : }
    1662                 : 
    1663                 : // nsBookmarks::WriteItem
    1664                 : //
    1665                 : //    "<DT><A HREF="..." ICON="...">Name</A>"
    1666                 : nsresult
    1667              57 : nsPlacesImportExportService::WriteItem(nsINavHistoryResultNode* aItem,
    1668                 :                                        const nsACString& aIndent,
    1669                 :                                        nsIOutputStream* aOutput)
    1670                 : {
    1671                 :   // before doing any attempt to write the item check that uri is valid, if the
    1672                 :   // item has a bad uri we skip it silently, otherwise we could stop while
    1673                 :   // exporting, generating a corrupt file.
    1674             114 :   nsCAutoString uri;
    1675              57 :   nsresult rv = aItem->GetUri(uri);
    1676              57 :   NS_ENSURE_SUCCESS(rv, rv);
    1677             114 :   nsCOMPtr<nsIURI> pageURI;
    1678              57 :   rv = NS_NewURI(getter_AddRefs(pageURI), uri, nsnull);
    1679              57 :   if (NS_FAILED(rv)) {
    1680               2 :     nsCAutoString warnMsg;
    1681               1 :     warnMsg.Append("Bookmarks Export: Found invalid item uri '");
    1682               1 :     warnMsg.Append(uri);
    1683               1 :     warnMsg.Append("'");
    1684               1 :     NS_WARNING(warnMsg.get());
    1685               1 :     return NS_OK;
    1686                 :   }
    1687                 : 
    1688                 :   // indent
    1689                 :   PRUint32 dummy;
    1690              56 :   if (!aIndent.IsEmpty()) {
    1691              56 :     rv = aOutput->Write(PromiseFlatCString(aIndent).get(), aIndent.Length(), &dummy);
    1692              56 :     NS_ENSURE_SUCCESS(rv, rv);
    1693                 :   }
    1694                 : 
    1695                 :   // '<DT><A'
    1696              56 :   rv = aOutput->Write(kItemOpen, sizeof(kItemOpen)-1, &dummy);
    1697              56 :   NS_ENSURE_SUCCESS(rv, rv);
    1698                 : 
    1699                 :   // ' HREF="http://..."' - note that we need to call GetURI on the result
    1700                 :   // node because some nodes (eg queries) generate this lazily.
    1701              56 :   rv = aOutput->Write(kHrefAttribute, sizeof(kHrefAttribute)-1, &dummy);
    1702              56 :   NS_ENSURE_SUCCESS(rv, rv);
    1703              56 :   rv = WriteEscapedUrl(uri, aOutput);
    1704              56 :   NS_ENSURE_SUCCESS(rv, rv);
    1705              56 :   rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
    1706              56 :   NS_ENSURE_SUCCESS(rv, rv);
    1707                 : 
    1708                 :   // write ADD_DATE
    1709              56 :   PRTime dateAdded = 0;
    1710              56 :   rv = aItem->GetDateAdded(&dateAdded);
    1711              56 :   NS_ENSURE_SUCCESS(rv, rv);
    1712                 : 
    1713              56 :   if (dateAdded) {
    1714              56 :     rv = WriteDateAttribute(kDateAddedAttribute, sizeof(kDateAddedAttribute)-1, dateAdded, aOutput);
    1715              56 :     NS_ENSURE_SUCCESS(rv, rv);
    1716                 :   }
    1717                 : 
    1718                 :   // write LAST_MODIFIED
    1719              56 :   PRTime lastModified = 0;
    1720              56 :   rv = aItem->GetLastModified(&lastModified);
    1721              56 :   NS_ENSURE_SUCCESS(rv, rv);
    1722                 : 
    1723              56 :   if (lastModified) {
    1724              56 :     rv = WriteDateAttribute(kLastModifiedAttribute, sizeof(kLastModifiedAttribute)-1, lastModified, aOutput);
    1725              56 :     NS_ENSURE_SUCCESS(rv, rv);
    1726                 :   }
    1727                 : 
    1728                 :   // ' ICON="..."'
    1729              56 :   rv = WriteFaviconAttribute(uri, aOutput);
    1730              56 :   NS_ENSURE_SUCCESS(rv, rv);
    1731                 : 
    1732                 :   // get item id 
    1733                 :   PRInt64 itemId;
    1734              56 :   rv = aItem->GetItemId(&itemId);
    1735              56 :   NS_ENSURE_SUCCESS(rv, rv);
    1736                 : 
    1737                 :   // keyword (shortcuturl)
    1738             112 :   nsAutoString keyword;
    1739              56 :   rv = mBookmarksService->GetKeywordForBookmark(itemId, keyword);
    1740              56 :   NS_ENSURE_SUCCESS(rv, rv);
    1741              56 :   if (!keyword.IsEmpty()) {
    1742               5 :     rv = aOutput->Write(kKeywordAttribute, sizeof(kKeywordAttribute)-1, &dummy);
    1743               5 :     NS_ENSURE_SUCCESS(rv, rv);
    1744               5 :     char* escapedKeyword = nsEscapeHTML(NS_ConvertUTF16toUTF8(keyword).get());
    1745               5 :     rv = aOutput->Write(escapedKeyword, strlen(escapedKeyword), &dummy);
    1746               5 :     nsMemory::Free(escapedKeyword);
    1747               5 :     NS_ENSURE_SUCCESS(rv, rv);
    1748               5 :     rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
    1749               5 :     NS_ENSURE_SUCCESS(rv, rv);
    1750                 :   }
    1751                 : 
    1752                 :   // post data
    1753                 :   bool hasPostData;
    1754             112 :   rv = mAnnotationService->ItemHasAnnotation(itemId, POST_DATA_ANNO,
    1755              56 :                                              &hasPostData);
    1756              56 :   NS_ENSURE_SUCCESS(rv, rv);
    1757              56 :   if (hasPostData) {
    1758              10 :     nsAutoString postData;
    1759              10 :     rv = mAnnotationService->GetItemAnnotationString(itemId, POST_DATA_ANNO,
    1760               5 :                                                      postData);
    1761               5 :     NS_ENSURE_SUCCESS(rv, rv);
    1762               5 :     rv = aOutput->Write(kPostDataAttribute, sizeof(kPostDataAttribute)-1, &dummy);
    1763               5 :     NS_ENSURE_SUCCESS(rv, rv);
    1764               5 :     char* escapedPostData = nsEscapeHTML(NS_ConvertUTF16toUTF8(postData).get());
    1765               5 :     rv = aOutput->Write(escapedPostData, strlen(escapedPostData), &dummy);
    1766               5 :     nsMemory::Free(escapedPostData);
    1767               5 :     NS_ENSURE_SUCCESS(rv, rv);
    1768               5 :     rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
    1769               5 :     NS_ENSURE_SUCCESS(rv, rv);
    1770                 :   }
    1771                 : 
    1772                 :   // Write WEB_PANEL="true" if the load-in-sidebar annotation is set for the
    1773                 :   // item
    1774              56 :   bool loadInSidebar = false;
    1775             112 :   rv = mAnnotationService->ItemHasAnnotation(itemId, LOAD_IN_SIDEBAR_ANNO,
    1776              56 :                                              &loadInSidebar);
    1777              56 :   NS_ENSURE_SUCCESS(rv, rv);
    1778              56 :   if (loadInSidebar)
    1779               5 :     aOutput->Write(kWebPanelAttribute, sizeof(kWebPanelAttribute)-1, &dummy);
    1780                 : 
    1781                 :   // last charset
    1782             112 :   nsAutoString lastCharset;
    1783             112 :   if (NS_SUCCEEDED(mHistoryService->GetCharsetForURI(pageURI, lastCharset)) &&
    1784              56 :       !lastCharset.IsEmpty()) {
    1785               5 :     rv = aOutput->Write(kLastCharsetAttribute, sizeof(kLastCharsetAttribute)-1, &dummy);
    1786               5 :     NS_ENSURE_SUCCESS(rv, rv);
    1787               5 :     char* escapedLastCharset = nsEscapeHTML(NS_ConvertUTF16toUTF8(lastCharset).get());
    1788               5 :     rv = aOutput->Write(escapedLastCharset, strlen(escapedLastCharset), &dummy);
    1789               5 :     nsMemory::Free(escapedLastCharset);
    1790               5 :     NS_ENSURE_SUCCESS(rv, rv);
    1791               5 :     rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
    1792               5 :     NS_ENSURE_SUCCESS(rv, rv);
    1793                 :   }
    1794                 : 
    1795                 :   // '>'
    1796              56 :   rv = aOutput->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy);
    1797              56 :   NS_ENSURE_SUCCESS(rv, rv);
    1798                 : 
    1799                 :   // title
    1800             112 :   nsCAutoString title;
    1801              56 :   rv = aItem->GetTitle(title);
    1802              56 :   NS_ENSURE_SUCCESS(rv, rv);
    1803              56 :   char* escapedTitle = nsEscapeHTML(title.get());
    1804              56 :   if (escapedTitle) {
    1805              56 :     rv = aOutput->Write(escapedTitle, strlen(escapedTitle), &dummy);
    1806              56 :     nsMemory::Free(escapedTitle);
    1807              56 :     NS_ENSURE_SUCCESS(rv, rv);
    1808                 :   }
    1809                 : 
    1810                 :   // '</A>\n'
    1811              56 :   rv = aOutput->Write(kItemClose, sizeof(kItemClose)-1, &dummy);
    1812              56 :   NS_ENSURE_SUCCESS(rv, rv);
    1813                 : 
    1814                 :   // description
    1815              56 :   rv = WriteDescription(itemId, nsINavBookmarksService::TYPE_BOOKMARK, aOutput);
    1816              56 :   NS_ENSURE_SUCCESS(rv, rv);
    1817                 : 
    1818              56 :   return NS_OK;
    1819                 : }
    1820                 : 
    1821                 : 
    1822                 : // WriteLivemark
    1823                 : //
    1824                 : //    Similar to WriteItem, this has an additional FEEDURL attribute and
    1825                 : //    the HREF is optional and points to the source page.
    1826                 : nsresult
    1827               5 : nsPlacesImportExportService::WriteLivemark(nsINavHistoryResultNode* aFolder, const nsACString& aIndent,
    1828                 :                               nsIOutputStream* aOutput)
    1829                 : {
    1830                 :   PRUint32 dummy;
    1831                 :   nsresult rv;
    1832                 : 
    1833                 :   // indent
    1834               5 :   if (!aIndent.IsEmpty()) {
    1835               5 :     rv = aOutput->Write(PromiseFlatCString(aIndent).get(), aIndent.Length(), &dummy);
    1836               5 :     NS_ENSURE_SUCCESS(rv, rv);
    1837                 :   }
    1838                 : 
    1839                 :   // '<DT><A'
    1840               5 :   rv = aOutput->Write(kItemOpen, sizeof(kItemOpen)-1, &dummy);
    1841               5 :   NS_ENSURE_SUCCESS(rv, rv);
    1842                 : 
    1843                 :   // get folder id
    1844                 :   PRInt64 folderId;
    1845               5 :   rv = aFolder->GetItemId(&folderId);
    1846               5 :   NS_ENSURE_SUCCESS(rv, rv);
    1847                 : 
    1848                 :   // get feed URI
    1849              10 :   nsString feedSpec;
    1850               5 :   rv = mAnnotationService->GetItemAnnotationString(folderId,
    1851               5 :                                                    NS_LITERAL_CSTRING(LMANNO_FEEDURI),
    1852               5 :                                                    feedSpec);
    1853                 :   
    1854               5 :   NS_ENSURE_SUCCESS(rv, rv);
    1855                 : 
    1856                 :   // write feed URI
    1857               5 :   rv = aOutput->Write(kFeedURIAttribute, sizeof(kFeedURIAttribute)-1, &dummy);
    1858               5 :   NS_ENSURE_SUCCESS(rv, rv);
    1859               5 :   rv = WriteEscapedUrl(NS_ConvertUTF16toUTF8(feedSpec), aOutput);
    1860               5 :   NS_ENSURE_SUCCESS(rv, rv);
    1861               5 :   rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
    1862               5 :   NS_ENSURE_SUCCESS(rv, rv);
    1863                 : 
    1864                 :   // get the optional site URI
    1865              10 :   nsString siteSpec;
    1866               5 :   rv = mAnnotationService->GetItemAnnotationString(folderId,
    1867               5 :                                                    NS_LITERAL_CSTRING(LMANNO_SITEURI),
    1868               5 :                                                    siteSpec);
    1869               5 :   if (NS_SUCCEEDED(rv) && !siteSpec.IsEmpty()) {
    1870                 :     // write site URI
    1871               5 :     rv = aOutput->Write(kHrefAttribute, sizeof(kHrefAttribute)-1, &dummy);
    1872               5 :     NS_ENSURE_SUCCESS(rv, rv);
    1873               5 :     rv = WriteEscapedUrl(NS_ConvertUTF16toUTF8(siteSpec), aOutput);
    1874               5 :     NS_ENSURE_SUCCESS(rv, rv);
    1875               5 :     rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
    1876               5 :     NS_ENSURE_SUCCESS(rv, rv);
    1877                 :   }
    1878                 : 
    1879                 :   // '>'
    1880               5 :   rv = aOutput->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy);
    1881               5 :   NS_ENSURE_SUCCESS(rv, rv);
    1882                 : 
    1883                 :   // title
    1884               5 :   rv = WriteTitle(aFolder, aOutput);
    1885               5 :   NS_ENSURE_SUCCESS(rv, rv);
    1886                 : 
    1887                 :   // '</A>\n'
    1888               5 :   rv = aOutput->Write(kItemClose, sizeof(kItemClose)-1, &dummy);
    1889               5 :   NS_ENSURE_SUCCESS(rv, rv);
    1890                 : 
    1891                 :   // description
    1892               5 :   rv = WriteDescription(folderId, nsINavBookmarksService::TYPE_BOOKMARK, aOutput);
    1893               5 :   NS_ENSURE_SUCCESS(rv, rv);
    1894                 : 
    1895               5 :   return NS_OK;
    1896                 : }
    1897                 : 
    1898                 : 
    1899                 : // nsPlacesImportExportService::WriteSeparator
    1900                 : //
    1901                 : //    "<HR NAME="...">"
    1902                 : nsresult
    1903               0 : nsPlacesImportExportService::WriteSeparator(nsINavHistoryResultNode* aItem,
    1904                 :                                             const nsACString& aIndent,
    1905                 :                                             nsIOutputStream* aOutput)
    1906                 : {
    1907                 :   PRUint32 dummy;
    1908                 :   nsresult rv;
    1909                 : 
    1910                 :   // indent
    1911               0 :   if (!aIndent.IsEmpty()) {
    1912               0 :     rv = aOutput->Write(PromiseFlatCString(aIndent).get(), aIndent.Length(),
    1913               0 :                         &dummy);
    1914               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1915                 :   }
    1916                 : 
    1917               0 :   rv = aOutput->Write(kSeparator, sizeof(kSeparator)-1, &dummy);
    1918               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1919                 : 
    1920                 :   // XXX: separator result nodes don't support the title getter yet
    1921                 :   PRInt64 itemId;
    1922               0 :   rv = aItem->GetItemId(&itemId);
    1923               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1924                 : 
    1925                 :   // Note: we can't write the separator ID or anything else other than NAME
    1926                 :   // because it makes Firefox 2.x crash/hang - see bug #381129
    1927                 : 
    1928               0 :   nsCAutoString title;
    1929               0 :   rv = mBookmarksService->GetItemTitle(itemId, title);
    1930               0 :   if (NS_SUCCEEDED(rv) && !title.IsEmpty()) {
    1931               0 :     rv = aOutput->Write(kNameAttribute, strlen(kNameAttribute), &dummy);
    1932               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1933                 : 
    1934               0 :     char* escapedTitle = nsEscapeHTML(title.get());
    1935               0 :     if (escapedTitle) {
    1936                 :       PRUint32 dummy;
    1937               0 :       rv = aOutput->Write(escapedTitle, strlen(escapedTitle), &dummy);
    1938               0 :       nsMemory::Free(escapedTitle);
    1939               0 :       NS_ENSURE_SUCCESS(rv, rv);
    1940               0 :       rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
    1941               0 :       NS_ENSURE_SUCCESS(rv, rv);
    1942                 :     }
    1943                 :   }
    1944                 : 
    1945                 :   // '>'
    1946               0 :   rv = aOutput->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy);
    1947               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1948                 : 
    1949                 :   // line break
    1950               0 :   rv = aOutput->Write(NS_LINEBREAK, sizeof(NS_LINEBREAK)-1, &dummy);
    1951               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1952                 : 
    1953               0 :   return rv;
    1954                 : }
    1955                 : 
    1956                 : 
    1957                 : // WriteEscapedUrl
    1958                 : //
    1959                 : //    Writes the given string to the stream escaped as necessary for URLs.
    1960                 : //
    1961                 : //    Unfortunately, the old bookmarks system uses a custom hardcoded and
    1962                 : //    braindead escaping scheme that we need to emulate. It just replaces
    1963                 : //    quotes with %22 and that's it.
    1964                 : nsresult
    1965              92 : WriteEscapedUrl(const nsCString& aString, nsIOutputStream* aOutput)
    1966                 : {
    1967             184 :   nsCAutoString escaped(aString);
    1968                 :   PRInt32 offset;
    1969             184 :   while ((offset = escaped.FindChar('\"')) >= 0) {
    1970               0 :     escaped.Cut(offset, 1);
    1971               0 :     escaped.Insert(NS_LITERAL_CSTRING("%22"), offset);
    1972                 :   }
    1973                 :   PRUint32 dummy;
    1974              92 :   return aOutput->Write(escaped.get(), escaped.Length(), &dummy);
    1975                 : }
    1976                 : 
    1977                 : 
    1978                 : // nsPlacesImportExportService::WriteContainerContents
    1979                 : //
    1980                 : //    The indent here is the indent of the parent. We will add an additional
    1981                 : //    indent before writing data.
    1982                 : nsresult
    1983              32 : nsPlacesImportExportService::WriteContainerContents(nsINavHistoryResultNode* aFolder,
    1984                 :                                                     const nsACString& aIndent,
    1985                 :                                                     nsIOutputStream* aOutput)
    1986                 : {
    1987              64 :   nsCAutoString myIndent(aIndent);
    1988              32 :   myIndent.Append(kIndent);
    1989                 : 
    1990                 :   PRInt64 folderId;
    1991              32 :   nsresult rv = aFolder->GetItemId(&folderId);
    1992              32 :   NS_ENSURE_SUCCESS(rv, rv);
    1993                 : 
    1994              64 :   nsCOMPtr<nsINavHistoryContainerResultNode> folderNode = do_QueryInterface(aFolder, &rv);
    1995              32 :   NS_ENSURE_SUCCESS(rv, rv);
    1996                 : 
    1997              32 :   rv = folderNode->SetContainerOpen(true);
    1998              32 :   NS_ENSURE_SUCCESS(rv, rv);
    1999                 : 
    2000              32 :   PRUint32 childCount = 0;
    2001              32 :   folderNode->GetChildCount(&childCount);
    2002             104 :   for (PRUint32 i = 0; i < childCount; ++i) {
    2003             144 :     nsCOMPtr<nsINavHistoryResultNode> child;
    2004              72 :     rv = folderNode->GetChild(i, getter_AddRefs(child));
    2005              72 :     NS_ENSURE_SUCCESS(rv, rv);
    2006              72 :     PRUint32 type = 0;
    2007              72 :     rv = child->GetType(&type);
    2008              72 :     NS_ENSURE_SUCCESS(rv, rv);
    2009              72 :     if (type == nsINavHistoryResultNode::RESULT_TYPE_FOLDER) {
    2010                 :       // bookmarks folder
    2011                 :       PRInt64 childFolderId;
    2012              15 :       rv = child->GetItemId(&childFolderId);
    2013              15 :       NS_ENSURE_SUCCESS(rv, rv);
    2014                 : 
    2015                 :       // it could be a regular folder or it could be a livemark.
    2016                 :       // Livemarks service is async, for now just workaround using annotations
    2017                 :       // service.
    2018                 :       bool isLivemark;
    2019              15 :       nsresult rv = mAnnotationService->ItemHasAnnotation(childFolderId,
    2020              15 :                                                           NS_LITERAL_CSTRING(LMANNO_FEEDURI),
    2021              15 :                                                           &isLivemark);
    2022              15 :       NS_ENSURE_SUCCESS(rv, rv);
    2023                 : 
    2024              15 :       if (isLivemark)
    2025               5 :         rv = WriteLivemark(child, myIndent, aOutput);
    2026                 :       else
    2027              10 :         rv = WriteContainer(child, myIndent, aOutput);
    2028                 :     }
    2029              57 :     else if (type == nsINavHistoryResultNode::RESULT_TYPE_SEPARATOR) {
    2030               0 :       rv = WriteSeparator(child, myIndent, aOutput);
    2031                 :     }
    2032                 :     else {
    2033              57 :       rv = WriteItem(child, myIndent, aOutput);
    2034                 :     }
    2035              72 :     NS_ENSURE_SUCCESS(rv, rv);
    2036                 :   }
    2037              32 :   return NS_OK;
    2038                 : }
    2039                 : 
    2040                 : 
    2041                 : // NotifyImportObservers
    2042                 : //
    2043                 : //    Notifies bookmarks-restore observers using nsIObserverService.  This
    2044                 : //    function is void and we simply return on failure because we don't want
    2045                 : //    the import itself to fail if notifying observers does.
    2046                 : static void
    2047              62 : NotifyImportObservers(const char* aTopic,
    2048                 :                       PRInt64 aFolderId,
    2049                 :                       bool aIsInitialImport)
    2050                 : {
    2051             124 :   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    2052              62 :   if (!obs)
    2053                 :     return;
    2054                 : 
    2055             124 :   nsCOMPtr<nsISupports> folderIdSupp = nsnull;
    2056              62 :   if (aFolderId > 0) {
    2057                 :     nsCOMPtr<nsISupportsPRInt64> folderIdInt =
    2058              20 :       do_CreateInstance(NS_SUPPORTS_PRINT64_CONTRACTID);
    2059              10 :     if (!folderIdInt)
    2060                 :       return;
    2061                 : 
    2062              10 :     if (NS_FAILED(folderIdInt->SetData(aFolderId)))
    2063                 :       return;
    2064                 : 
    2065              20 :     folderIdSupp = do_QueryInterface(folderIdInt);
    2066                 :   }
    2067                 : 
    2068              62 :   obs->NotifyObservers(folderIdSupp,
    2069                 :                        aTopic,
    2070              46 :                        (aIsInitialImport ? RESTORE_INITIAL_NSIOBSERVER_DATA
    2071              62 :                                          : RESTORE_NSIOBSERVER_DATA).get());
    2072                 : }
    2073                 : 
    2074                 : 
    2075                 : NS_IMETHODIMP
    2076              15 : nsPlacesImportExportService::ImportHTMLFromFile(nsILocalFile* aFile,
    2077                 :                                                 bool aIsInitialImport)
    2078                 : {
    2079              15 :   NotifyImportObservers(RESTORE_BEGIN_NSIOBSERVER_TOPIC, -1, aIsInitialImport);
    2080                 : 
    2081                 :   // this version is exposed on the interface and disallows changing of roots
    2082                 :   nsresult rv = ImportHTMLFromFileInternal(aFile,
    2083                 :                                            false,
    2084                 :                                            0,
    2085              15 :                                            aIsInitialImport);
    2086                 : 
    2087              15 :   if (NS_FAILED(rv)) {
    2088                 :     NotifyImportObservers(RESTORE_FAILED_NSIOBSERVER_TOPIC,
    2089                 :                           -1,
    2090               2 :                           aIsInitialImport);
    2091                 :   }
    2092                 :   else {
    2093                 :     NotifyImportObservers(RESTORE_SUCCESS_NSIOBSERVER_TOPIC,
    2094                 :                           -1,
    2095              13 :                           aIsInitialImport);
    2096                 :   }
    2097                 : 
    2098              15 :   return rv;
    2099                 : }
    2100                 : 
    2101                 : 
    2102                 : NS_IMETHODIMP
    2103              11 : nsPlacesImportExportService::ImportHTMLFromURI(nsIURI* aURI,
    2104                 :                                                bool aIsInitialImport)
    2105                 : {
    2106              11 :   NotifyImportObservers(RESTORE_BEGIN_NSIOBSERVER_TOPIC, -1, aIsInitialImport);
    2107                 : 
    2108                 :   // this version is exposed on the interface and disallows changing of roots
    2109                 :   nsresult rv = ImportHTMLFromURIInternal(aURI,
    2110                 :                                           false,
    2111                 :                                           0,
    2112              11 :                                           aIsInitialImport);
    2113                 : 
    2114              11 :   if (NS_FAILED(rv)) {
    2115                 :     NotifyImportObservers(RESTORE_FAILED_NSIOBSERVER_TOPIC,
    2116                 :                           -1,
    2117               0 :                           aIsInitialImport);
    2118                 :   }
    2119                 :   else {
    2120                 :     NotifyImportObservers(RESTORE_SUCCESS_NSIOBSERVER_TOPIC,
    2121                 :                           -1,
    2122              11 :                           aIsInitialImport);
    2123                 :   }
    2124                 : 
    2125              11 :   return rv;
    2126                 : }
    2127                 : 
    2128                 : 
    2129                 : NS_IMETHODIMP
    2130               5 : nsPlacesImportExportService::ImportHTMLFromFileToFolder(nsILocalFile* aFile,
    2131                 :                                                         PRInt64 aFolderId,
    2132                 :                                                         bool aIsInitialImport)
    2133                 : {
    2134                 :   NotifyImportObservers(RESTORE_BEGIN_NSIOBSERVER_TOPIC,
    2135                 :                         aFolderId,
    2136               5 :                         aIsInitialImport);
    2137                 : 
    2138                 :   // this version is exposed on the interface and disallows changing of roots
    2139                 :   nsresult rv = ImportHTMLFromFileInternal(aFile,
    2140                 :                                            false,
    2141                 :                                            aFolderId,
    2142               5 :                                            aIsInitialImport);
    2143                 : 
    2144               5 :   if (NS_FAILED(rv)) {
    2145                 :     NotifyImportObservers(RESTORE_FAILED_NSIOBSERVER_TOPIC,
    2146                 :                           aFolderId,
    2147               1 :                           aIsInitialImport);
    2148                 :   }
    2149                 :   else {
    2150                 :     NotifyImportObservers(RESTORE_SUCCESS_NSIOBSERVER_TOPIC,
    2151                 :                           aFolderId,
    2152               4 :                           aIsInitialImport);
    2153                 :   }
    2154                 : 
    2155               5 :   return rv;
    2156                 : }
    2157                 : 
    2158                 : 
    2159                 : nsresult
    2160              20 : nsPlacesImportExportService::ImportHTMLFromFileInternal(nsILocalFile* aFile,
    2161                 :                                                         bool aAllowRootChanges,
    2162                 :                                                         PRInt64 aFolder,
    2163                 :                                                         bool aIsImportDefaults)
    2164                 : {
    2165                 :   nsresult rv;
    2166                 : 
    2167              40 :   nsCOMPtr<nsIFile> file = do_QueryInterface(aFile);
    2168              20 :   NS_ENSURE_STATE(file);
    2169                 : 
    2170                 : #ifdef DEBUG_IMPORT
    2171                 :   nsAutoString path;
    2172                 :   file->GetPath(path);
    2173                 :   printf("\nImporting %s\n", NS_ConvertUTF16toUTF8(path).get());
    2174                 : #endif
    2175                 : 
    2176                 :   // Confirm file to be imported exists.
    2177                 :   bool exists;
    2178              20 :   rv = file->Exists(&exists);
    2179              20 :   NS_ENSURE_SUCCESS(rv, rv);
    2180              20 :   if (!exists) {
    2181               3 :     return NS_ERROR_INVALID_ARG;
    2182                 :   }
    2183                 : 
    2184              34 :   nsCOMPtr<nsIIOService> ioservice = do_GetIOService(&rv);
    2185              17 :   NS_ENSURE_SUCCESS(rv, rv);
    2186              34 :   nsCOMPtr<nsIURI> fileURI;
    2187              17 :   rv = ioservice->NewFileURI(file, getter_AddRefs(fileURI));
    2188              17 :   NS_ENSURE_SUCCESS(rv, rv);
    2189                 : 
    2190              17 :   return ImportHTMLFromURIInternal(fileURI, aAllowRootChanges, aFolder, aIsImportDefaults);
    2191                 : }
    2192                 : 
    2193                 : nsresult
    2194              28 : nsPlacesImportExportService::ImportHTMLFromURIInternal(nsIURI* aURI,
    2195                 :                                                        bool aAllowRootChanges,
    2196                 :                                                        PRInt64 aFolder,
    2197                 :                                                        bool aIsImportDefaults)
    2198                 : {
    2199              28 :   nsresult rv = EnsureServiceState();
    2200              28 :   NS_ENSURE_SUCCESS(rv, rv);
    2201                 : 
    2202              56 :   nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID);
    2203              28 :   NS_ENSURE_TRUE(parser, NS_ERROR_OUT_OF_MEMORY);
    2204                 : 
    2205              56 :   nsCOMPtr<BookmarkContentSink> sink = new BookmarkContentSink();
    2206              28 :   NS_ENSURE_TRUE(sink, NS_ERROR_OUT_OF_MEMORY);
    2207              28 :   rv = sink->Init(aAllowRootChanges, aFolder, aIsImportDefaults);
    2208              28 :   NS_ENSURE_SUCCESS(rv, rv);
    2209              28 :   parser->SetContentSink(sink);
    2210                 : 
    2211                 :   // Set the content type on the channel, otherwise the default "unknown" type
    2212                 :   // will confuse the parser.
    2213              56 :   nsCOMPtr<nsIIOService> ioservice = do_GetIOService(&rv);
    2214              28 :   NS_ENSURE_SUCCESS(rv, rv);
    2215              28 :   rv = ioservice->NewChannelFromURI(aURI, getter_AddRefs(mImportChannel));
    2216              28 :   NS_ENSURE_SUCCESS(rv, rv);
    2217              28 :   rv = mImportChannel->SetContentType(NS_LITERAL_CSTRING("text/html"));
    2218              28 :   NS_ENSURE_SUCCESS(rv, rv);
    2219                 : 
    2220                 :   // Init parser.
    2221              28 :   rv = parser->Parse(aURI, nsnull);
    2222              28 :   NS_ENSURE_SUCCESS(rv, rv);
    2223                 : 
    2224                 :   // Run the import in batch mode, so it will be executed in a transaction
    2225                 :   // and will be faster.
    2226              28 :   mIsImportDefaults = aIsImportDefaults;
    2227              28 :   mBookmarksService->RunInBatchMode(this, parser);
    2228              28 :   mImportChannel = nsnull;
    2229                 : 
    2230              28 :   return NS_OK;
    2231                 : }
    2232                 : 
    2233                 : 
    2234                 : NS_IMETHODIMP
    2235              28 : nsPlacesImportExportService::RunBatched(nsISupports* aUserData)
    2236                 : {
    2237                 :   nsresult rv;
    2238              28 :   if (mIsImportDefaults) {
    2239                 :     PRInt64 bookmarksMenuFolder;
    2240              22 :     rv = mBookmarksService->GetBookmarksMenuFolder(&bookmarksMenuFolder);
    2241              22 :     NS_ENSURE_SUCCESS(rv, rv);
    2242                 : 
    2243              22 :     rv = mBookmarksService->RemoveFolderChildren(bookmarksMenuFolder);
    2244              22 :     NS_ENSURE_SUCCESS(rv, rv);
    2245                 : 
    2246                 :     PRInt64 toolbarFolder;
    2247              22 :     rv = mBookmarksService->GetToolbarFolder(&toolbarFolder);
    2248              22 :     NS_ENSURE_SUCCESS(rv, rv);
    2249                 : 
    2250              22 :     rv = mBookmarksService->RemoveFolderChildren(toolbarFolder);
    2251              22 :     NS_ENSURE_SUCCESS(rv, rv);
    2252                 : 
    2253                 :     PRInt64 unfiledBookmarksFolder;
    2254              22 :     rv = mBookmarksService->GetUnfiledBookmarksFolder(&unfiledBookmarksFolder);
    2255              22 :     NS_ENSURE_SUCCESS(rv, rv);
    2256                 : 
    2257              22 :     rv = mBookmarksService->RemoveFolderChildren(unfiledBookmarksFolder);
    2258              22 :     NS_ENSURE_SUCCESS(rv, rv);
    2259                 :   }
    2260                 : 
    2261                 :   // streams
    2262              56 :   nsCOMPtr<nsIInputStream> stream;
    2263              28 :   rv = mImportChannel->Open(getter_AddRefs(stream));
    2264              28 :   NS_ENSURE_SUCCESS(rv, rv);
    2265              56 :   nsCOMPtr<nsIInputStream> bufferedstream;
    2266              28 :   rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedstream), stream, 4096);
    2267              28 :   NS_ENSURE_SUCCESS(rv, rv);
    2268                 : 
    2269                 :   // feed the parser the data
    2270                 :   // Note: on error, we always need to set the channel's status to be the
    2271                 :   // same, and to always call OnStopRequest with the channel error.
    2272              56 :   nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(aUserData, &rv);
    2273              28 :   NS_ENSURE_SUCCESS(rv, rv);
    2274              28 :   rv = listener->OnStartRequest(mImportChannel, nsnull);
    2275              28 :   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "OnStartRequest failed");
    2276              28 :   rv = SyncChannelStatus(mImportChannel, rv);
    2277              28 :   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SyncChannelStatus failed");
    2278                 : 
    2279              81 :   while (NS_SUCCEEDED(rv))
    2280                 :   {
    2281                 :     PRUint32 available;
    2282              53 :     rv = bufferedstream->Available(&available);
    2283              53 :     if (rv == NS_BASE_STREAM_CLOSED) {
    2284               0 :       rv = NS_OK;
    2285               0 :       available = 0;
    2286                 :     }
    2287              53 :     if (NS_FAILED(rv)) {
    2288               0 :       mImportChannel->Cancel(rv);
    2289               0 :       break;
    2290                 :     }
    2291              53 :     if (!available)
    2292              28 :       break; // blocking input stream has none available when done
    2293                 : 
    2294              25 :     rv = listener->OnDataAvailable(mImportChannel, nsnull, bufferedstream, 0,
    2295              25 :                                    available);
    2296              25 :     if (NS_FAILED(rv))
    2297               0 :       break;
    2298              25 :     rv = SyncChannelStatus(mImportChannel, rv);
    2299              25 :     if (NS_FAILED(rv))
    2300               0 :       break;
    2301                 :   }
    2302                 : 
    2303              28 :   rv = listener->OnStopRequest(mImportChannel, nsnull, rv);
    2304              28 :   NS_ENSURE_SUCCESS(rv, rv);
    2305                 : 
    2306              28 :   return NS_OK;
    2307                 : }
    2308                 : 
    2309                 : 
    2310                 : NS_IMETHODIMP
    2311              10 : nsPlacesImportExportService::ExportHTMLToFile(nsILocalFile* aBookmarksFile)
    2312                 : {
    2313              10 :   NS_ENSURE_ARG(aBookmarksFile);
    2314                 : 
    2315                 : #ifdef DEBUG_EXPORT
    2316                 :   nsAutoString path;
    2317                 :   aBookmarksFile->GetPath(path);
    2318                 :   printf("\nExporting %s\n", NS_ConvertUTF16toUTF8(path).get());
    2319                 : 
    2320                 :   PRTime startTime = PR_Now();
    2321                 :   printf("\nStart time: %lld\n", startTime);
    2322                 : #endif
    2323                 : 
    2324              10 :   nsresult rv = EnsureServiceState();
    2325              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2326                 : 
    2327                 :   // get a safe output stream, so we don't clobber the bookmarks file unless
    2328                 :   // all the writes succeeded.
    2329              20 :   nsCOMPtr<nsIOutputStream> out;
    2330              10 :   rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out),
    2331                 :                                        aBookmarksFile,
    2332                 :                                        PR_WRONLY | PR_CREATE_FILE,
    2333              10 :                                        0600, 0);
    2334              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2335                 : 
    2336                 :   // We need a buffered output stream for performance.
    2337                 :   // See bug 202477.
    2338              20 :   nsCOMPtr<nsIOutputStream> strm;
    2339              10 :   rv = NS_NewBufferedOutputStream(getter_AddRefs(strm), out, 4096);
    2340              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2341                 : 
    2342                 :   // Get a new query object.
    2343              20 :   nsCOMPtr<nsINavHistoryQuery> query;
    2344              10 :   rv = mHistoryService->GetNewQuery(getter_AddRefs(query));
    2345              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2346              20 :   nsCOMPtr<nsINavHistoryQueryOptions> options;
    2347              10 :   rv = mHistoryService->GetNewQueryOptions(getter_AddRefs(options));
    2348              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2349              20 :   nsCOMPtr<nsINavHistoryResult> result;
    2350                 : 
    2351                 :   // We need the bookmarks menu root node to write out the title.
    2352                 :   PRInt64 bookmarksMenuFolder;
    2353              10 :   rv = mBookmarksService->GetBookmarksMenuFolder(&bookmarksMenuFolder);
    2354              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2355              10 :   rv = query->SetFolders(&bookmarksMenuFolder, 1);
    2356              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2357              10 :   rv = mHistoryService->ExecuteQuery(query, options, getter_AddRefs(result));
    2358              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2359              20 :   nsCOMPtr<nsINavHistoryContainerResultNode> rootNode;
    2360              10 :   rv = result->GetRoot(getter_AddRefs(rootNode));
    2361              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2362                 : 
    2363                 :   // file header
    2364                 :   PRUint32 dummy;
    2365              10 :   rv = strm->Write(kFileIntro, sizeof(kFileIntro)-1, &dummy);
    2366              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2367                 : 
    2368                 :   // '<H1'
    2369              10 :   rv = strm->Write(kRootIntro, sizeof(kRootIntro)-1, &dummy); // <H1
    2370              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2371                 : 
    2372                 :   // '>Bookmarks</H1>
    2373              10 :   rv = strm->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy); // >
    2374              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2375              10 :   rv = WriteTitle(rootNode, strm);
    2376              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2377              10 :   rv = strm->Write(kCloseRootH1, sizeof(kCloseRootH1)-1, &dummy); // </H1>
    2378              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2379                 : 
    2380                 :   // Container's prologue.
    2381              10 :   rv = WriteContainerPrologue(EmptyCString(), strm);
    2382              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2383                 : 
    2384                 :   // indents
    2385              20 :   nsCAutoString indent;
    2386              10 :   indent.Assign(kIndent);
    2387                 : 
    2388                 :   // Bookmarks Menu.
    2389              10 :   rv = WriteContainerContents(rootNode, EmptyCString(), strm);
    2390              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2391                 : 
    2392                 :   // Bookmarks Toolbar.
    2393                 :   // We write this folder under the bookmarks-menu for backwards compatibility.
    2394                 :   PRInt64 toolbarFolder;
    2395              10 :   rv = mBookmarksService->GetToolbarFolder(&toolbarFolder);
    2396              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2397              10 :   rv = query->SetFolders(&toolbarFolder, 1);
    2398              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2399              10 :   rv = mHistoryService->ExecuteQuery(query, options, getter_AddRefs(result));
    2400              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2401              10 :   rv = result->GetRoot(getter_AddRefs(rootNode));
    2402              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2403                 :   // Write it out only if it's not empty.
    2404              10 :   rv = rootNode->SetContainerOpen(true);
    2405              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2406              10 :   PRUint32 childCount = 0;
    2407              10 :   rv = rootNode->GetChildCount(&childCount);
    2408              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2409              10 :   rv = rootNode->SetContainerOpen(false);
    2410              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2411              10 :   if (childCount) {
    2412               7 :     rv = WriteContainer(rootNode, nsDependentCString(kIndent), strm);
    2413               7 :     NS_ENSURE_SUCCESS(rv, rv);
    2414                 :   }
    2415                 : 
    2416                 :   // Unfiled Bookmarks.
    2417                 :   // We write this folder under the bookmarks-menu for backwards compatibility.
    2418                 :   PRInt64 unfiledBookmarksFolder;
    2419              10 :   rv = mBookmarksService->GetUnfiledBookmarksFolder(&unfiledBookmarksFolder);
    2420              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2421              10 :   rv = query->SetFolders(&unfiledBookmarksFolder, 1);
    2422              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2423              10 :   rv = mHistoryService->ExecuteQuery(query, options, getter_AddRefs(result));
    2424              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2425              10 :   rv = result->GetRoot(getter_AddRefs(rootNode));
    2426              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2427                 :   // Write it out only if it's not empty.
    2428              10 :   rv = rootNode->SetContainerOpen(true);
    2429              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2430              10 :   childCount = 0;
    2431              10 :   rootNode->GetChildCount(&childCount);
    2432              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2433              10 :   rv = rootNode->SetContainerOpen(false);
    2434              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2435              10 :   if (childCount) {
    2436               5 :     rv = WriteContainer(rootNode, nsDependentCString(kIndent), strm);
    2437               5 :     NS_ENSURE_SUCCESS(rv, rv);
    2438                 :   }
    2439                 : 
    2440                 :   // Container's epilogue.
    2441              10 :   rv = WriteContainerEpilogue(EmptyCString(), strm);
    2442              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2443                 : 
    2444                 :   // commit the write
    2445              20 :   nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(strm, &rv);
    2446              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2447              10 :   rv = safeStream->Finish();
    2448              10 :   NS_ENSURE_SUCCESS(rv, rv);
    2449                 : 
    2450                 : #ifdef DEBUG_EXPORT
    2451                 :   printf("\nTotal time in seconds: %lld\n", (PR_Now() - startTime)/1000000);
    2452                 : #endif
    2453                 : 
    2454              10 :   return NS_OK;
    2455                 : }
    2456                 : 
    2457                 : 
    2458                 : NS_IMETHODIMP
    2459               2 : nsPlacesImportExportService::BackupBookmarksFile()
    2460                 : {
    2461               2 :   nsresult rv = EnsureServiceState();
    2462               2 :   NS_ENSURE_SUCCESS(rv, rv);
    2463                 : 
    2464                 :   // get bookmarks file
    2465               4 :   nsCOMPtr<nsIFile> bookmarksFileDir;
    2466                 :   rv = NS_GetSpecialDirectory(NS_APP_BOOKMARKS_50_FILE,
    2467               2 :                               getter_AddRefs(bookmarksFileDir));
    2468                 : 
    2469               2 :   NS_ENSURE_SUCCESS(rv, rv);
    2470               4 :   nsCOMPtr<nsILocalFile> bookmarksFile = do_QueryInterface(bookmarksFileDir);
    2471               2 :   NS_ENSURE_STATE(bookmarksFile);
    2472                 : 
    2473                 :   // Create the file if it doesn't exist.
    2474                 :   bool exists;
    2475               2 :   rv = bookmarksFile->Exists(&exists);
    2476               2 :   if (NS_FAILED(rv) || !exists) {
    2477               1 :     rv = bookmarksFile->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
    2478               1 :     if (NS_FAILED(rv)) {
    2479               0 :       NS_WARNING("Unable to create bookmarks.html!");
    2480               0 :       return rv;
    2481                 :     }
    2482                 :   }
    2483                 : 
    2484                 :   // export bookmarks.html
    2485               2 :   rv = ExportHTMLToFile(bookmarksFile);
    2486               2 :   NS_ENSURE_SUCCESS(rv, rv);
    2487                 : 
    2488               2 :   return NS_OK;
    2489                 : }

Generated by: LCOV version 1.7