Bug 716706 - Fix and enhance the feed Subscriptions dialog. r=bienvenu,ui-review=bwinton
authoralta88@gmail.com
Fri, 24 Feb 2012 10:39:17 +0000
changeset 10928 71e65bd6dbd50bcbbe0b3bd0480e347cc616f13b
parent 10927 55b232b17c4cda0429eb5147670064e99e02386a
child 10929 9bcb86fe84f6d5b78b336d7ca62da1f8aa9f307d
push id463
push userbugzilla@standard8.plus.com
push dateTue, 24 Apr 2012 17:34:51 +0000
treeherdercomm-beta@e53588e8f7b0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbienvenu
bugs716706
Bug 716706 - Fix and enhance the feed Subscriptions dialog. r=bienvenu,ui-review=bwinton
mail/locales/en-US/chrome/messenger-newsblog/am-newsblog.properties
mail/locales/en-US/chrome/messenger-newsblog/feed-properties.dtd
mail/locales/en-US/chrome/messenger-newsblog/feed-subscriptions.dtd
mail/locales/en-US/chrome/messenger-newsblog/newsblog.properties
mail/locales/jar.mn
mail/themes/gnomestripe/mail/newsblog/feed-subscriptions.css
mail/themes/pinstripe/mail/newsblog/feed-subscriptions.css
mail/themes/qute/mail/newsblog/feed-subscriptions.css
mailnews/extensions/newsblog/content/am-newsblog.xul
mailnews/extensions/newsblog/content/edittree.xml
mailnews/extensions/newsblog/content/feed-properties.js
mailnews/extensions/newsblog/content/feed-properties.xul
mailnews/extensions/newsblog/content/feed-subscriptions.js
mailnews/extensions/newsblog/content/feed-subscriptions.xul
mailnews/extensions/newsblog/content/newsblogOverlay.js
mailnews/extensions/newsblog/jar.mn
suite/locales/en-US/chrome/mailnews/newsblog/am-newsblog.properties
suite/locales/en-US/chrome/mailnews/newsblog/feed-properties.dtd
suite/locales/en-US/chrome/mailnews/newsblog/feed-subscriptions.dtd
suite/locales/en-US/chrome/mailnews/newsblog/newsblog.properties
suite/locales/jar.mn
suite/themes/classic/messenger/newsblog/feed-subscriptions.css
suite/themes/modern/messenger/newsblog/feed-subscriptions.css
deleted file mode 100644
--- a/mail/locales/en-US/chrome/messenger-newsblog/am-newsblog.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-## Strings used in the Account Manager
-prefPanel-newsblog=Feed Settings
deleted file mode 100644
--- a/mail/locales/en-US/chrome/messenger-newsblog/feed-properties.dtd
+++ /dev/null
@@ -1,19 +0,0 @@
-<!-- Feed Properties Dialog -->
-<!ENTITY window.title             "Feed Properties">
-
-<!ENTITY feedFolder.label         "Store Articles in: ">
-<!ENTITY feedFolder.accesskey     "S">
-
-<!ENTITY feedLocation.label       "Feed URL: ">
-<!ENTITY feedLocation.accesskey   "F">
-
-<!ENTITY choosethisfolder.label   "choose this folder">
-
-<!ENTITY quickMode.label          "Show the article summary instead of loading the web page">
-<!ENTITY quickMode.accesskey      "h">
-
-<!ENTITY copyLinkCmd.label        "Copy Link Location">
-<!ENTITY copyLinkCmd.accesskey    "C">
-
-<!ENTITY pasteLinkCmd.label       "Paste">
-<!ENTITY pasteLinkCmd.accesskey   "P">
--- a/mail/locales/en-US/chrome/messenger-newsblog/feed-subscriptions.dtd
+++ b/mail/locales/en-US/chrome/messenger-newsblog/feed-subscriptions.dtd
@@ -1,18 +1,31 @@
 <!-- Subscription Dialog -->
-<!ENTITY feedSubscriptions.label "Feed Subscriptions">
-<!ENTITY subscriptionDesc.label "Note: Removing or changing the folder for a feed will not affect previously downloaded articles.">
-<!ENTITY feedTitle.label "Title:">
-<!ENTITY location.label  "Location:">
+<!ENTITY feedSubscriptions.label     "Feed Subscriptions">
+<!ENTITY subscriptionDesc.label      "Note: Removing or changing the folder for a feed will not affect previously downloaded articles.">
+
+<!ENTITY feedTitle.label             "Title:">
+<!ENTITY feedTitle.accesskey         "T">
+
+<!ENTITY feedLocation.label          "Feed URL:">
+<!ENTITY feedLocation.accesskey      "U">
+<!ENTITY feedLocation.placeholder    "Enter a valid feed url to Add">
+
+<!ENTITY feedFolder.label            "Store Articles in:">
+<!ENTITY feedFolder.accesskey        "S">
 
-<!ENTITY button.addFeed.label       "Add">
-<!ENTITY button.addFeed.accesskey   "A">
-<!ENTITY button.editFeed.label      "Edit">
-<!ENTITY button.editFeed.accesskey  "E">
-<!ENTITY button.removeFeed.label "Remove">
-<!ENTITY button.removeFeed.accesskey   "R">
-<!ENTITY button.importOPML.label "Import">
-<!ENTITY button.importOPML.accesskey   "I">
-<!ENTITY button.exportOPML.label "Export">
-<!ENTITY button.exportOPML.accesskey   "X">
+<!ENTITY quickMode.label             "Show the article summary instead of loading the web page">
+<!ENTITY quickMode.accesskey         "h">
+
+<!ENTITY validateText.label          "Check validation and retrieve a valid url.">
 
-<!ENTITY cmd.close.commandKey             "w">
+<!ENTITY button.addFeed.label        "Add">
+<!ENTITY button.addFeed.accesskey    "A">
+<!ENTITY button.editFeed.label       "Update">
+<!ENTITY button.editFeed.accesskey   "U">
+<!ENTITY button.removeFeed.label     "Remove">
+<!ENTITY button.removeFeed.accesskey "R">
+<!ENTITY button.importOPML.label     "Import">
+<!ENTITY button.importOPML.accesskey "I">
+<!ENTITY button.exportOPML.label     "Export">
+<!ENTITY button.exportOPML.accesskey "X">
+
+<!ENTITY cmd.close.commandKey        "w">
--- a/mail/locales/en-US/chrome/messenger-newsblog/newsblog.properties
+++ b/mail/locales/en-US/chrome/messenger-newsblog/newsblog.properties
@@ -1,14 +1,19 @@
 subscribe-validating-feed=Verifying the feed…
 subscribe-cancelSubscription=Are you sure you wish to cancel subscribing to the current feed?
 subscribe-cancelSubscriptionTitle=Subscribing to a Feed…
-subscribe-noFeedIsSelected=<no feed selected>
 subscribe-feedAlreadySubscribed=You already have a subscription for this feed.
 subscribe-errorOpeningFile=Could not open the file.
+subscribe-feedAdded=Feed added.
+subscribe-feedUpdated=Feed updated.
+subscribe-feedMoved=Feed subscription moved.
+subscribe-feedRemoved=Feed unsubscribed.
+subscribe-feedNotValid=The Feed URL is not a valid feed.
+subscribe-networkError=The Feed URL could not be found. Please check the name and try again.
 
 subscribe-OPMLImportTitle=Select OPML file to import
 subscribe-OPMLExportTitle=Export feeds as an OPML file
 ## LOCALIZATION NOTE(subscribe-OPMLExportFileDialogTitle): %S is the brandShortName
 subscribe-OPMLExportFileDialogTitle=%S OPML Export
 ## LOCALIZATION NOTE(subscribe-OPMLExportDefaultFileName): %S is the brandShortName
 subscribe-OPMLExportDefaultFileName=My%SFeeds.opml
 ## LOCALIZATION NOTE(subscribe-OPMLImportInvalidFile): %S is the name of the OPML file the user tried to import.
@@ -27,18 +32,16 @@ subscribe-confirmFeedDeletionTitle=Remov
 subscribe-confirmFeedDeletion=Are you sure you want to unsubscribe from the feed: \n %S?
 
 ## LOCALIZATION NOTE(subscribe-gettingFeedItems):
 ##  - The first %S is the number of articles processed so far;
 ##  - The second %S is the total number of items
 subscribe-gettingFeedItems=Downloading feed articles (%S of %S)…
 
 newsblog-noNewArticlesForFeed=There are no new articles for this feed.
-newsblog-networkErrorTitle=Network Error
 ## LOCALIZATION NOTE(newsblog-networkError): %S is the feed URL
 newsblog-networkError=%S could not be found. Please check the name and try again.
-newsblog-feedNotValidTitle=Invalid Feed
 ## LOCALIZATION NOTE(newsblog-feedNotValid): %S is the feed URL
 newsblog-feedNotValid=%S is not a valid feed.
 newsblog-getNewMsgsCheck=Checking feeds for new items…
 
 ## LOCALIZATION NOTE(feeds-accountname): This string should be the same as feeds.accountName in am-newsblog.dtd
 feeds-accountname=Blogs & News Feeds
--- a/mail/locales/jar.mn
+++ b/mail/locales/jar.mn
@@ -161,18 +161,16 @@
   locale/@AB_CD@/messenger/downloads/settingsChange.dtd                 (%chrome/overrides/settingsChange.dtd)
   locale/@AB_CD@/messenger/netError.dtd                                 (%chrome/overrides/netError.dtd)
   locale/@AB_CD@/messenger/downloadsOverlay.dtd                         (%chrome/messenger/downloadsOverlay.dtd)
 % locale messenger-mapi @AB_CD@ %locale/@AB_CD@/messenger-mapi/
   locale/@AB_CD@/messenger-mapi/mapi.properties                         (%chrome/messenger-mapi/mapi.properties)
 % locale messenger-newsblog @AB_CD@ %locale/@AB_CD@/messenger-newsblog/
   locale/@AB_CD@/messenger-newsblog/newsblog.properties                 (%chrome/messenger-newsblog/newsblog.properties)
   locale/@AB_CD@/messenger-newsblog/feed-subscriptions.dtd              (%chrome/messenger-newsblog/feed-subscriptions.dtd)
-  locale/@AB_CD@/messenger-newsblog/feed-properties.dtd                 (%chrome/messenger-newsblog/feed-properties.dtd)
-  locale/@AB_CD@/messenger-newsblog/am-newsblog.properties              (%chrome/messenger-newsblog/am-newsblog.properties)
   locale/@AB_CD@/messenger-newsblog/am-newsblog.dtd                     (%chrome/messenger-newsblog/am-newsblog.dtd)
 % locale messenger-smime @AB_CD@ %locale/@AB_CD@/messenger-smime/
   locale/@AB_CD@/messenger-smime/msgCompSMIMEOverlay.dtd                (%chrome/messenger-smime/msgCompSMIMEOverlay.dtd)
   locale/@AB_CD@/messenger-smime/msgCompSMIMEOverlay.properties         (%chrome/messenger-smime/msgCompSMIMEOverlay.properties)
   locale/@AB_CD@/messenger-smime/msgReadSMIMEOverlay.dtd                (%chrome/messenger-smime/msgReadSMIMEOverlay.dtd)
   locale/@AB_CD@/messenger-smime/msgReadSMIMEOverlay.properties         (%chrome/messenger-smime/msgReadSMIMEOverlay.properties)
   locale/@AB_CD@/messenger-smime/msgCompSecurityInfo.dtd                (%chrome/messenger-smime/msgCompSecurityInfo.dtd)
   locale/@AB_CD@/messenger-smime/msgCompSecurityInfo.properties         (%chrome/messenger-smime/msgCompSecurityInfo.properties)
--- a/mail/themes/gnomestripe/mail/newsblog/feed-subscriptions.css
+++ b/mail/themes/gnomestripe/mail/newsblog/feed-subscriptions.css
@@ -34,45 +34,28 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ****** */
 
 /* ::::: Feed Subscription styling :::::: */
 
 #subscriptionsDialog {
+  width: 40em;
+  height: 30em;
   padding: 0px;
 }
 
 #contentPane {
   margin: 9px 8px 5px;
 }
 
-#subscriptionChildren::-moz-tree-image(folderNameCol) {
-  list-style-image: url("chrome://messenger/skin/icons/folder-pane.png");
-  -moz-image-region: rect(128px 16px 144px 0px);
+#rssFeedInfoBox {
+  border: 1px solid ThreeDShadow;
+  margin: 4px;
+  padding-top: 4px;
+  background-color: #EAE8E4;
 }
 
-#subscriptionChildren::-moz-tree-image(folderNameCol, container) {
-  background-position: center center;
-  background-repeat: no-repeat;
-  background-color: transparent;
-  background-attachment: scroll;
-  background-clip: border-box;
-  -moz-background-inline-policy: continuous;
-  -moz-background-origin: border;
-  background-image: url("moz-icon://stock/gtk-directory?size=menu");
-  list-style-image: url("chrome://messenger/skin/icons/folder-blank.png");
-
-  -moz-margin-end: 2px;
+#statusContainerBox {
+  height: 24px;
 }
 
-#rssFeedInfoBox {
-  border: 1px solid ThreeDShadow;
-  border-radius: 0px;
-  margin: 4px;
-  padding: 0px;
-}
-
-#backgroundBox {
-  background-color: #FFFFFF;
-  opacity: 0.5;
-}
--- a/mail/themes/pinstripe/mail/newsblog/feed-subscriptions.css
+++ b/mail/themes/pinstripe/mail/newsblog/feed-subscriptions.css
@@ -31,34 +31,26 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ****** */
 
-/* ::::: Feed Subscription UI icons :::::: */
+/* ::::: Feed Subscription styling :::::: */
 
 #subscriptionsDialog { 
+  width: 40em;
+  height: 30em;
   padding: 14px;
 }
 
-#subscriptionChildren::-moz-tree-image(folderNameCol) {
-  -moz-margin-end: 2px;
-  list-style-image: url("chrome://messenger/skin/icons/folder-newsgroup.png");
-}
-
-#subscriptionChildren::-moz-tree-image(folderNameCol, container) {
-  list-style-image: url("chrome://messenger/skin/icons/folder-closed.png");
-}
-
 #rssFeedInfoBox {
   border: 1px solid ThreeDShadow;
-  border-radius: 0px;
   margin: 4px;
-  padding: 0px;
+  padding-top: 4px;
+  background-color: #EAE8E4;
 }
 
-#backgroundBox {
-  background-color: #FFFFFF; 
-  opacity: 0.5;
+#statusContainerBox {
+  height: 24px;
 }
--- a/mail/themes/qute/mail/newsblog/feed-subscriptions.css
+++ b/mail/themes/qute/mail/newsblog/feed-subscriptions.css
@@ -31,44 +31,30 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ****** */
 
-/* ::::: Feed Subscription UI icons :::::: */
+/* ::::: Feed Subscription styling :::::: */
 
-#subscriptionsDialog { 
+#subscriptionsDialog {
+  width: 40em;
+  height: 30em;
   padding: 0px;
 }
 
 #contentPane {
   margin: 9px 8px 5px;
 }
 
-#subscriptionChildren::-moz-tree-image(folderNameCol) {
-  -moz-margin-end: 2px;
-  list-style-image: url("chrome://messenger-newsblog/skin/icons/rss-feed.png");
-  -moz-image-region: rect(0 16px 16px 0);
-}
-
-#subscriptionChildren::-moz-tree-image(folderNameCol, container) {
-  list-style-image: url("chrome://messenger/skin/icons/folder.png");
-  -moz-image-region: rect(0 16px 16px 0);
+#rssFeedInfoBox {
+  border: 1px solid ThreeDShadow;
+  margin: 4px;
+  padding-top: 4px;
+  background-color: #EAE8E4;
 }
 
-#subscriptionChildren::-moz-tree-image(folderNameCol, container, open) {
-  -moz-image-region: rect(0 32px 16px 16px);
+#statusContainerBox {
+  height: 24px;
 }
-
-#rssFeedInfoBox {
-  border: 1px solid ThreeDShadow;
-  border-radius: 0px;
-  margin: 4px;
-  padding: 0px;
-}
-
-#backgroundBox {
-  background-color: #FFFFFF; 
-  opacity: 0.5;
-}
--- a/mailnews/extensions/newsblog/content/am-newsblog.xul
+++ b/mailnews/extensions/newsblog/content/am-newsblog.xul
@@ -111,11 +111,11 @@
               prefstring="mail.server.%serverkey%.empty_trash_on_exit"/>
 
     <separator class="thin"/>
 
     <hbox align="center">
       <spacer flex="1"/>
       <button label="&manageSubscriptions.label;"
               accesskey="&manageSubscriptions.accesskey;"
-              oncommand="openSubscriptionsDialog(null, gIncomingServer);"/>
+              oncommand="openSubscriptionsDialog(gIncomingServer.rootFolder);"/>
     </hbox>
 </page>
deleted file mode 100644
--- a/mailnews/extensions/newsblog/content/edittree.xml
+++ /dev/null
@@ -1,177 +0,0 @@
-<?xml version="1.0"?>
-
-<bindings id="treeEditBindings"
-   xmlns="http://www.mozilla.org/xbl"
-   xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
-  <binding id="edittree" extends="chrome://global/content/bindings/tree.xml#tree">
-    <content>
-      <children includes="treecols"/>
-      <xul:stack flex="1">
-        <xul:treerows class="tree-rows" flex="1">
-          <children/>
-        </xul:treerows>
-        <xul:textbox ileattr="text" left="0" top="0" hidden="true"/>
-      </xul:stack>
-    </content>
-    <implementation>
-      <field name="_editOriginalValue">0</field>
-      <field name="_editRow">-1</field>
-      <field name="_editCol">null</field>
-      <field name="onAccept">null</field>
-      <method name="setEditMode">
-        <parameter name="x"/>
-        <parameter name="y"/>
-        <parameter name="val"/>
-        <body>
-        <![CDATA[
-          var txt = document.getAnonymousElementByAttribute(this, "ileattr", "text");
-          if (val){
-            if (x < 0) return;
-
-            var originalValue = this.view.getCellText(x,y);
-            var cellnode = this.getCellNodeAt(x,y);
-            if (!(cellnode || this.view.isEditable(x,y))) return;
-
-            if (this._editRow >= 0) this._assignValueToCell(txt.value,true);
-
-            if (cellnode && cellnode.getAttribute("readonly")) return;
-            txt.removeAttribute("hidden");
-
-            var treeBox = this.treeBoxObject;
-            var outx = {}, outy = {}, outwidth = {}, outheight = {};
-            var coords = treeBox.getCoordsForCellItem(x,y,"cell",outx,outy,outwidth,outheight);
-
-            this._editRow = x;
-            this._editCol = y;
-
-            txt.setAttribute("left",outx.value-3);
-            txt.setAttribute("top",outy.value-3);
-            txt.setAttribute("height",outheight.value);
-
-            txt.setAttribute("width",outwidth.value - outy.value);
-
-            this._editOriginalValue = originalValue;
-            if (cellnode) cellnode.setAttribute("label","");
-            this.view.setCellText(x,y,"");
-
-            txt.value = originalValue;
-            txt.select();
-            this.setAttribute("editing","true");
-
-            txt.addEventListener("keypress", this.fieldKeyDown, false);
-            txt.addEventListener("blur", this.fieldChange, true);
-          }
-          else {
-            this.removeAttribute("editing");
-
-            txt.setAttribute("hidden","true");
-            txt.removeEventListener("keypress", this.fieldKeyDown, false);
-            txt.removeEventListener("blur", this.fieldChange, true);
-            txt.blur();
-          }
-        ]]>
-        </body>
-      </method>
-      <method name="getCellNodeAt">
-        <parameter name="row"/>
-        <parameter name="col"/>
-        <body>
-          var view;
-          try {
-            view = this.contentView;
-          } catch (ex){}
-          if (view){
-            var elem = view.getItemAtIndex(row);
-            if (elem){
-              var pos = ((document.getElementById(col).ordinal - 1) >> 1);
-              return elem.firstChild.childNodes[pos];
-            }
-          }
-          return null;
-        </body>
-      </method>
-      <method name="fieldKeyDown">
-        <parameter name="aEvent"/>
-        <body>
-        <![CDATA[
-          var tree = aEvent.target;
-          while (tree && tree.tagName != "tree") tree = tree.parentNode;
-          if (aEvent.keyCode == 13){
-            tree._assignValueToCell(this.value,true);
-          }
-          if (aEvent.keyCode == 27){
-            tree._assignValueToCell(tree._editOriginalValue,false);
-          }
-          aEvent.stopPropagation();
-        ]]>
-        </body>
-      </method>
-      <method name="_assignValueToCell">
-        <parameter name="value"/>
-        <parameter name="acceptMode"/>
-        <body>
-        <![CDATA[
-          var rdf = Components
-              .classes["@mozilla.org/rdf/rdf-service;1"]
-                .getService(Components.interfaces.nsIRDFService);
-          if (this._editRow == -1) return;
-          if (acceptMode && this.onAccept &&
-              this.onAccept(this._editRow,this._editCol,this._editOriginalValue,value))
-            return;
-
-          var cellnode = this.getCellNodeAt(this._editRow,this._editCol);
-          if (cellnode) {
-              cellnode.setAttribute("label", value);
-
-              var item = cellnode;
-              while (item && item.tagName != "treeitem")
-                  item = item.parentNode;
-  
-              if (this._editCol == "subs-name-column") {
-                  updateTitle(item.id, value);
-              }
-              else if (this._editCol == "subs-url-column") {
-                  updateURL(item.id, value);
-              }
-          }
-
-          this.view.setCellText(this._editRow,this._editCol,value);
-          this.treeBoxObject.invalidateCell(this._editRow,this._editCol);
-          this._editRow = -1;
-          this._editCol = null;
-          this.builder.rebuild();
-
-          this.setEditMode("normal");
-        ]]>
-        </body>
-      </method>
-      <method name="fieldChange">
-        <parameter name="aEvent"/>
-        <body>
-        <![CDATA[
-          var tree = aEvent.target;
-          while (tree && tree.tagName != "tree") tree = tree.parentNode;
-          tree._assignValueToCell(this.value,true);
-        ]]>
-        </body>
-      </method>
-    </implementation>
-    <handlers>
-      <handler event="click" clickcount="2" phase="capturing">
-      <![CDATA[
-        var treeBox = this.treeBoxObject;
-        var row = {};
-        var col = {};
-        var obj = {};
-        if(row.value != -1) {
-          event.stopPropagation();
-          treeBox.getCellAt(event.clientX,event.clientY,row,col,obj);
-          this.setEditMode(row.value,col.value,true);
-        }
-      ]]>
-      </handler>
-    </handlers>
-  </binding>
-
-</bindings>
deleted file mode 100644
--- a/mailnews/extensions/newsblog/content/feed-properties.js
+++ /dev/null
@@ -1,112 +0,0 @@
-/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Mail Code.
- *
- * The Initial Developer of the Original Code is
- * The Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2004
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Scott MacGregor <mscott@mozilla.org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-function onLoad()
-{ 
-  var feedLocationEl = document.getElementById('feedLocation');
-  var rssAccountMenuItem = document.getElementById('rssAccountMenuItem');
-
-  if (window.arguments[0].feedLocation)
-    feedLocationEl.value = window.arguments[0].feedLocation;  
-
-  // root the location picker to the news & blogs server
-  document.getElementById('selectFolder').setAttribute('ref', window.arguments[0].serverURI);
-
-  document.getElementById('selectFolder').selectedIndex = -1;
-  SetFolderPicker(window.arguments[0].folderURI ? window.arguments[0].folderURI : window.arguments[0].serverURI, 'selectFolder');
-
-  rssAccountMenuItem.label = window.arguments[0].serverPrettyName;
-  rssAccountMenuItem.value = window.arguments[0].serverURI;
-
-  // set quick mode value
-  document.getElementById('quickMode').checked = window.arguments[0].quickMode;
-
-  if (!window.arguments[0].newFeed)
-  {
-    // if we are editing an existing feed, disable the top level account
-    rssAccountMenuItem.setAttribute('disabled', 'true');
-    feedLocationEl.setAttribute('readonly', true);
-    feedLocationEl.setAttribute('context', 'copyUrlPopup');
-  }
-  else
-    feedLocationEl.setAttribute('context', 'pasteUrlPopup');
-}
-
-function onOk()
-{
-  var feedLocation = document.getElementById('feedLocation').value;
-  // trim leading and trailing white space from the url
-  feedLocation = feedLocation.trim();
-
-  window.arguments[0].feedLocation = feedLocation;
-  window.arguments[0].folderURI = document.getElementById('selectFolder').getAttribute("uri");
-  window.arguments[0].quickMode = document.getElementById('quickMode').checked;
-  window.arguments[0].result = true;
-
-  return true;
-}
-
-function PickedMsgFolder(selection,pickerID)
-{
-  SetFolderPicker(selection.value, pickerID);
-}   
-
-function SetFolderPicker(uri,pickerID)
-{
-  var picker = document.getElementById(pickerID);
-  var msgfolder = GetMsgFolderFromUri(uri, true);
-  if (!msgfolder) 
-    return;
-
-  picker.setAttribute("label",msgfolder.name);
-  picker.setAttribute("uri",uri);
-}
-
-// CopyWebsiteAddress takes the website address title button, extracts
-// the website address we stored in there and copies it to the clipboard
-function CopyWebsiteAddress(websiteAddressNode)
-{
-  if (websiteAddressNode)
-  {
-    var websiteAddress = websiteAddressNode.value;
-    var contractid = "@mozilla.org/widget/clipboardhelper;1";
-    var iid = Components.interfaces.nsIClipboardHelper;
-    var clipboard = Components.classes[contractid].getService(iid);
-    clipboard.copyString(websiteAddress);
-  }
-}
deleted file mode 100644
--- a/mailnews/extensions/newsblog/content/feed-properties.xul
+++ /dev/null
@@ -1,146 +0,0 @@
-<?xml version="1.0"?>
-# -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
-# ***** BEGIN LICENSE BLOCK *****
-# Version: MPL 1.1/GPL 2.0/LGPL 2.1
-#
-# The contents of this file are subject to the Mozilla Public License Version
-# 1.1 (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-# http://www.mozilla.org/MPL/
-#
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-# for the specific language governing rights and limitations under the
-# License.
-#
-# The Original Code is Thunderbird RSS Feed Properties UI
-#
-# The Initial Developer of the Original Code is
-# The Mozilla Foundation.
-# Portions created by the Initial Developer are Copyright (C) 2005
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#  Scott MacGregor <mscott@mozilla.org>
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either the GNU General Public License Version 2 or later (the "GPL"), or
-# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-# in which case the provisions of the GPL or the LGPL are applicable instead
-# of those above. If you wish to allow use of your version of this file only
-# under the terms of either the GPL or the LGPL, and not to allow others to
-# use your version of this file under the terms of the MPL, indicate your
-# decision by deleting the provisions above and replace them with the notice
-# and other provisions required by the GPL or the LGPL. If you do not delete
-# the provisions above, a recipient may use your version of this file under
-# the terms of any one of the MPL, the GPL or the LGPL.
-#
-# ***** END LICENSE BLOCK ******
-
-<?xml-stylesheet href="chrome://messenger/skin/" type="text/css"?>
-
-<!DOCTYPE dialog SYSTEM "chrome://messenger-newsblog/locale/feed-properties.dtd">
-
-<dialog id="feedPropertyDialog"
-  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-  xmlns:nc="http://home.netscape.com/NC-rdf#"
-  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-  title="&window.title;"
-#ifdef XP_MACOSX
-  style="width: 40em;"
-#else
-  style="width: 33em;"
-#endif
-  buttons="accept,cancel"
-  onload="onLoad();"
-  ondialogaccept="return onOk();">
-
-  <script type="application/javascript" src="feed-properties.js"/>
-  <script type="application/javascript" src="chrome://messenger/content/widgetglue.js"/>
-  <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
-
-  <menupopup id="copyUrlPopup" popupanchor="bottomleft">
-    <menuitem label="&copyLinkCmd.label;" accesskey="&copyLinkCmd.accesskey;"
-              oncommand="CopyWebsiteAddress(document.popupNode)"/>
-  </menupopup>
-
-  <menupopup id="pasteUrlPopup" popupanchor="bottomleft">
-    <menuitem label="&pasteLinkCmd.label;" accesskey="&pasteLinkCmd.accesskey;"
-              oncommand="goDoCommand('cmd_paste')"/>
-  </menupopup>
-
-  <grid flex="1">
-    <columns>
-      <column/>
-      <column  flex="1"/>
-    </columns>
-
-    <rows>
-      <row>
-        <label value="&feedLocation.label;" accesskey="&feedLocation.accesskey;" control="feedLocation"/>
-        <textbox id="feedLocation" class="uri-element"/>
-      </row>
-
-      <row>
-        <separator class="thin"/>
-      </row>
-
-      <row>
-        <label value="&feedFolder.label;" accesskey="&feedFolder.accesskey;" control="selectFolder"/>
-        <menulist id="selectFolder"
-              containment="http://home.netscape.com/NC-rdf#child"
-              sortResource="http://home.netscape.com/NC-rdf#FolderTreeName"
-              sortDirection="ascending"
-              datasources="rdf:msgaccountmanager rdf:mailnewsfolders"
-              ref="...">
-            <menupopup>
-              <menuitem id="rssAccountMenuItem"
-                        oncommand="PickedMsgFolder(event.target, 'selectFolder')"/>
-              <menuseparator/>
-            </menupopup>
-
-            <template>
-              <!-- cheat and use the CanRename property to make sure we don't list the Trash folder as a possible feed folder -->
-              <rule nc:CanFileMessages="true" iscontainer="true" isempty="false" nc:CanRename="true">
-                <menupopup>
-                  <menu uri="..."
-                        class="folderMenuItem menu-iconic"
-                        oncommand="PickedMsgFolder(event.target, 'selectFolder')"
-                        SpecialFolder="rdf:http://home.netscape.com/NC-rdf#SpecialFolder"
-                        BiffState="rdf:http://home.netscape.com/NC-rdf#BiffState"
-                        IsServer="rdf:http://home.netscape.com/NC-rdf#IsServer"
-                        IsSecure="rdf:http://home.netscape.com/NC-rdf#IsSecure"
-                        ServerType="rdf:http://home.netscape.com/NC-rdf#ServerType"
-                        label="rdf:http://home.netscape.com/NC-rdf#Name">
-                    <menupopup class="menulist-menupopup">
-                      <menuitem label="&choosethisfolder.label;"
-                                oncommand="PickedMsgFolder(event.target.parentNode.parentNode,'selectFolder')"/>
-                      <menuseparator/>
-                    </menupopup>
-                  </menu>
-                </menupopup>
-              </rule>
-              <rule nc:CanFileMessages="true" nc:CanRename="true">
-                <menupopup>
-                     <menuitem uri="..." value="..."
-                      class="folderMenuItem menuitem-iconic"
-                      oncommand="PickedMsgFolder(event.target,'selectFolder')"
-                      SpecialFolder="rdf:http://home.netscape.com/NC-rdf#SpecialFolder"
-                      BiffState="rdf:http://home.netscape.com/NC-rdf#BiffState"
-                      IsServer="rdf:http://home.netscape.com/NC-rdf#IsServer"
-                      IsSecure="rdf:http://home.netscape.com/NC-rdf#IsSecure"
-                      ServerType="rdf:http://home.netscape.com/NC-rdf#ServerType"
-                      label="rdf:http://home.netscape.com/NC-rdf#Name"/>
-                 </menupopup>
-              </rule>
-            </template>
-        </menulist>
-      </row>
-    </rows>
-  </grid>
-
-  <separator class="thin"/>
-
-  <checkbox id="quickMode" accesskey="&quickMode.accesskey;" label="&quickMode.label;"/>
-
-</dialog>
--- a/mailnews/extensions/newsblog/content/feed-subscriptions.js
+++ b/mailnews/extensions/newsblog/content/feed-subscriptions.js
@@ -32,354 +32,555 @@
 # use your version of this file under the terms of the MPL, indicate your
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK ******
 
-const IPS = Components.interfaces.nsIPromptService;
-const nsIDragService = Components.interfaces.nsIDragService;
-const kRowIndexUndefined = -1;
+var gFeedSubscriptionsWindow = {
+  get mTree() { return document.getElementById("rssSubscriptionsList"); },
+
+  mFeedContainers: [],
+  mRSSServer     : null,
+  mActionMode    : null,
+  kSubscribeMode : 1,
+  kUpdateMode    : 2,
+  kMoveMode      : 3,
+
+  onLoad: function ()
+  {
+    // Extract the folder argument.
+    let folder;
+    if (window.arguments && window.arguments[0].folder)
+      folder = window.arguments[0].folder;
+
+    this.refreshSubscriptionView(folder)
 
-var gFeedSubscriptionsWindow = {
-  mFeedContainers   : [],
-  mTree             : null,
-  mBundle           : null,
-  mRSSServer        : null,
+    let win = Services.wm.getMostRecentWindow("mail:3pane");
+    if (win)
+    {
+      win.FeedFolderNotificationService =
+        Cc["@mozilla.org/messenger/msgnotificationservice;1"].
+        getService(Ci.nsIMsgFolderNotificationService);
+      win.FeedFolderNotificationService.addListener(this.FolderListener,
+        Ci.nsIMsgFolderNotificationService.folderAdded |
+        Ci.nsIMsgFolderNotificationService.folderDeleted |
+        Ci.nsIMsgFolderNotificationService.folderRenamed |
+        Ci.nsIMsgFolderNotificationService.folderMoveCopyCompleted);
+    }
+  },
+
+  onUnload: function ()
+  {
+    let dismissDialog = true;
+
+    // If we are in the middle of subscribing to a feed, inform the user that
+    // dismissing the dialog right now will abort the feed subscription.
+    if (this.mActionMode == this.kSubscribeMode)
+    {
+      let pTitle = FeedUtils.strings.GetStringFromName(
+                     "subscribe-cancelSubscriptionTitle");
+      let pMessage = FeedUtils.strings.GetStringFromName(
+                       "subscribe-cancelSubscription");
+      dismissDialog =
+        !(Services.prompt.confirmEx(window, pTitle, pMessage,
+                                    Ci.nsIPromptService.STD_YES_NO_BUTTONS,
+                                    null, null, null, null, { }));
+    }
+
+    if (dismissDialog)
+    {
+      let win = Services.wm.getMostRecentWindow("mail:3pane");
+      if (win)
+        delete win.FeedFolderNotificationService;
+    }
 
-  init: function ()
-  {  
-    // extract the server argument
-    if (window.arguments[0].server)
-      this.mRSSServer = window.arguments[0].server;
+    return dismissDialog;
+  },
 
-    var docshell = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-                        .getInterface(Components.interfaces.nsIWebNavigation)
-                        .QueryInterface(Components.interfaces.nsIDocShell);        
-    docshell.allowAuth = true;
+  refreshSubscriptionView: function(aSelectFolder)
+  {
+    let item = this.mView.currentItem;
+    let firstVisRow, lastVisRow, curFirstVisRow;
+    if (this.mView.treeBox)
+      firstVisRow = this.mView.treeBox.getFirstVisibleRow();
+    this.loadSubscriptions();
+    this.mTree.view = this.mView;
+    if (aSelectFolder)
+      // Ensure dialog is fully loaded before selecting, to get visible row.
+      setTimeout(function() {
+        gFeedSubscriptionsWindow.selectFolder(aSelectFolder);
+      }, 0);
+    else
+    {
+      // If no folder to select, try to select the pre rebuild selection, in
+      // an existing window.  For folderpane changes in a feed account.
+      if (item)
+      {
+        if (item.container)
+        {
+          if (!this.selectFolder(item.folder))
+            // The item no longer exists, an ancestor folder was deleted or
+            // renamed/moved.
+            this.selectFolder(item.folder.rootFolder);
+        }
+        else
+        {
+          // If selecting a prior selected feed, get its folder from the db
+          // in case an ancestor folder was renamed/moved.
+          let itemResource = rdf.GetResource(item.url);
+          let ds = getSubscriptionsDS(item.parentFolder.server);
+          let itemFolder = ds.GetTarget(itemResource, FZ_DESTFOLDER, true);
+          if (itemFolder)
+          {
+            itemFolder = itemFolder.QueryInterface(Ci.nsIMsgFolder);
+            this.selectFeed({folder: itemFolder, url: item.url}, null);
+          }
+          else
+            // The item no longer exists, an ancestor folder was deleted.
+            this.selectFolder(item.parentFolder.rootFolder);
+        }
 
-    this.mTree = document.getElementById("rssSubscriptionsList");
-    this.mBundle = document.getElementById("bundle_newsblog");
-
-    this.loadSubscriptions();
-    this.mTree.treeBoxObject.view = this.mView;
-
-    if (window.arguments[0].folder)
-      this.selectFolder(window.arguments[0].folder);
+        curFirstVisRow = this.mView.treeBox.getFirstVisibleRow();
+        lastVisRow = this.mView.treeBox.getLastVisibleRow();
+        if (firstVisRow >= 0 && //firstVisRow >= curFirstVisRow &&
+            this.mView.rowCount - lastVisRow > firstVisRow - curFirstVisRow)
+          this.mView.treeBox.scrollToRow(firstVisRow);
+        else
+          this.mView.treeBox.ensureRowIsVisible(this.mView.rowCount - 1);
+        FeedUtils.log.debug("refreshSubscriptionView: curIndex:curFirstVisRow:" +
+                            "firstVisRow:lastVisRow:rowCount - " +
+                            this.mView.selection.currentIndex+":"+
+                            curFirstVisRow+":"+
+                            firstVisRow+":"+lastVisRow+":"+this.mView.rowCount);
+      }
+    }
   },
 
-  uninit: function ()
+  mView:
   {
-    var dismissDialog = true;
+    _atoms: [],
+    _getAtomFor: function(aName) {
+      if (!this._atoms[aName])
+        this._atoms[aName] = this._makeAtom(aName);
+      return this._atoms[aName];
+    },
+  
+    _makeAtom: function(aString) {
+      return Cc["@mozilla.org/atom-service;1"].
+             getService(Ci.nsIAtomService).
+             getAtom(aString);
+    },
 
-    // if we are in the middle of subscribing to a feed, inform the user that 
-    // dismissing the dialog right now will abort the feed subscription.
-    // cheat and look at the disabled state of the add button to determine if we are in the middle of a new subscription
-    if (document.getElementById('addFeed').getAttribute('disabled'))
-    {
-      var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(IPS);
-      var newsBlogBundle = document.getElementById("bundle_newsblog");
-      dismissDialog = !(promptService.confirmEx(window, newsBlogBundle.getString('subscribe-cancelSubscriptionTitle'), 
-                                       newsBlogBundle.getString('subscribe-cancelSubscription'), 
-                                       IPS.STD_YES_NO_BUTTONS,
-                                       null, null, null, null, { }));    
-    }  
-    return dismissDialog;
-  },
-  
-  mView: 
-  {
-    mRowCount   : 0,
+    kRowIndexUndefined: -1,
 
-    get rowCount() 
-    { 
-      return this.mRowCount; 
-    },
-    
-    getItemAtIndex: function (aIndex)
-    {
-      if (aIndex < 0 ||
-          aIndex >= gFeedSubscriptionsWindow.mFeedContainers.length)
-        return null;
-      return gFeedSubscriptionsWindow.mFeedContainers[aIndex];
+    get currentItem() {
+      // Get the current selection, if any.
+      let seln = this.selection;
+      let currentSelectionIndex =  seln ? seln.currentIndex : null;
+      let item;
+      if (currentSelectionIndex)
+        item = this.getItemAtIndex(currentSelectionIndex);
+
+      return item;
     },
 
-    removeItemAtIndex: function (aIndex, aCount)
+    /* nsITreeView */
+    treeBox: null,
+
+    mRowCount: 0,
+    get rowCount()                         { return this.mRowCount; },
+
+    _selection: null,
+    get selection ()                       { return this._selection; },
+    set selection (val)                    { return this._selection = val; },
+
+    setTree: function(aTreebox)            { this.treeBox = aTreebox; },
+    isSeparator: function(aRow)            { return false; },
+    isSorted: function()                   { return false; },
+    isSelectable: function(aRow, aColumn)  { return false; },
+    isEditable: function (aRow, aColumn)   { return false; },
+
+    getImageSrc: function(aRow, aCol)      { return null; },
+    getProgressMode : function(aRow, aCol) {},
+    cycleHeader: function(aCol)            {},
+    cycleCell: function(aRow, aCol)        {},
+    selectionChanged: function()           {},
+    performAction: function(aAction)       {},
+    performActionOnRow: function (aAction, aRow)       {},
+    performActionOnCell: function(aAction, aRow, aCol) {},
+    getRowProperties: function(aRow, aProperties)      {},
+    getColumnProperties: function(aCol, aProperties)   {},
+    getCellValue: function (aRow, aColumn)             {},
+    setCellValue: function (aRow, aColumn, aValue)     {},
+    setCellText: function (aRow, aColumn, aValue)      {},
+
+    getCellProperties: function (aRow, aColumn, aProperties) {
+//      aProperties.AppendElement(this._getAtomFor("folderNameCol"));
+      let item = this.getItemAtIndex(aRow);
+      let folder = item && item.folder ? item.folder : null;
+      if (folder)
+      {
+        if (folder.isServer)
+        {
+          aProperties.AppendElement(this._getAtomFor("serverType-rss"));
+          aProperties.AppendElement(this._getAtomFor("isServer-true"));
+        }
+        else
+          // It's a feed folder.
+          aProperties.AppendElement(this._getAtomFor("livemark"));
+      }
+      else
+        // It's a feed.
+        aProperties.AppendElement(this._getAtomFor("serverType-rss"));
+    },
+
+    isContainer: function (aRow)
     {
-      var itemToRemove = this.getItemAtIndex(aIndex);
-      if (!itemToRemove) 
+      let item = this.getItemAtIndex(aRow);
+      return item ? item.container : false;
+    },
+
+    isContainerOpen: function (aRow)
+    { 
+      let item = this.getItemAtIndex(aRow);
+      return item ? item.open : false;
+    },
+
+    isContainerEmpty: function (aRow)
+    { 
+      let item = this.getItemAtIndex(aRow);
+      if (!item) 
+        return false;
+
+      return item.children.length == 0;
+    },
+
+    getItemAtIndex: function (aRow)
+    {
+      if (aRow < 0 || aRow >= gFeedSubscriptionsWindow.mFeedContainers.length)
+        return null;
+
+      return gFeedSubscriptionsWindow.mFeedContainers[aRow];
+    },
+
+    removeItemAtIndex: function (aRow, aNoSelect)
+    {
+      let itemToRemove = this.getItemAtIndex(aRow);
+      if (!itemToRemove)
         return;
 
-      var parentIndex = this.getParentIndex(aIndex);
-      if (parentIndex != kRowIndexUndefined)
+      let parentIndex = this.getParentIndex(aRow);
+      let hasNextSibling = this.hasNextSibling(aRow, aRow);
+      if (parentIndex != this.kRowIndexUndefined)
       {
-        var parent = this.getItemAtIndex(parentIndex);
+        let parent = this.getItemAtIndex(parentIndex);
         if (parent)
         {
-          for (var index = 0; index < parent.children.length; index++)
+          for (let index = 0; index < parent.children.length; index++)
             if (parent.children[index] == itemToRemove)
             {
               parent.children.splice(index, 1);
               break;
             }
         }
       }
 
-      // now remove it from our view
-      gFeedSubscriptionsWindow.mFeedContainers.splice(aIndex, 1);
+      // Now remove it from our view.
+      gFeedSubscriptionsWindow.mFeedContainers.splice(aRow, 1);
 
-      // now invalidate the correct tree rows
-      var tbo = gFeedSubscriptionsWindow.mTree.treeBoxObject;
-
+      // Now invalidate the correct tree rows.
       this.mRowCount--;
-      tbo.rowCountChanged(aIndex, -1);
+      this.treeBox.rowCountChanged(aRow, -1);
 
-      // now update the selection position
-      if (aIndex < gFeedSubscriptionsWindow.mFeedContainers.length)
-        this.selection.select(aIndex);
-      else 
-        this.selection.clearSelection();
+      // Now update the selection position, unless noSelect (selection is
+      // done later or not at all).  If the item is the last child, select the
+      // parent.  Otherwise select the next sibling.
+      if (!aNoSelect) {
+        if (aRow <= gFeedSubscriptionsWindow.mFeedContainers.length)
+          this.selection.select(hasNextSibling ? aRow : aRow - 1);
+        else
+          this.selection.clearSelection();
+      }
 
-      // now refocus the tree
+      // Now refocus the tree.
       gFeedSubscriptionsWindow.mTree.focus();
     },
-    
-    getCellText: function (aIndex, aColumn)
+
+    getCellText: function (aRow, aColumn)
     {
-      var item = this.getItemAtIndex(aIndex);
+      let item = this.getItemAtIndex(aRow);
       return (item && aColumn.id == "folderNameCol") ? item.name : "";
     },
 
-    _selection: null, 
-    get selection () { return this._selection; },
-    set selection (val) { this._selection = val; return val; },
-    getRowProperties: function (aIndex, aProperties) {},
-    getCellProperties: function (aIndex, aColumn, aProperties) {},
-    getColumnProperties: function (aColumn, aProperties) {},
-
-    isContainer: function (aIndex)
-    {
-      var item = this.getItemAtIndex(aIndex);
-      return item ? item.container : false;
-    },
-
-    isContainerOpen: function (aIndex) 
+    canDrop: function (aRow, aOrientation)
     { 
-      var item = this.getItemAtIndex(aIndex);
-      return item ? item.open : false;
+      let dropResult = this.extractDragData();
+      return (aOrientation == Ci.nsITreeView.DROP_ON) &&
+             dropResult.canDrop &&
+             (dropResult.url || dropResult.index != this.kRowIndexUndefined);
     },
 
-    isContainerEmpty: function (aIndex) 
-    { 
-      var item = this.getItemAtIndex(aIndex);
-      if (!item) 
-        return false;
-      return item.children.length == 0;
-    },
-
-    isSeparator: function (aIndex) { return false; },    
-    isSorted: function (aIndex) { return false; },    
-    
-    canDrop: function (aIndex, aOrientation) 
-    { 
-      var dropResult = this.extractDragData();
-      return (aOrientation == Components.interfaces.nsITreeView.DROP_ON) && 
-                              dropResult.canDrop && (dropResult.url || (dropResult.index != kRowIndexUndefined)); 
-    },
-    
-    mDropUrl: "",
-    mDropFolderUrl: "",
-    drop: function (aIndex, aOrientation) 
-    {  
-      var results = this.extractDragData();
+    drop: function (aRow, aOrientation)
+    {
+      let win = gFeedSubscriptionsWindow;
+      let results = this.extractDragData();
       if (!results.canDrop)
         return;
 
+      // Preselect the drop folder.
+      this.selection.select(aRow);
+
       if (results.url)
       {
-        var folderItem = this.getItemAtIndex(aIndex);
-        // don't freeze the app that initiaed the drop just because we are in a loop waiting for the user
-        // to dimisss the add feed dialog....
-        this.mDropUrl = results.url;
-        this.mDropFolderUrl = folderItem.url;
-        setTimeout(processDrop, 0);
-      } 
-      else if (results.index != kRowIndexUndefined)
-        gFeedSubscriptionsWindow.moveFeed(results.index, aIndex);
+        // Don't freeze the app that initiated the drop just because we are
+        // in a loop waiting for the user to dimisss the add feed dialog.
+        setTimeout(function() {
+          win.addFeed(results.url, null, true, win.kSubscribeMode);
+        }, 0);
+        let folderItem = this.getItemAtIndex(aRow);
+        FeedUtils.log.debug("drop: folder, url - " +
+                            folderItem.folder.name+", "+results.url);
+      }
+      else if (results.index != this.kRowIndexUndefined)
+      {
+        win.moveFeed(results.index, aRow);
+      }
     },
-    
-    //  helper function for drag and drop
+
+    // Helper function for drag and drop.
     extractDragData: function()
     {
-      var canDrop = false;
-      var urlToDrop, sourceUri;
-      var sourceIndex = kRowIndexUndefined;
-      var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].getService().QueryInterface(nsIDragService);
-      var dragSession = dragService.getCurrentSession();
+      let canDrop = false;
+      let urlToDrop, sourceUri;
+      let sourceIndex = this.kRowIndexUndefined;
+      let dragService = Cc["@mozilla.org/widget/dragservice;1"].
+                        getService().QueryInterface(Ci.nsIDragService);
+      let dragSession = dragService.getCurrentSession();
 
-      var transfer = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
+      let transfer = Cc["@mozilla.org/widget/transferable;1"].
+                     createInstance(Ci.nsITransferable);
       transfer.addDataFlavor("text/x-moz-url");
       transfer.addDataFlavor("text/x-moz-feed-index");
-    
-      dragSession.getData (transfer, 0);
-      var dataObj = new Object();
-      var flavor = new Object();
-      var len = new Object();
+
+      dragSession.getData(transfer, 0);
+      let dataObj = new Object();
+      let flavor = new Object();
+      let len = new Object();
 
       try {
-        transfer.getAnyTransferData(flavor, dataObj, len);   
-      } catch (ex) { return { canDrop: false, url: "" }; }
+        transfer.getAnyTransferData(flavor, dataObj, len);
+      }
+      catch (ex) {
+        return { canDrop: false, url: "" };
+      }
 
       if (dataObj.value)
       {
-        dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);      
-        sourceUri = dataObj.data.substring(0, len.value); // pull the URL out of the data object
-     
-        if (flavor.value == 'text/x-moz-url')
+        dataObj = dataObj.value.QueryInterface(Ci.nsISupportsString);
+        // Pull the URL out of the data object.
+        sourceUri = dataObj.data.substring(0, len.value);
+
+        if (flavor.value == "text/x-moz-url")
         {
-          var uri = Components.classes["@mozilla.org/network/standard-url;1"].createInstance(Components.interfaces.nsIURI);
+          let uri = Cc["@mozilla.org/network/standard-url;1"].
+                    createInstance(Ci.nsIURI);
           uri.spec = sourceUri.split("\n")[0];
-            
+
           if (uri.schemeIs("http") || uri.schemeIs("https"))
           {
             urlToDrop = uri.spec;
             canDrop = true;
           }
-        } 
-        else if (flavor.value == 'text/x-moz-feed-index')
+        }
+        else if (flavor.value == "text/x-moz-feed-index")
         {
           sourceIndex = parseInt(sourceUri);
           canDrop = true;
         }
       }  // if dataObj.value
 
       return { canDrop: canDrop, url: urlToDrop, index: sourceIndex };
     },
 
-    getParentIndex: function (aIndex) 
+    getParentIndex: function (aRow)
     {
-      var item = this.getItemAtIndex(aIndex);
+      let item = this.getItemAtIndex(aRow);
 
       if (item)
       {
-        for (var index = aIndex; index >= 0; index--)
-          if (gFeedSubscriptionsWindow.mFeedContainers[index].level <  item.level)
+        for (let index = aRow; index >= 0; index--)
+          if (gFeedSubscriptionsWindow.mFeedContainers[index].level < item.level)
             return index;
       }
-   
-      return kRowIndexUndefined;
-    },    
-    hasNextSibling: function (aParentIndex, aIndex) 
-    { 
-      var item = this.getItemAtIndex(aIndex);
-      if (item) 
-      {
-        // if the next node in the view has the same level as us, then we must have a next sibling...
-        if (aIndex + 1 < gFeedSubscriptionsWindow.mFeedContainers.length )
-          return this.getItemAtIndex(aIndex + 1).level == item.level;
+
+      return this.kRowIndexUndefined;
+    },
+
+    hasNextSibling: function(aRow, aAfterIndex) {
+      let targetLevel = this.getItemAtIndex(aRow).level;
+      let rows = gFeedSubscriptionsWindow.mFeedContainers;
+      for (let i = aAfterIndex + 1; i < rows.length; i++) {
+        if (this.getItemAtIndex(i).level == targetLevel)
+          return true;
+        if (this.getItemAtIndex(i).level < targetLevel)
+          return false;
       }
 
       return false;
     },
-    hasPreviousSibling: function (aIndex)
+
+    hasPreviousSibling: function (aRow)
     {
-      var item = this.getItemAtIndex(aIndex);
-      if (item && aIndex)
-        return this.getItemAtIndex(aIndex - 1).level == item.level;
+      let item = this.getItemAtIndex(aRow);
+      if (item && aRow)
+        return this.getItemAtIndex(aRow - 1).level == item.level;
       else
-        return false;      
+        return false;
     },
-    getLevel: function (aIndex) 
+
+    getLevel: function (aRow)
     {
-      var item = this.getItemAtIndex(aIndex);
-      if (!item) 
+      let item = this.getItemAtIndex(aRow);
+      if (!item)
         return 0;
+
       return item.level;
     },
-    getImageSrc: function (aIndex, aColumn) {},    
-    getProgressMode: function (aIndex, aColumn) {},    
-    getCellValue: function (aIndex, aColumn) {},
-    setTree: function (aTree) {},    
-    toggleOpenState: function (aIndex) 
+
+    toggleOpenState: function (aRow)
+    {
+      let item = this.getItemAtIndex(aRow);
+      if (!item)
+        return;
+
+      // Save off the current selection item.
+      let seln = this.selection;
+      let currentSelectionIndex = seln.currentIndex;
+
+      let rowsChanged = this.toggle(aRow)
+
+      // Now restore selection, ensuring selection is maintained on toggles.
+      if (currentSelectionIndex > aRow)
+        seln.currentIndex = currentSelectionIndex + rowsChanged;
+      else
+        seln.select(currentSelectionIndex);
+
+      seln.selectEventsSuppressed = false;
+    },
+
+    toggle: function (aRow)
     {
-      var item = this.getItemAtIndex(aIndex);
-      if (!item) return;
+      // Collapse the row, or build sub rows based on open states in the map.
+      let item = this.getItemAtIndex(aRow);
+      if (!item)
+        return null;
+
+      let rows = gFeedSubscriptionsWindow.mFeedContainers;
+      let rowCount = 0;
+      let multiplier;
+
+      if (item.open)
+      {
+        // Close the container.  Add up all subfolders and their descendants
+        // who may be open.
+        multiplier = -1;
+        let nextRow = aRow + 1;
+        let nextItem = rows[nextRow];
+        while (nextItem && nextItem.level > item.level)
+        {
+          rowCount++;
+          nextItem = rows[++nextRow];
+        }
 
-      // save off the current selection item
-      var seln = this.selection;
-      var currentSelectionIndex = seln.currentIndex;
+        rows.splice(aRow + 1, rowCount);
+      }
+      else
+      {
+        // Open the container.  Restore the open state of all subfolder and
+        // their descendants.
+        multiplier = 1;
+        function addDescendants(aItem)
+        {
+          for (let i = 0; i < aItem.children.length; i++)
+          {
+            rowCount++;
+            let child = aItem.children[i];
+            rows.splice(aRow + rowCount, 0, child);
+            if (child.open)
+              addDescendants(child);
+          }
+        }
 
-      var multiplier = item.open ? -1 : 1;
-      var delta = multiplier * item.children.length;
+        addDescendants(item);
+      }
+
+      let delta = multiplier * rowCount;
       this.mRowCount += delta;
 
-      if (multiplier < 0)
-        gFeedSubscriptionsWindow.mFeedContainers.splice(aIndex + 1, item.children.length);
-      else
-        for (var i = 0; i < item.children.length; i++)
-          gFeedSubscriptionsWindow.mFeedContainers.splice(aIndex + 1 + i, 0, item.children[i]);
-
-      // add or remove the children from our view
       item.open = !item.open;
-      gFeedSubscriptionsWindow.mTree.treeBoxObject.rowCountChanged(aIndex, delta);
-
-      // now restore selection
-      seln.select(currentSelectionIndex);
-      
-    },    
-    cycleHeader: function (aColumn) {},    
-    selectionChanged: function () {},    
-    cycleCell: function (aIndex, aColumn) {},    
-    isEditable: function (aIndex, aColumn) 
-    { 
-      return false; 
-    },
-    isSelectable: function (aIndex, aColumn) 
-    { 
-      return false; 
-    },
-    setCellValue: function (aIndex, aColumn, aValue) {},    
-    setCellText: function (aIndex, aColumn, aValue) {},    
-    performAction: function (aAction) {},  
-    performActionOnRow: function (aAction, aIndex) {},    
-    performActionOnCell: function (aAction, aindex, aColumn) {}
+      // Suppress the select event caused by rowCountChanged.
+      this.selection.selectEventsSuppressed = true;
+      // Add or remove the children from our view.
+      this.treeBox.rowCountChanged(aRow, delta);
+      return delta;
+    }
   },
 
   makeFolderObject: function (aFolder, aCurrentLevel)
   {
-    var folderObject =  { children : [],
+    let defaultQuickMode = aFolder.server.getBoolValue("quickMode");
+    let folderObject =  { children : [],
+                          folder   : aFolder,
                           name     : aFolder.prettiestName,
                           level    : aCurrentLevel,
-                          url      : aFolder.QueryInterface(Components.interfaces.nsIRDFResource).Value,
+                          url      : aFolder.URI,
+                          //QueryInterface(Ci.nsIRDFResource).Value,
+                          quickMode: defaultQuickMode,
                           open     : false,
                           container: true };
 
-    // If a feed has any sub folders, we should add them to the list of children.
-    var folderEnumerator = aFolder.subFolders;
+    // If a feed has any sub folders, add them to the list of children.
+    let folderEnumerator = aFolder.subFolders;
 
     while (folderEnumerator.hasMoreElements())
     {
-      var folder = folderEnumerator.getNext();
-      if ((folder instanceof Components.interfaces.nsIMsgFolder) &&
-          !folder.getFlag(Components.interfaces.nsMsgFolderFlags.Virtual))
+      let folder = folderEnumerator.getNext();
+      if ((folder instanceof Ci.nsIMsgFolder) &&
+          !folder.getFlag(Ci.nsMsgFolderFlags.Trash) &&
+          !folder.getFlag(Ci.nsMsgFolderFlags.Virtual))
+      {
         folderObject.children
                     .push(this.makeFolderObject(folder, aCurrentLevel + 1));
+      }
     }
 
-    var feeds = this.getFeedsInFolder(aFolder);
+    function sorter(a, b)
+    {
+      let sortKey = a.folder.compareSortKeys(b.folder);
+      if (sortKey)
+        return sortKey;
+      return a.name.toLowerCase() > b.name.toLowerCase();
+    }
+ 
+    folderObject.children.sort(sorter);
 
+    let feeds = this.getFeedsInFolder(aFolder);
     for (let feed in feeds)
     {
-      // Special case, if a folder only has a single feed associated with it, then just use the feed
-      // in the view and don't show the folder at all. 
-//      if (feedUrlArray.length <= 2 && !aFolder.hasSubFolders) // Note: split always adds an empty element to the array...
-//        this.mFeedContainers[aCurrentLength] = this.makeFeedObject(feed, aCurrentLevel);
-//      else // now add any feed urls for the folder
+      // Special case, if a folder only has a single feed associated with it,
+      // then just use the feed in the view and don't show the folder at all.
+      // Note: split always adds an empty element to the array...
+//      if (feedUrlArray.length <= 2 && !aFolder.hasSubFolders)
+//        this.mFeedContainers[aCurrentLength] =
+//            this.makeFeedObject(feed, aCurrentLevel);
+//      else
+        // Now add any feed urls for the folder.
         folderObject.children.push(this.makeFeedObject(feeds[feed],
                                                        aFolder,
                                                        aCurrentLevel + 1));
     }
 
     return folderObject;
   },
 
@@ -391,682 +592,1105 @@ var gFeedSubscriptionsWindow = {
       // No feedUrls in this folder.
       return;
 
     for (let url in feedUrlArray)
     {
       if (!feedUrlArray[url])
         continue;
       let feedResource = rdf.GetResource(feedUrlArray[url]);
-      let feed = new Feed(feedResource, this.mRSSServer);
+      let feed = new Feed(feedResource, aFolder.server);
       feeds.push(feed);
     }
 
     return feeds;
   },
 
   makeFeedObject: function (aFeed, aFolder, aLevel)
   {
-    // look inside the data source for the feed properties
-    var feed = { children    : [],
+    // Look inside the data source for the feed properties.
+    let feed = { children    : [],
                  parentFolder: aFolder,
                  name        : aFeed.title || aFeed.description || aFeed.url,
                  url         : aFeed.url,
+                 quickMode   : aFeed.quickMode,
                  level       : aLevel,
                  open        : false,
                  container   : false };
     return feed;
   },
 
-  loadSubscriptions: function () 
+  loadSubscriptions: function ()
   {
-    // put together an array of folders
-    var numFolders = 0;
-    this.mFeedContainers = [];
+    // Put together an array of folders.  Each feed account level folder is
+    // included as the root.
+    let numFolders = 0;
+    let feedRootFolders = [];
+    let feedContainers = [];
 
-    var folderEnumerator = this.mRSSServer.rootFolder.subFolders;
-
-    while (folderEnumerator.hasMoreElements())
+    // Get all the feed account folders.
+    let allServers = MailServices.accounts.allServers;
+    for (let i = 0; i < allServers.Count(); i++)
     {
-      var folder = folderEnumerator.getNext();
-      if ((folder instanceof Components.interfaces.nsIMsgFolder) &&
-          !folder.getFlag(Components.interfaces.nsMsgFolderFlags.Trash) &&
-          !folder.getFlag(Components.interfaces.nsMsgFolderFlags.Virtual))
-      {
-        this.mFeedContainers.push(this.makeFolderObject(folder, 0));
-        numFolders++;
-      }
+      let currentServer = allServers.QueryElementAt(i, Ci.nsIMsgIncomingServer);
+      if (currentServer && currentServer.type == "rss")
+        feedRootFolders.push(currentServer.rootFolder);
     }
+
+    feedRootFolders.forEach(function(rootFolder) {
+      feedContainers.push(this.makeFolderObject(rootFolder, 0));
+      numFolders++;
+    }, this);
+
+    this.mFeedContainers = feedContainers;
     this.mView.mRowCount = numFolders;
 
     gFeedSubscriptionsWindow.mTree.focus();
   },
 
-  selectFolder: function(aFolder)
+  /**
+   * Find the folder in the tree.  The search may be limited to subfolders of
+   * a known folder, or expanded to include the entire tree.  The first
+   * occurence of a folder URI will be selected.
+   * 
+   * @param  aFolder nsIMsgFolder - the folder to find.
+   * @param  [aSelect] boolean    - if true (default) the folder's ancestors
+   *                                will be opened and the folder selected.
+   * @param  [aParentIndex] int   - index of folder to start the search.
+   * @param  [aOpen] boolean      - if true (default) the folder is opened.
+   * 
+   * @return bool found - true if found, false if not.
+   */
+  selectFolder: function(aFolder, aSelect, aParentIndex, aOpen)
   {
-    if (aFolder.isServer)
-      return;
+    let folderURI = aFolder.URI;
+    let selectIt = aSelect == null ? true : aSelect;
+    let openIt = aOpen == null ? true : aOpen;
+    let startIndex, startItem;
+    let found = false;
 
-    var folderURI = aFolder.QueryInterface(Components.interfaces.nsIRDFResource)
-                           .Value;
+    if (aFolder.isServer || aParentIndex != null)
+      // For a server, the aParentIndex doesn't matter, they are always visible.
+      startIndex = aParentIndex;
+    else
+    {
+      // Get the folder's root parent index.
+      let index = 0;
+      for (index; index < this.mView.rowCount; index++)
+      {
+        let item = this.mView.getItemAtIndex(index);
+        if (item.url == aFolder.server.rootFolder.URI)
+          break;
+      }
+      startIndex = index;
+    }
+
+    if (!aFolder.isServer)
+      startItem = this.mView.getItemAtIndex(startIndex);
 
     function containsFolder(aItem)
     {
-      var items = aItem ? aItem.children : this.mFeedContainers;
-      for (var i = 0; i < items.length; i++) {
-        if (items[i].url == folderURI ||
-            item.container && containsFolder(items[i]))
-          return true;
+      // Search for the folder.  If it's found, set the open state on all
+      // ancestor folders.  A toggle() rebuilds the view rows to match the map.
+      if (aItem.url == folderURI)
+        return true;
+
+      for (let i = 0; i < aItem.children.length; i++) {
+        if (aItem.children[i].container && containsFolder(aItem.children[i]))
+          return aItem.children[i].open = true;
       }
+
       return false;
     }
 
-    for (var index = 0; index < this.mView.rowCount; index++)
+    if (startItem)
     {
-      var item = this.mView.getItemAtIndex(index);
-      if (item.url == folderURI || containsFolder(item))
+      // Find a folder with a specific parent.
+      found = containsFolder(startItem);
+      if (!found)
+        return false;
+
+      if (!selectIt)
+        return true;
+
+      if (startItem.open)
+        this.mView.toggle(startIndex);
+      this.mView.toggle(startIndex);
+    }
+
+    for (let index = 0; index < this.mView.rowCount && selectIt; index++)
+    {
+      // The desired folder is now in the view.
+      let item = this.mView.getItemAtIndex(index);
+      if (!item.container)
+        continue;
+      if (item.url == folderURI)
       {
-        if (!item.open)
-          this.mView.toggleOpenState(index);
-        if (item.url == folderURI) {
-          // we found the actual folder - not an ancestor
-          this.mTree.view.selection.select(index);
-          this.mTree.boxObject.ensureRowIsVisible(index);
-          break;
+        if (item.children.length && !item.open && openIt)
+          this.mView.toggle(index);
+        this.mView.selection.select(index);
+        this.mView.treeBox.ensureRowIsVisible(index);
+        found = true;
+        break;
+      }
+    }
+
+    if (this.mView.selection.selectEventsSuppressed)
+      this.mView.selection.selectEventsSuppressed = false;
+    return found;
+  },
+
+  /**
+   * Find the feed in the tree.  The search first gets the feed's folder,
+   * then selects the child feed.
+   * 
+   * @param  aFeed {Feed object}    - the feed to find.
+   * @param  [aParentIndex] integer - index to start the folder search.
+   * 
+   * @return found bool - true if found, false if not.
+   */
+  selectFeed: function(aFeed, aParentIndex)
+  {
+    let found = false;
+    if (this.selectFolder(aFeed.folder, true, aParentIndex))
+    {
+      let seln = this.mView.selection;
+      let item = this.mView.currentItem;
+      if (item) {
+        for (let i = seln.currentIndex + 1; i < this.mView.rowCount; i++) {
+          if (this.mView.getItemAtIndex(i).url == aFeed.url) {
+            this.mView.selection.select(i);
+            this.mView.treeBox.ensureRowIsVisible(i);
+            found = true;
+            break;
+          }
         }
       }
     }
-  },
 
-  selectFeed: function(aFeed)
-  {
-    this.selectFolder(aFeed.folder);
-
-    var seln = this.mTree.view.selection;
-    var item = this.mView.getItemAtIndex(seln.currentIndex);
-    if (item) {
-      for (var i = 0; i < item.children.length; i++) {
-        if (item.children[i].url == aFeed.url) {
-          var index = seln.currentIndex + i + 1;
-          this.mTree.view.selection.select(index);
-          this.mTree.boxObject.ensureRowIsVisible(index);
-          break;
-        }
-      }
-    }
+    return found;
   },
 
   updateFeedData: function (aItem)
   {
-    var ids = ['nameLabel', 'nameValue', 'locationLabel', 'locationValue'];
-    if (aItem && !aItem.container) 
+    if (!aItem)
+      return;
+
+    let nameValue = document.getElementById("nameValue");
+    let locationValue = document.getElementById("locationValue");
+    let selectFolder = document.getElementById("selectFolder");
+    let rssAccountMenuItem = document.getElementById("rssAccountMenuItem");
+    let server, rootFolder, displayFolder;
+
+    if (!aItem.container)
     {
-      // set the feed location and title info
-      document.getElementById('nameValue').value = aItem.name;
-      document.getElementById('locationValue').value = aItem.url;
+      // A feed item.  Set the feed location and title info.
+      nameValue.value = aItem.name;
+      locationValue.value = aItem.url;
+
+      // Root the location picker to the news & blogs server.
+      server = aItem.parentFolder.server;
+      rootFolder = aItem.parentFolder.rootFolder;
+      displayFolder = aItem.parentFolder;
     }
-    else 
+    else
     {
-      var noneSelected = this.mBundle.getString('subscribe-noFeedIsSelected');
-      document.getElementById('nameValue').value = noneSelected;
-      document.getElementById('locationValue').value = "";
+      // A folder/container item.
+      nameValue.value = "";
+      nameValue.disabled = true;
+      locationValue.value = "";
+
+      server = aItem.folder.server;
+      rootFolder = aItem.folder.rootFolder;
+      displayFolder = aItem.folder;
     }
 
-    for (var i = 0; i < ids.length; ++i)
-      document.getElementById(ids[i]).disabled = !aItem || aItem.container;
+    // Common to both folder and feed items.
+    nameValue.disabled = aItem.container;
+
+    selectFolder.setAttribute("ref", rootFolder.URI);
+    selectFolder.selectedIndex = -1;
+    selectFolder.disabled = aItem.container;
+    this.setFolderPicker(displayFolder.URI);
+
+    rssAccountMenuItem.label = server.prettyName;
+    rssAccountMenuItem.value = server.serverURI;
+    // A feed can't be added to the account folder.
+    rssAccountMenuItem.setAttribute("disabled", true);
+
+    // Set quick mode value.
+    document.getElementById("quickMode").checked = aItem.quickMode;
+  },
+
+  setFolderPicker: function(aFolderURI)
+  {
+    let selectFolder = document.getElementById("selectFolder");
+    let msgFolder = GetMsgFolderFromUri(aFolderURI, true);
+    if (!msgFolder)
+      return;
+
+    selectFolder.setAttribute("label", msgFolder.name);
+    selectFolder.setAttribute("uri", msgFolder.URI);
   },
 
   onKeyPress: function(aEvent)
   {
-    if (aEvent.keyCode == aEvent.DOM_VK_ENTER ||
-        aEvent.keyCode == aEvent.DOM_VK_RETURN)
-      this.editFeed();
-    else if (aEvent.keyCode == aEvent.DOM_VK_DELETE)
-      this.removeFeed();
+    if (aEvent.keyCode == aEvent.DOM_VK_DELETE)
+      this.removeFeed(true);
+
+    this.clearStatusInfo();
   },
 
-  onSelect: function () 
+  onSelect: function ()
   {
-    var properties, item;
-    var seln = this.mTree.view.selection;
-    item = this.mView.getItemAtIndex(seln.currentIndex);
+    let item = this.mView.currentItem;
+    let isServer = item && item.folder && item.folder.isServer;
+    let disable;
     this.updateFeedData(item);
-    document.getElementById("removeFeed").disabled = !item || item.container;
-    document.getElementById("editFeed").disabled = !item || item.container;
+    disable = !item || (!item.container || this.mActionMode == this.kMoveMode);
+    document.getElementById("addFeed").disabled = disable;
+    disable = !item || (item.container && this.mActionMode != this.kMoveMode);
+    document.getElementById("removeFeed").disabled = disable;
+    document.getElementById("editFeed").disabled = disable;
+    document.getElementById("importOPML").disabled = !item || !isServer;
+    document.getElementById("exportOPML").disabled = !item || !isServer;
   },
 
-  removeFeed: function ()
+  onClick: function (aEvent)
   {
-    var seln = this.mView.selection;
+    if (aEvent.button != 0)
+      return;
+
+    this.clearStatusInfo();
+  },
+
+  removeFeed: function (aPrompt)
+  {
+    let seln = this.mView.selection;
     if (seln.count != 1)
       return;
 
-    var itemToRemove = this.mView.getItemAtIndex(seln.currentIndex);
+    let itemToRemove = this.mView.getItemAtIndex(seln.currentIndex);
 
     if (!itemToRemove || itemToRemove.container)
       return;
 
-    // Confirm unsubscribe prompt.
-    var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
-                                  .getService(IPS);
-    var pTitle = this.mBundle.getString('subscribe-confirmFeedDeletionTitle');
-    var pMessage = this.mBundle.getFormattedString('subscribe-confirmFeedDeletion',
-                                                   [itemToRemove.name], 1);
-    var abortRemoval = promptService.confirmEx(window, pTitle, pMessage,
-                                               IPS.STD_YES_NO_BUTTONS,
-                                               null, null, null, null, { });
-    if (abortRemoval)
-      return;
+    if (aPrompt)
+    {
+      // Confirm unsubscribe prompt.
+      let pTitle = FeedUtils.strings.GetStringFromName(
+                     "subscribe-confirmFeedDeletionTitle");
+      let pMessage = FeedUtils.strings.formatStringFromName(
+                       "subscribe-confirmFeedDeletion", [itemToRemove.name], 1);
+      if (Services.prompt.confirmEx(window, pTitle, pMessage,
+                                    Ci.nsIPromptService.STD_YES_NO_BUTTONS,
+                                    null, null, null, null, { }))
+        return;
+    }
 
     deleteFeed(rdf.GetResource(itemToRemove.url),
-                               this.mRSSServer,
-                               itemToRemove.parentFolder);
+               itemToRemove.parentFolder.server,
+               itemToRemove.parentFolder);
 
     // Now that we have removed the feed from the datasource, it is time to
     // update our view layer.  Start by removing the child from its parent
     // folder object.
-    this.mView.removeItemAtIndex(seln.currentIndex);
+    this.mView.removeItemAtIndex(seln.currentIndex, false);
+    let message = FeedUtils.strings.GetStringFromName("subscribe-feedRemoved");
+    this.updateStatusItem("statusText", message);
   },
 
-  // aRootFolderURI --> optional argument.  The folder to initially create
-  // the new feed under.
-  addFeed: function(aFeedLocation, aRootFolderURI)
+
+  /**
+   * This addFeed is used by 1) Add button, 1) Update button, 3) Drop of a
+   * feed url on a folder (which can be an add or move).  If Update, the new
+   * url is added and the old removed; thus aParse is false and no new messages
+   * are downloaded, the feed is only validated and stored in the db.  If dnd,
+   * the drop folder is selected and the url is prefilled, so proceed just as
+   * though the url were entered manually.  This allows a user to see the dnd
+   * url better in case of errors.
+   * 
+   * @param  [aFeedLocation] string    - the feed url; get the url from the
+   *                                     input field if null.
+   * @param  [aFolder] nsIMsgFolder    - folder to subscribe, current selected
+   *                                     folder if null.
+   * @param  [aParse] boolean          - if true (default) parse and download
+   *                                     the feed's articles.
+   * @param  [aMode] integer           - action mode (default is kSubscribeMode)
+   *                                     of the add.
+   * 
+   * @return success boolean           - true if edit checks passed and an
+   *                                     async download has been initiated.
+   */
+  addFeed: function(aFeedLocation, aFolder, aParse, aMode)
   {
-    var userAddedFeed = false; 
-    var defaultQuickMode = this.mRSSServer.getBoolValue('quickMode');
-    var feedProperties = { feedName: "", feedLocation: aFeedLocation, 
-                           serverURI: this.mRSSServer.serverURI, 
-                           serverPrettyName: this.mRSSServer.prettyName,  
-                           quickMode: this.mRSSServer.getBoolValue('quickMode'),
-                           newFeed: true,
-                           result: userAddedFeed};
+    let message;
+    let parse = aParse == null ? true : aParse;
+    let mode = aMode == null ? this.kSubscribeMode : aMode;
+    let locationValue = document.getElementById("locationValue");
 
-    // unless another folder is specified, default to currently selected
-    if (aRootFolderURI) {
-      feedProperties.folderURI = aRootFolderURI;
-    } else {
-      var index = this.mTree.view.selection.currentIndex;
-      var item = this.mView.getItemAtIndex(index);
-      if (item) {
-        if (!item.container)
-          item = this.mView.getItemAtIndex(this.mView.getParentIndex(index));
-        feedProperties.folderURI = item.url;
-      }
+    if (aFeedLocation)
+      locationValue.value = aFeedLocation;
+    let feedLocation = locationValue.value.trim();
+
+    if (!feedLocation)
+    {
+      message = locationValue.getAttribute("placeholder");
+      this.updateStatusItem("statusText", message);
+      return false;
     }
 
-    feedProperties = openFeedEditor(feedProperties);
-
-    // if the user hit cancel, exit without doing anything
-    if (!feedProperties.result)
-      return;
+    let addFolder;
+    if (aFolder)
+    {
+      // For Update or if passed a folder.
+      if (aFolder instanceof Ci.nsIMsgFolder)
+        addFolder = aFolder;
+    }
+    else
+    {
+      // A folder must be selected for Add and Drop.
+      let index = this.mView.selection.currentIndex;
+      let item = this.mView.getItemAtIndex(index);
+      if (item && item.container)
+        addFolder = item.folder;
+    }
 
-    if (!feedProperties.feedLocation)
-      return;
+    // Shouldn't happen.  Or else not passed an nsIMsgFolder.
+    if (!addFolder)
+      return false;
 
-    // before we go any further, make sure the user is not already subscribed to this feed.
-    if (feedAlreadyExists(feedProperties.feedLocation, this.mRSSServer))
+    // Before we go any further, make sure the user is not already subscribed
+    // to this feed.
+    if (feedAlreadyExists(feedLocation, addFolder.server))
     {
-      var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(IPS);
-      promptService.alert(window, null, this.mBundle.getString("subscribe-feedAlreadySubscribed"));            
-      return;
+      message = FeedUtils.strings.GetStringFromName(
+                  "subscribe-feedAlreadySubscribed");
+      this.updateStatusItem("statusText", message);
+      return false;
     }
 
-    var feed = this.storeFeed(feedProperties);
-    if (!feed)
-      return;
+    let name = document.getElementById("nameValue").value;
+    let quickMode = document.getElementById("quickMode").checked;
+    let folderURI = addFolder.isServer ? null : addFolder.URI;
+    let feedProperties = { feedName     : name,
+                           feedLocation : feedLocation,
+                           folderURI    : folderURI,
+                           server       : addFolder.server,
+                           quickMode    : quickMode };
 
-    // Now validate and start downloadng the feed....
-    updateStatusItem('statusText', document.getElementById('bundle_newsblog')
-                                           .getString('subscribe-validating-feed'));
-    updateStatusItem('progressMeter', 0);
-    document.getElementById('addFeed').setAttribute('disabled', 'true');
-    feed.download(true, this.mFeedDownloadCallback);
+    let feed = this.storeFeed(feedProperties);
+    if (!feed)
+      return false;
+
+    // Now validate and start downloading the feed.
+    message = FeedUtils.strings.GetStringFromName("subscribe-validating-feed");
+    this.updateStatusItem("statusText", message);
+    this.updateStatusItem("progressMeter", 0);
+    document.getElementById("addFeed").setAttribute("disabled", true);
+    this.mActionMode = mode;
+    feed.download(parse, this.mFeedDownloadCallback);
+    return true;
   },
 
-  // helper routine used by addFeed and importOPMLFile
+  // Helper routine used by addFeed and importOPMLFile.
   storeFeed: function(feedProperties)
   {
-    var itemResource = rdf.GetResource(feedProperties.feedLocation);
-    var feed = new Feed(itemResource, this.mRSSServer);
+    let itemResource = rdf.GetResource(feedProperties.feedLocation);
+    let feed = new Feed(itemResource, feedProperties.server);
 
-    // if the user specified a specific folder to add the feed too, then set it here
+    // If the user specified a folder to add the feed to, then set it here.
     if (feedProperties.folderURI)
     {
-      var folderResource = rdf.GetResource(feedProperties.folderURI);   
+      let folderResource = rdf.GetResource(feedProperties.folderURI);
       if (folderResource)
       {
-        var folder = folderResource.QueryInterface(Components.interfaces.nsIMsgFolder);
+        let folder = folderResource.QueryInterface(Ci.nsIMsgFolder);
         if (folder && !folder.isServer)
           feed.folder = folder;
       }
     }
 
+    feed.title = feedProperties.feedName;
     feed.quickMode = feedProperties.quickMode;
     return feed;
   },
 
-  editFeed: function() 
+  editFeed: function()
   {
-    var seln = this.mView.selection;
-    if (seln.count != 1) 
+    let seln = this.mView.selection;
+    if (seln.count != 1)
       return;
 
-    var itemToEdit = this.mView.getItemAtIndex(seln.currentIndex);
-    if (!itemToEdit || itemToEdit.container)
+    let itemToEdit = this.mView.getItemAtIndex(seln.currentIndex);
+    if (!itemToEdit || itemToEdit.container || !itemToEdit.parentFolder)
       return;
 
-    var resource = rdf.GetResource(itemToEdit.url);
-    var feed = new Feed(resource, this.mRSSServer);
+    let resource = rdf.GetResource(itemToEdit.url);
+    let currentFolderServer = itemToEdit.parentFolder.server;
+    let ds = getSubscriptionsDS(currentFolderServer);
+    let currentFolder = ds.GetTarget(resource, FZ_DESTFOLDER, true);
+    let currentFolderURI = currentFolder.QueryInterface(Ci.nsIRDFResource).Value;
+    let feed = new Feed(resource, currentFolderServer);
 
-    var ds = getSubscriptionsDS(this.mRSSServer);
-    var currentFolder = ds.GetTarget(resource, FZ_DESTFOLDER, true);
-    var currentFolderURI = currentFolder.QueryInterface(Components.interfaces.nsIRDFResource).Value;
-   
-    var userModifiedFeed = false; 
-    var feedProperties = { feedLocation: itemToEdit.url, serverURI: this.mRSSServer.serverURI, 
-                           serverPrettyName: this.mRSSServer.prettyName, folderURI: currentFolderURI, 
-                           quickMode: feed.quickMode, newFeed: false, result: userModifiedFeed};
+    let editNameValue = document.getElementById("nameValue").value;
+    let editFeedLocation = document.getElementById("locationValue").value.trim();
+    let editQuickMode = document.getElementById("quickMode").checked;
+
+    if (feed.url != editFeedLocation)
+    {
+      // Updating a url.  We need to add the new url and delete the old, to
+      // ensure everything is cleaned up correctly.
+      this.addFeed(null, itemToEdit.parentFolder, false, this.kUpdateMode)
+      return;
+    }
 
-    feedProperties = openFeedEditor(feedProperties);
-    if (!feedProperties.result) // did the user cancel?
-      return;
+    let updated = false;
+    // Check to see if the title value changed.
+    if (feed.title != editNameValue)
+    {
+      feed.title = editNameValue;
+      itemToEdit.name = editNameValue;
+      updated = true;
+    }
 
-    // check to see if the quickMode value changed
-    if (feed.quickMode != feedProperties.quickMode)
-      feed.quickMode = feedProperties.quickMode;
+    // Check to see if the quickMode value changed.
+    if (feed.quickMode != editQuickMode)
+    {
+      feed.quickMode = editQuickMode;
+      itemToEdit.quickMode = editQuickMode;
+      updated = true;
+    }
 
-    // did the user change the folder URI for storing the feed?
-    if (feedProperties.folderURI && feedProperties.folderURI != currentFolderURI)
+    // Did the user change the folder URI for storing the feed?
+    let editFolderURI =
+      document.getElementById("selectFolder").getAttribute("uri");
+    if (currentFolderURI != editFolderURI)
     {
-      // we need to find the index of the new parent folder...
-      var newParentIndex = kRowIndexUndefined;
+      // We need to find the index of the new parent folder.
+      let newParentIndex = this.mView.kRowIndexUndefined;
       for (let index = 0; index < this.mView.rowCount; index++)
       {
-        var item = this.mView.getItemAtIndex(index);
-        if (item && item.container && item.url == feedProperties.folderURI)
+        let item = this.mView.getItemAtIndex(index);
+        if (item && item.container && item.url == editFolderURI)
         {
           newParentIndex = index;
           break;
-        }      
+        }
       }
 
-      if (newParentIndex != kRowIndexUndefined)
+      if (newParentIndex != this.mView.kRowIndexUndefined)
         this.moveFeed(seln.currentIndex, newParentIndex)
+
+      updated = true;
     }
-      
-    ds.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource).Flush(); // flush any changes
-  }, 
 
-  // moves the feed located at aOldFeedIndex to a child of aNewParentIndex
-  moveFeed: function(aOldFeedIndex, aNewParentIndex)
-  {
-    // if the new parent is the same as the current parent, then do nothing
-    if (this.mView.getParentIndex(aOldFeedIndex) == aNewParentIndex)
+    if (!updated)
       return;
 
-    var currentItem = this.mView.getItemAtIndex(aOldFeedIndex);
-    var currentParentItem = this.mView.getItemAtIndex(this.mView.getParentIndex(aOldFeedIndex));
-    var currentParentResource = rdf.GetResource(currentParentItem.url);
+    ds.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
+
+    let message = FeedUtils.strings.GetStringFromName("subscribe-feedUpdated");
+    this.updateStatusItem("statusText", message);
+  },
 
-    var newParentItem = this.mView.getItemAtIndex(aNewParentIndex);
-    var newParentResource = rdf.GetResource(newParentItem.url);
+  // Moves the feed located at aOldFeedIndex to a child of aNewParentIndex.
+  moveFeed: function(aOldFeedIndex, aNewParentIndex)
+  {
+    let currentItem = this.mView.getItemAtIndex(aOldFeedIndex);
+    if (!currentItem ||
+        this.mView.getParentIndex(aOldFeedIndex) == aNewParentIndex)
+      // If the new parent is the same as the current parent, then do nothing.
+      return false;
 
-    var ds = getSubscriptionsDS(this.mRSSServer);
-    var resource = rdf.GetResource(currentItem.url);
-    var currentFolder = currentParentResource.QueryInterface(Components.interfaces.nsIMsgFolder);
+    let currentParentIndex = this.mView.getParentIndex(aOldFeedIndex);
+    let currentParentItem = this.mView.getItemAtIndex(currentParentIndex);
+    let currentParentResource = rdf.GetResource(currentParentItem.url);
+    let currentFolder = currentParentResource.QueryInterface(Ci.nsIMsgFolder);
+
+    let newParentItem = this.mView.getItemAtIndex(aNewParentIndex);
+    let newParentResource = rdf.GetResource(newParentItem.url);
+    let newFolder = newParentResource.QueryInterface(Ci.nsIMsgFolder);
+
+    let ds = getSubscriptionsDS(currentItem.parentFolder.server);
+    let resource = rdf.GetResource(currentItem.url);
 
-    // unassert the older URI, add an assertion for the new parent URI...
-    ds.Change(resource, FZ_DESTFOLDER, currentParentResource, newParentResource);
+    let accountMove = false;
+    if (currentFolder.rootFolder.URI == newFolder.rootFolder.URI)
+    {
+      // Moving within the same account/feeds db.
+      if (newFolder.isServer)
+        // No moving to account folder if already in the account.
+        return false;
 
-    // we need to update the feed url attributes on the databases for each folder
-    updateFolderFeedUrl(currentParentResource.QueryInterface(Components.interfaces.nsIMsgFolder), 
-                        currentItem.url, true); // remove our feed url property from the current folder
-    updateFolderFeedUrl(newParentResource.QueryInterface(Components.interfaces.nsIMsgFolder), 
-                        currentItem.url, false);       // add our feed url property to the new folder
+      // Unassert the older URI, add an assertion for the new parent URI.
+      ds.Change(resource, FZ_DESTFOLDER,
+                currentParentResource, newParentResource);
+      ds.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
+      // Update the feed url attributes on the databases for each folder:
+      // Remove our feed url property from the current folder.
+      updateFolderFeedUrl(currentFolder, currentItem.url, true);
+      // Add our feed url property to the new folder.
+      updateFolderFeedUrl(newFolder, currentItem.url, false);
+    }
+    else
+    {
+      // Moving to a new account.  If dropping on the account folder, a new
+      // subfolder is created if necessary.
+      accountMove = true;
+      // Subscribe to the new folder first.  If it already exists in the
+      // account or on error, return.
+      if (!this.addFeed(currentItem.url, newFolder, false, this.kMoveMode))
+        return;
+      // Unsubscribe the feed from the old folder, if add to the new folder
+      // is successfull.
+      deleteFeed(rdf.GetResource(currentItem.url),
+                 currentItem.parentFolder.server,
+                 currentItem.parentFolder);
+    }
 
-
-    // Finally, update our view layer
-    this.mView.removeItemAtIndex(aOldFeedIndex, 1);
+    // Finally, update our view layer.  Remove the old row.
+    this.mView.removeItemAtIndex(aOldFeedIndex, true);
     if (aNewParentIndex > aOldFeedIndex)
       aNewParentIndex--;
-    
-    currentItem.level = newParentItem.level + 1;
-    newParentItem.children.push(currentItem);
-    var indexOfNewItem = aNewParentIndex + newParentItem.children.length;
 
-    if (!newParentItem.open) // force open the container
-      this.mView.toggleOpenState(aNewParentIndex);
-    else
+    if (accountMove)
     {
-      this.mFeedContainers.splice(indexOfNewItem, 0, currentItem);
-      this.mView.mRowCount++;
-      this.mTree.treeBoxObject.rowCountChanged(indexOfNewItem, 1);
+      // If a cross account move, download callback will update the view with
+      // the new location.  Preselect folder/mode for callback.
+      this.selectFolder(newFolder, true, aNewParentIndex);
+      return true;
     }
 
-    gFeedSubscriptionsWindow.mTree.view.selection.select(indexOfNewItem)
+    // Add the new row location to the view.
+    currentItem.level = newParentItem.level + 1;
+    currentItem.parentFolder = newFolder;
+    newParentItem.children.push(currentItem);
+
+    if (newParentItem.open)
+      // Close the container, selecting the feed will rebuild the view rows.
+      this.mView.toggle(aNewParentIndex);
+
+    this.selectFeed({folder: newParentItem.folder, url: currentItem.url},
+                    aNewParentIndex);
+
+    let message = FeedUtils.strings.GetStringFromName("subscribe-feedMoved");
+    this.updateStatusItem("statusText", message);
   },
 
   beginDrag: function (aEvent)
   {
-    // get the selected feed article (if there is one)
-    var seln = this.mView.selection;
-    if (seln.count != 1) 
+    // Get the selected feed article (if there is one).
+    let seln = this.mView.selection;
+    if (seln.count != 1)
       return;
 
-    // only initiate a drag if the item is a feed (i.e. ignore folders/containers)
-    var item = this.mView.getItemAtIndex(seln.currentIndex);
+    // Only initiate a drag if the item is a feed (ignore folders/containers).
+    let item = this.mView.getItemAtIndex(seln.currentIndex);
     if (!item || item.container)
       return;
 
-    aEvent.dataTransfer.setData("text/x-moz-feed-index", seln.currentIndex.toString());
+    aEvent.dataTransfer.setData("text/x-moz-feed-index",
+                                seln.currentIndex.toString());
     aEvent.dataTransfer.effectAllowed = "move";
   },
 
   mFeedDownloadCallback:
   {
     downloaded: function(feed, aErrorCode)
     {
-      var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
-                                    .getService(Components.interfaces.nsIPromptService);
+      // Feed is null if our attempt to parse the feed failed.
+      let message = "";
+      let win = gFeedSubscriptionsWindow;
+      if (aErrorCode == FeedUtils.kNewsBlogSuccess)
+      {
+        win.updateStatusItem("progressMeter", 100);
 
-      // feed is null if our attempt to parse the feed failed
-      if (aErrorCode == kNewsBlogSuccess)
-      {
-        updateStatusItem('progressMeter', 100);
-
-        // if we get here...we should always have a folder by now...either
-        // in feed.folder or FeedItems created the folder for us....
+        // If we get here we should always have a folder by now, either in
+        // feed.folder or FeedItems created the folder for us.
         updateFolderFeedUrl(feed.folder, feed.url, false);
 
-        // add feed just adds the feed we have validated and downloaded to our datasource
-        // it also flushes the subscription datasource
+        // Add feed adds the feed to the subscriptions db and flushes the
+        // datasource.
         addFeed(feed.url, feed.name, feed.folder); 
 
-        // now add the feed to our view
-        refreshSubscriptionView();
+        // Now add the feed to our view.  If adding, the current selection will
+        // be a folder; if updating it will be a feed.  No need to rebuild the
+        // entire view, that is too jarring.
+        let curIndex = win.mView.selection.currentIndex;
+        let curItem = win.mView.getItemAtIndex(curIndex);
+        if (curItem)
+        {
+          let parentIndex, parentItem, newItem, level;
+          let rows = win.mFeedContainers;
+          if (curItem.container)
+          {
+            // Open the container, if it exists.
+            let folderExists = win.selectFolder(feed.folder, true, curIndex);
+            if (!folderExists) //curItem.folder.isServer && 
+            {
+              // This means a new folder was created.
+              parentIndex = curIndex;
+              parentItem = curItem;
+              level = curItem.level + 1;
+              newItem = win.makeFolderObject(feed.folder, level);
+            }
+            else
+            {
+              // If a folder happens to exist which matches one that would
+              // have been created, the feed system reuses it.  Get the
+              // current item again if reusing a previously unselected folder.
+              curIndex = win.mView.selection.currentIndex;
+              curItem = win.mView.getItemAtIndex(curIndex);
+              parentIndex = curIndex;
+              parentItem = curItem;
+              level = curItem.level + 1;
+              newItem = win.makeFeedObject(feed, feed.folder, level);
+            }
+          }
+          else
+          {
+            parentIndex = win.mView.getParentIndex(curIndex);
+            parentItem = win.mView.getItemAtIndex(parentIndex);
+            level = curItem.level;
+            newItem = win.makeFeedObject(feed, feed.folder, level);
+          }
 
-        gFeedSubscriptionsWindow.selectFeed(feed);
+          parentItem.children.push(newItem);
+
+          if (win.mActionMode == win.kSubscribeMode)
+            message = FeedUtils.strings.GetStringFromName(
+                        "subscribe-feedAdded");
+          if (win.mActionMode == win.kUpdateMode)
+          {
+            win.removeFeed(false);
+            message = FeedUtils.strings.GetStringFromName(
+                        "subscribe-feedUpdated");
+          }
+          if (win.mActionMode == win.kMoveMode)
+            message = FeedUtils.strings.GetStringFromName(
+                        "subscribe-feedMoved");
+
+          win.selectFeed(feed, parentIndex);
+        }
       }
-      else if (aErrorCode == kNewsBlogInvalidFeed) //  the feed was bad...
-        promptService.alert(null,
-          gFeedSubscriptionsWindow.mBundle.getString('newsblog-feedNotValidTitle'),
-          gFeedSubscriptionsWindow.mBundle.getFormattedString('newsblog-feedNotValid', [feed.url]));
-      else if (aErrorCode == kNewsBlogRequestFailure)
-        promptService.alert(null,
-          gFeedSubscriptionsWindow.mBundle.getString('newsblog-networkErrorTitle'),
-          gFeedSubscriptionsWindow.mBundle.getFormattedString('newsblog-networkError', [feed.url]));
+      else
+      {
+        // Non success.  Remove intermediate traces from the feeds database.
+        if (feed && feed.url && feed.server)
+          deleteFeed(rdf.GetResource(feed.url),
+                     feed.server,
+                     feed.server.rootFolder);
 
-      // re-enable the add button now that we are done subscribing
-      document.getElementById('addFeed').removeAttribute('disabled');
+        if (aErrorCode == FeedUtils.kNewsBlogInvalidFeed)
+          message = FeedUtils.strings.GetStringFromName(
+                      "subscribe-feedNotValid");
+        if (aErrorCode == FeedUtils.kNewsBlogRequestFailure)
+          message = FeedUtils.strings.GetStringFromName(
+                      "subscribe-networkError");
 
-      // our operation is done...clear out the status text and progressmeter
-      setTimeout(clearStatusInfo, 1000);
+        if (win.mActionMode != win.kUpdateMode)
+          // Re-enable the add button if subscribe failed.
+          document.getElementById("addFeed").removeAttribute("disabled");
+      }
+
+      win.clearStatusInfo();
+      win.updateStatusItem("statusText", message, aErrorCode);
     },
 
-    // this gets called after the RSS parser finishes storing a feed item to disk
-    // aCurrentFeedItems is an integer corresponding to how many feed items have been downloaded so far
-    // aMaxFeedItems is an integer corresponding to the total number of feed items to download
+    // This gets called after the RSS parser finishes storing a feed item to
+    // disk.  aCurrentFeedItems is an integer corresponding to how many feed
+    // items have been downloaded so far.  aMaxFeedItems is an integer
+    // corresponding to the total number of feed items to download.
     onFeedItemStored: function (feed, aCurrentFeedItems, aMaxFeedItems)
     {
-      updateStatusItem('statusText', gFeedSubscriptionsWindow.mBundle.getFormattedString('subscribe-gettingFeedItems',
-                                                                                         [aCurrentFeedItems, aMaxFeedItems]));
+      let message = FeedUtils.strings.formatStringFromName(
+                      "subscribe-gettingFeedItems",
+                      [aCurrentFeedItems, aMaxFeedItems], 2);
+      gFeedSubscriptionsWindow.updateStatusItem("statusText", message);
       this.onProgress(feed, aCurrentFeedItems, aMaxFeedItems);
     },
 
     onProgress: function(feed, aProgress, aProgressMax)
     {
-      updateStatusItem('progressMeter', (aProgress * 100) / aProgressMax);
+      gFeedSubscriptionsWindow.updateStatusItem("progressMeter",
+                                                (aProgress * 100) / aProgressMax);
     }
   },
 
+  // Status routines.
+  updateStatusItem: function(aID, aValue, aErrorCode)
+  {
+    let el = document.getElementById(aID);
+    if (el.getAttribute("collapsed"))
+      el.removeAttribute("collapsed");
+
+    el.value = aValue;
+
+    el = document.getElementById("validationText");
+    if (aErrorCode == FeedUtils.kNewsBlogInvalidFeed)
+      el.removeAttribute("collapsed");
+    else
+      el.setAttribute("collapsed", true);
+  },
+
+  clearStatusInfo: function()
+  {
+    this.mActionMode = null;
+    document.getElementById("statusText").value = "";
+    document.getElementById("progressMeter").collapsed = true;
+    document.getElementById("validationText").collapsed = true;
+  },
+
+  checkValidation: function(aEvent)
+  {
+    if (aEvent.button != 0)
+      return;
+
+    let validationSite = "http://validator.w3.org";
+    let validationQuery = "http://validator.w3.org/feed/check.cgi?url=";
+
+    let win = Services.wm.getMostRecentWindow("mail:3pane");
+    if (win && win instanceof Ci.nsIDOMWindow)
+    {
+      let tabmail = win.document.getElementById("tabmail");
+      if (tabmail)
+      {
+        let feedLocation = document.getElementById("locationValue").value;
+        let url = validationQuery + feedLocation;
+
+        win.focus();
+        win.openContentTab(url, "tab", "^" + validationSite);
+        FeedUtils.log.debug("checkValidation: query url - "+url);
+      }
+    }
+    aEvent.stopPropagation();
+  },
+
+  // Listener for folder pane changes.
+  FolderListener: {
+    get feedWindow() {
+      if (this._feedWindow)
+        return this._feedWindow;
+      let subscriptionsWindow =
+        Services.wm.getMostRecentWindow("Mail:News-BlogSubscriptions");
+      if (subscriptionsWindow)
+        return this._feedWindow = subscriptionsWindow.gFeedSubscriptionsWindow;
+      return null;
+    },
+
+    get currentSelectedItem() {
+      return this.feedWindow ? this.feedWindow.mView.currentItem : null;
+    },
+
+    folderAdded: function(aFolder)
+    {
+      if (aFolder.server.type != "rss")
+        return;
+      FeedUtils.log.debug("folderAdded: folder - "+ aFolder.name);
+      this.feedWindow.refreshSubscriptionView();
+    },
+
+    folderDeleted: function(aFolder)
+    {
+      if (aFolder.server.type != "rss")
+        return;
+      FeedUtils.log.debug("folderDeleted: folder - "+ aFolder.name);
+      let curSelItem = this.currentSelectedItem;
+      let feedWindow = this.feedWindow;
+      if (curSelItem && curSelItem.container && curSelItem.folder == aFolder)
+      {
+        let curSelIndex = this.feedWindow.mView.selection.currentIndex;
+        this.feedWindow.mView.removeItemAtIndex(curSelIndex);
+      }
+      else
+        setTimeout(function() {
+          feedWindow.refreshSubscriptionView();
+        }, 20);
+    },
+
+    folderRenamed: function(aOrigFolder, aNewFolder)
+    {
+      if (aNewFolder.server.type != "rss")
+        return;
+      FeedUtils.log.debug("folderRenamed: old:new - "+
+                          aOrigFolder.name+":"+aNewFolder.name);
+      let curSelItem = this.currentSelectedItem;
+      let feedWindow = this.feedWindow;
+      setTimeout(function() {
+        feedWindow.refreshSubscriptionView();
+        if (curSelItem && curSelItem.container &&
+            curSelItem.folder == aOrigFolder)
+          feedWindow.selectFolder(aNewFolder, true, null, false);
+      }, 20);
+    },
+
+    folderMoveCopyCompleted: function(aMove, aSrcFolder, aDestFolder)
+    {
+      if (aDestFolder.server.type != "rss")
+        return;
+      FeedUtils.log.debug("folderMoveCopyCompleted: move:src:dest - "+
+                          aMove+":"+aSrcFolder.name+":"+aDestFolder.name);
+      let curSelItem = this.currentSelectedItem;
+      let feedWindow = this.feedWindow;
+      if (aMove && aDestFolder.getFlag(Ci.nsMsgFolderFlags.Trash))
+        return this.folderDeleted(aSrcFolder);
+
+      setTimeout(function() {
+        feedWindow.refreshSubscriptionView();
+        if (curSelItem && curSelItem.container &&
+            curSelItem.folder == aSrcFolder)
+          feedWindow.selectFolder(aDestFolder);
+      }, 20);
+    }
+  },
+
+  /* *************************************************************** */
+  /* OPML Functions                                                  */
+  /* *************************************************************** */
   exportOPML: function()
   {
+    // Account folder must be selected.
+    let item = this.mView.currentItem;
+    if (!item || !item.folder || !item.folder.isServer)
+      return;
+
+    this.mRSSServer = item.folder.server;
+    let SPACES2 = "  ";
+    let SPACES4 = "    ";
+
     if (this.mRSSServer.rootFolder.hasSubFolders)
     {
-      var opmlDoc = document.implementation.createDocument("","opml",null);
-      var opmlRoot = opmlDoc.documentElement;
+      let opmlDoc = document.implementation.createDocument("", "opml", null);
+      let opmlRoot = opmlDoc.documentElement;
       opmlRoot.setAttribute("version","1.0");
 
-      this.generatePPSpace(opmlRoot,"  ");
+      this.generatePPSpace(opmlRoot, SPACES2);
 
-      var brandBundle = document.getElementById("bundle_brand");
+      let brandBundle = document.getElementById("bundle_brand");
 
-      // Make the <head> element
-      var head = opmlDoc.createElement("head");
-      this.generatePPSpace(head, "    ");
-      var titleText = this.mBundle.getFormattedString(
+      // Make the <head> element.
+      let head = opmlDoc.createElement("head");
+      this.generatePPSpace(head, SPACES4);
+      let titleText = FeedUtils.strings.formatStringFromName(
                         "subscribe-OPMLExportFileDialogTitle",
                         [brandBundle.getString("brandShortName")], 1);
-      var title = opmlDoc.createElement("title");
+      let title = opmlDoc.createElement("title");
       title.appendChild(opmlDoc.createTextNode(titleText));
       head.appendChild(title);
-      this.generatePPSpace(head, "    ");
-      var dt = opmlDoc.createElement("dateCreated");
+      this.generatePPSpace(head, SPACES4);
+      let dt = opmlDoc.createElement("dateCreated");
       dt.appendChild(opmlDoc.createTextNode((new Date()).toGMTString()));
       head.appendChild(dt);
-      this.generatePPSpace(head, "  ");
+      this.generatePPSpace(head, SPACES2);
       opmlRoot.appendChild(head);
 
-      this.generatePPSpace(opmlRoot, "  ");
+      this.generatePPSpace(opmlRoot, SPACES2);
 
-      //add <outline>s to the <body>
-      var body = opmlDoc.createElement("body");
-      this.generateOutline(this.mRSSServer.rootFolder, body, 4);
-      this.generatePPSpace(body, "  ");
+      // Add <outline>s to the <body>.
+      let body = opmlDoc.createElement("body");
+      this.generateOutline(this.mRSSServer.rootFolder, body, SPACES4.length);
+      this.generatePPSpace(body, SPACES2);
       opmlRoot.appendChild(body);
 
       this.generatePPSpace(opmlRoot, "");
 
-      var serial=new XMLSerializer();
-      var omplFileName = this.mBundle.getFormattedString(
+      let serial=new XMLSerializer();
+      let omplFileName = FeedUtils.strings.formatStringFromName(
                            "subscribe-OPMLExportDefaultFileName",
                            [brandBundle.getString("brandShortName")], 1);
-      var rv = pickSaveAs(this.mBundle.getString("subscribe-OPMLExportTitle"),'$all',
-                          omplFileName);
-      if(rv.reason == PICK_CANCEL)
+      let pTitle = FeedUtils.strings.GetStringFromName(
+                     "subscribe-OPMLExportTitle");
+      let rv = pickSaveAs(pTitle, "$all", omplFileName);
+      if (rv.reason == PICK_CANCEL)
         return;
-      else if(rv)
+      else if (rv)
       {
         //debug("opml:\n"+serial.serializeToString(opmlDoc)+"\n");
-        var file = new LocalFile(rv.file, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE);
-        serial.serializeToStream(opmlDoc,file.outputStream,'utf-8');
+        let file = new LocalFile(rv.file,
+                                 MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE);
+        serial.serializeToStream(opmlDoc,file.outputStream, "utf-8");
         file.close();
       }
     }
   },
 
   generatePPSpace: function(aNode, indentString)
   {
     aNode.appendChild(aNode.ownerDocument.createTextNode("\n"));
     aNode.appendChild(aNode.ownerDocument.createTextNode(indentString));
   },
-  
+
   generateOutline: function(baseFolder, parent, indentLevel)
   {
-    var folderEnumerator = baseFolder.subFolders;
+    let folderEnumerator = baseFolder.subFolders;
 
-    // pretty printing
-    var indentString = "";
+    // Pretty printing.
+    let indentString = "";
     for (let i = 0; i < indentLevel; i++)
       indentString = indentString + " ";
- 
+
     while (folderEnumerator.hasMoreElements())
     {
-      var folder = folderEnumerator.getNext();
-      if ((folder instanceof Components.interfaces.nsIMsgFolder) &&
-          !folder.getFlag(Components.interfaces.nsMsgFolderFlags.Trash) &&
-          !folder.getFlag(Components.interfaces.nsMsgFolderFlags.Virtual))
+      let folder = folderEnumerator.getNext();
+      if ((folder instanceof Ci.nsIMsgFolder) &&
+          !folder.getFlag(Ci.nsMsgFolderFlags.Trash) &&
+          !folder.getFlag(Ci.nsMsgFolderFlags.Virtual))
       {
-        var outline;
+        let outline;
         if (folder.hasSubFolders)
         {
-          // Make a mostly empty outline element
+          // Make a mostly empty outline element.
           outline = parent.ownerDocument.createElement("outline");
-          outline.setAttribute("text",folder.prettiestName);
-          this.generateOutline(folder, outline, indentLevel+2); // recurse
+          outline.setAttribute("text", folder.prettiestName);
+          // Recurse.
+          this.generateOutline(folder, outline, indentLevel + 2);
           this.generatePPSpace(parent, indentString);
           this.generatePPSpace(outline, indentString);
           parent.appendChild(outline);
         }
         else
         {
-          // Add outline elements with xmlUrls
-          var feeds = this.getFeedsInFolder(folder);
+          // Add outline elements with xmlUrls.
+          let feeds = this.getFeedsInFolder(folder);
           for (let feed in feeds)
           {
-            outline = this.opmlFeedToOutline(feeds[feed],parent.ownerDocument);
+            outline = this.opmlFeedToOutline(feeds[feed], parent.ownerDocument);
             this.generatePPSpace(parent, indentString);
             parent.appendChild(outline);
           }
         }
       }
     }
   },
-  
-  opmlFeedToOutline: function(aFeed,aDoc)
+
+  opmlFeedToOutline: function(aFeed, aDoc)
   {
-    var outRv = aDoc.createElement("outline");
-    outRv.setAttribute("title",aFeed.title);
-    outRv.setAttribute("text",aFeed.title);
-    outRv.setAttribute("type","rss");
-    outRv.setAttribute("version","RSS");
-    outRv.setAttribute("xmlUrl",aFeed.url);
-    outRv.setAttribute("htmlUrl",aFeed.link);
+    let outRv = aDoc.createElement("outline");
+    outRv.setAttribute("title", aFeed.title);
+    outRv.setAttribute("text", aFeed.title);
+    outRv.setAttribute("type", "rss");
+    outRv.setAttribute("version", "RSS");
+    outRv.setAttribute("xmlUrl", aFeed.url);
+    outRv.setAttribute("htmlUrl", aFeed.link);
     return outRv;
   },
 
   importOPML: function()
   {
-    var rv = pickOpen(this.mBundle.getString("subscribe-OPMLImportTitle"), '$opml $xml $all');
+    // Account folder must be selected.
+    let item = this.mView.currentItem;
+    if (!item || !item.folder || !item.folder.isServer)
+      return;
+
+    this.mRSSServer = item.folder.server;
+
+    let pTitle = FeedUtils.strings.GetStringFromName(
+                   "subscribe-OPMLImportTitle");
+    let rv = pickOpen(pTitle, "$opml $xml $all");
     if(rv.reason == PICK_CANCEL)
       return;
 
-    var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(IPS);
-    var stream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
-    var opmlDom = null;
+    let opmlDom = null;
+    let statusReport;
+    let feedsAdded = 0;
+    let stream = Cc["@mozilla.org/network/file-input-stream;1"].
+                 createInstance(Ci.nsIFileInputStream);
 
-    // read in file as raw bytes, so Expat can do the decoding for us
-    try{
+    // Read in file as raw bytes, so Expat can do the decoding for us.
+    try {
       stream.init(rv.file, MODE_RDONLY, PERM_IROTH, 0);
-      var parser = new DOMParser();
-      opmlDom = parser.parseFromStream(stream, null, stream.available(), 'application/xml');
-    }catch(e){
-      promptService.alert(window, null, this.mBundle.getString("subscribe-errorOpeningFile"));
+      let parser = new DOMParser();
+      opmlDom = parser.parseFromStream(stream, null, stream.available(),
+                                       "application/xml");
+    }
+    catch(e) {
+      statusReport = FeedUtils.strings.GetStringFromName(
+                       "subscribe-errorOpeningFile");
+      Services.prompt.alert(window, null, statusReport);
       return;
-    }finally{
+    }
+    finally {
       stream.close();
     }
 
-    var statusReport;
-    var feedsAdded = 0;
-
-    // return if the user didn't give us an OPML file
-    if(!opmlDom || !(opmlDom.documentElement.tagName == "opml"))
+    // Return if the user didn't give us an OPML file.
+    if(!opmlDom || opmlDom.documentElement.tagName != "opml")
     {
-      statusReport = this.mBundle.getFormattedString("subscribe-OPMLImportInvalidFile", [rv.file.leafName]);
+      statusReport = FeedUtils.strings.formatStringFromName(
+                       "subscribe-OPMLImportInvalidFile", [rv.file.leafName], 1);
+      Services.prompt.alert(window, null, statusReport);
+      return;
     }
     else
     {
-      let outlines = opmlDom.getElementsByTagName("body")[0].getElementsByTagName("outline");
-      // try to import records if there are any
+      let outlines = opmlDom.getElementsByTagName("body")[0].
+                             getElementsByTagName("outline");
+      // Try to import records if there are any.
       for (let index = 0; index < outlines.length; index++)
       {
         if (this.importOutline(outlines[index]) == 1)
           feedsAdded++;
       }
 
       if (outlines.length > feedsAdded)
-        statusReport = this.mBundle.getFormattedString("subscribe-OPMLImportTotalFeeds", [feedsAdded, outlines.length]);
+        statusReport = FeedUtils.strings.formatStringFromName(
+                         "subscribe-OPMLImportTotalFeeds",
+                         [feedsAdded, outlines.length], 2);
       else
-        statusReport = this.mBundle.getFormattedString("subscribe-OPMLImportNewFeeds", [feedsAdded]);
+        statusReport = FeedUtils.strings.formatStringFromName(
+                         "subscribe-OPMLImportNewFeeds", [feedsAdded], 1);
     }
 
-    promptService.alert(window, null, statusReport);
+    this.clearStatusInfo();
+    this.updateStatusItem("statusText", statusReport);
 
-    // add the new feeds to our view
+    // Add the new feeds to our view.
     if (feedsAdded)
-      refreshSubscriptionView();
+      this.refreshSubscriptionView(this.mRSSServer.rootFolder);
   },
 
   importOutline: function(aOutline)
   {
     // XXX only dealing with flat OPML files for now.
     // We still need to add support for grouped files.
-    var newFeedUrl = aOutline.getAttribute("xmlUrl") ||
+    let newFeedUrl = aOutline.getAttribute("xmlUrl") ||
                      aOutline.getAttribute("url");
     if (!newFeedUrl)
       return -1;
 
-    // Silently skip feeds that are already subscribed to.
+    // Silently skip feeds that are already subscribed.
     if (feedAlreadyExists(newFeedUrl, this.mRSSServer))
+    {
+      FeedUtils.log.debug("importOutline: already subscribed in account "+
+                          this.mRSSServer.prettyName+", url - "+ newFeedUrl);
       return 0;
+    }
 
-    var feedName = aOutline.getAttribute("text") ||
+    let feedName = aOutline.getAttribute("text") ||
                    aOutline.getAttribute("title") ||
                    aOutline.getAttribute("xmlUrl");
 
-    var defaultQuickMode = this.mRSSServer.getBoolValue('quickMode');
-    var feedProperties = { feedName: feedName,
-                           feedLocation: newFeedUrl,
-                           serverURI: this.mRSSServer.serverURI,
-                           serverPrettyName: this.mRSSServer.prettyName,
-                           folderURI: "",
-                           quickMode: defaultQuickMode};
+    let defaultQuickMode = this.mRSSServer.getBoolValue("quickMode");
+    let feedProperties = { feedName     : feedName,
+                           feedLocation : newFeedUrl,
+                           server       : this.mRSSServer,
+                           folderURI    : "",
+                           quickMode    : defaultQuickMode};
 
-    debug("importing feed: "+ feedProperties.feedName);
+    FeedUtils.log.debug("importOutline: importing feed: name, url - "+
+                        feedName+", "+newFeedUrl);
 
-    var feed = this.storeFeed(feedProperties);
+    let feed = this.storeFeed(feedProperties);
     feed.title = feedProperties.feedName;
     if (aOutline.hasAttribute("htmlUrl"))
       feed.link = aOutline.getAttribute("htmlUrl");
 
     feed.createFolder();
     updateFolderFeedUrl(feed.folder, feed.url, false);
 
     // addFeed adds the feed we have validated and downloaded to
     // our datasource, it also flushes the subscription datasource.
     addFeed(feed.url, feed.name, feed.folder);
-    return 1; // feed correctly added
+    // Feed correctly added.
+    return 1;
   }
 };
-
-// opens the feed properties dialog
-function openFeedEditor(aFeedProperties)
-{
-  window.openDialog('chrome://messenger-newsblog/content/feed-properties.xul',
-                    'feedproperties', 'modal,titlebar,chrome,centerscreen',
-                    aFeedProperties);
-  return aFeedProperties;
-} 
-
-function refreshSubscriptionView()
-{
-  gFeedSubscriptionsWindow.loadSubscriptions();
-  gFeedSubscriptionsWindow.mTree.treeBoxObject.invalidate();
-  gFeedSubscriptionsWindow.mTree.treeBoxObject.view = gFeedSubscriptionsWindow.mView;
-  if (gFeedSubscriptionsWindow.mView.rowCount > 0) 
-    gFeedSubscriptionsWindow.mTree.view.selection.select(0);
-}
-
-function processDrop()
-{
-  gFeedSubscriptionsWindow.addFeed(gFeedSubscriptionsWindow.mView.mDropUrl, gFeedSubscriptionsWindow.mView.mDropFolderUrl);
-}
-
-// status helper routines
-
-function updateStatusItem(aID, aValue)
-{
-  var el = document.getElementById(aID);
-  if (el.getAttribute('collapsed'))
-    el.removeAttribute('collapsed');
-
-  el.value = aValue;
-}
-
-function clearStatusInfo()
-{
-  document.getElementById('statusText').value = "";
-  document.getElementById('progressMeter').collapsed = true;
-}
-
-
-
--- a/mailnews/extensions/newsblog/content/feed-subscriptions.xul
+++ b/mailnews/extensions/newsblog/content/feed-subscriptions.xul
@@ -33,123 +33,227 @@
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK ******
 
 <?xml-stylesheet href="chrome://messenger/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/skin/folderPane.css" type="text/css"?>
 <?xml-stylesheet href="chrome://messenger-newsblog/skin/feed-subscriptions.css" type="text/css"?>
 
 <!DOCTYPE window SYSTEM "chrome://messenger-newsblog/locale/feed-subscriptions.dtd">
 
 <window id="subscriptionsDialog"
+        flex="1"
         title="&feedSubscriptions.label;"
+        windowtype="Mail:News-BlogSubscriptions"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-#ifdef XP_MACOSX
-        style="width: 40em; height: 30em;"
-#else
-        style="width: 30em; height: 25em;"
-#endif
+        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+        xmlns:nc="http://home.netscape.com/NC-rdf#"
         persist="width height screenX screenY sizemode"
-        onload="gFeedSubscriptionsWindow.init();"
-        onunload="return gFeedSubscriptionsWindow.uninit();"
-        windowtype="Mail:News-BlogSubscriptions"
-        flex="1">
+        onload="gFeedSubscriptionsWindow.onLoad();"
+        onunload="return gFeedSubscriptionsWindow.onUnload();"
+        onclick="gFeedSubscriptionsWindow.onClick(event);">
 
   <script type="application/javascript" src="utils.js"/>
   <script type="application/javascript" src="file-utils.js"/>
   <script type="application/javascript" src="debug-utils.js"/>
   <script type="application/javascript" src="feed-subscriptions.js"/>
   <script type="application/javascript" src="Feed.js"/>
   <script type="application/javascript" src="FeedItem.js"/>
   <script type="application/javascript" src="feed-parser.js"/>
   <script type="application/javascript" src="chrome://global/content/nsDragAndDrop.js"/>
+  <script type="application/javascript" src="chrome://messenger/content/widgetglue.js"/>
 
   <keyset id="extensionsKeys">
-    <key id="key_close"   key="&cmd.close.commandKey;" modifiers="accel" oncommand="window.close();"/>
-    <key id="key_close2"  keycode="VK_ESCAPE" oncommand="window.close();"/>
+    <key id="key_close"
+         key="&cmd.close.commandKey;"
+         modifiers="accel"
+         oncommand="window.close();"/>
+    <key id="key_close2"
+         keycode="VK_ESCAPE"
+         oncommand="window.close();"/>
   </keyset>
 
-  <stringbundle id="bundle_newsblog" src="chrome://messenger-newsblog/locale/newsblog.properties"/>
-  <stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
+  <stringbundle id="bundle_newsblog"
+                src="chrome://messenger-newsblog/locale/newsblog.properties"/>
+  <stringbundle id="bundle_brand"
+                src="chrome://branding/locale/brand.properties"/>
 
   <vbox flex="1" id="contentPane">
     <description control="rssSubscriptionsList">&subscriptionDesc.label;</description>
 
     <separator class="thin"/>
 
     <tree id="rssSubscriptionsList"
           treelines="true"
           flex="1"
-          style="height: 10em;"
           hidecolumnpicker="true"
           onselect="gFeedSubscriptionsWindow.onSelect();"
           onkeypress="gFeedSubscriptionsWindow.onKeyPress(event);"
           seltype="single">
       <treecols>
-        <treecol id="folderNameCol" flex="2" primary="true" hideheader="true"/>
+        <treecol id="folderNameCol"
+                 flex="2"
+                 primary="true"
+                 hideheader="true"/>
       </treecols>
       <treechildren id="subscriptionChildren"
-                    ondblclick="if (event.button == 0) gFeedSubscriptionsWindow.editFeed();"
                     ondraggesture="gFeedSubscriptionsWindow.beginDrag(event);"/>
     </tree>
 
     <hbox id="rssFeedInfoBox">
-      <stack flex="1">
-        <hbox id="backgroundBox" flex="1"/>
+      <vbox flex="1">
         <grid flex="1">
           <columns>
             <column/>
             <column flex="1"/>
           </columns>
           <rows>
-            <row align="center">
-              <hbox pack="end"><label id="nameLabel" control="nameValue" value="&feedTitle.label;"/></hbox>
-              <textbox id="nameValue" readonly="true" class="plain"/>
+            <row>
+              <hbox align="right" valign="middle">
+                <label id="nameLabel"
+                       accesskey="&feedTitle.accesskey;"
+                       control="nameValue"
+                       value="&feedTitle.label;"/>
+              </hbox>
+              <textbox id="nameValue"
+                       clickSelectsAll="true"/>
+            </row>
+            <row>
+              <hbox align="right" valign="middle">
+                <label id="locationLabel"
+                       accesskey="&feedLocation.accesskey;"
+                       control="locationValue"
+                       value="&feedLocation.label;"/>
+              </hbox>
+              <textbox id="locationValue"
+                       class="uri-element"
+                       placeholder="&feedLocation.placeholder;"
+                       clickSelectsAll="true"/>
             </row>
-            <row align="center">
-              <hbox pack="end"><label id="locationLabel" control="locationValue" value="&location.label;"/></hbox>
-              <textbox id="locationValue" readonly="true" class="plain uri-element"/>
+            <row>
+              <hbox align="right" valign="middle">
+                <label id="feedFolderLabel"
+                       value="&feedFolder.label;"
+                       accesskey="&feedFolder.accesskey;"
+                       control="selectFolder"/>
+              </hbox>
+              <menulist id="selectFolder"
+                    disabled="true"
+                    containment="http://home.netscape.com/NC-rdf#child"
+                    sortResource="http://home.netscape.com/NC-rdf#FolderTreeName"
+                    sortDirection="ascending"
+                    datasources="rdf:msgaccountmanager rdf:mailnewsfolders"
+                    ref="...">
+                  <menupopup>
+                    <menuitem id="rssAccountMenuItem"
+                              oncommand="gFeedSubscriptionsWindow.setFolderPicker(event.target.id)"/>
+                    <menuseparator/>
+                  </menupopup>
+
+                  <template>
+                    <!-- Cheat and use the CanRename property to make sure we
+                         don't list the Trash folder as a possible feed folder. -->
+                    <rule nc:CanFileMessages="true"
+                          iscontainer="true"
+                          isempty="false"
+                          nc:CanRename="true">
+                      <menupopup>
+                        <menu uri="..."
+                              class="folderMenuItem menu-iconic"
+                              oncommand="gFeedSubscriptionsWindow.setFolderPicker(event.target.id)"
+                              SpecialFolder="rdf:http://home.netscape.com/NC-rdf#SpecialFolder"
+                              BiffState="rdf:http://home.netscape.com/NC-rdf#BiffState"
+                              IsServer="rdf:http://home.netscape.com/NC-rdf#IsServer"
+                              IsSecure="rdf:http://home.netscape.com/NC-rdf#IsSecure"
+                              ServerType="rdf:http://home.netscape.com/NC-rdf#ServerType"
+                              label="rdf:http://home.netscape.com/NC-rdf#Name">
+                          <menupopup class="menulist-menupopup">
+                            <menuitem label="rdf:http://home.netscape.com/NC-rdf#Name"
+                                      oncommand="gFeedSubscriptionsWindow.setFolderPicker(event.target.parentNode.parentNode.id)"/>
+                            <menuseparator/>
+                          </menupopup>
+                        </menu>
+                      </menupopup>
+                    </rule>
+                    <rule nc:CanFileMessages="true"
+                          nc:CanRename="true">
+                      <menupopup>
+                        <menuitem uri="..."
+                                  value="..."
+                                  class="folderMenuItem menuitem-iconic"
+                                  oncommand="gFeedSubscriptionsWindow.setFolderPicker(event.target.id)"
+                                  SpecialFolder="rdf:http://home.netscape.com/NC-rdf#SpecialFolder"
+                                  BiffState="rdf:http://home.netscape.com/NC-rdf#BiffState"
+                                  IsServer="rdf:http://home.netscape.com/NC-rdf#IsServer"
+                                  IsSecure="rdf:http://home.netscape.com/NC-rdf#IsSecure"
+                                  ServerType="rdf:http://home.netscape.com/NC-rdf#ServerType"
+                                  label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+                       </menupopup>
+                    </rule>
+                  </template>
+              </menulist>
             </row>
           </rows>
         </grid>
-      </stack>
+        <checkbox id="quickMode"
+                  accesskey="&quickMode.accesskey;"
+                  label="&quickMode.label;"/>
+      </vbox>
     </hbox>
 
-    <hbox id="statusContainerBox" align="center">
-      <label id="statusText" class="statusbarpanel-text" crop="right" flex="1"/>
-      <progressmeter id="progressMeter" collapsed="true" class="progressmeter-statusbar" style="-moz-margin-end: 5px;" mode="normal" value="0"/>
+    <hbox id="statusContainerBox"
+          align="center"
+          valign="middle">
+      <label id="statusText"
+             class="statusbarpanel-text"
+             crop="end"
+             value=""/>
+      <label id="validationText"
+             collapsed="true"
+             class="text-link"
+             crop="end"
+             value="&validateText.label;"
+             onclick="gFeedSubscriptionsWindow.checkValidation(event);"/>
+      <spacer flex="1"/>
+      <progressmeter id="progressMeter"
+                     collapsed="true"
+                     mode="normal"
+                     value="0"/>
     </hbox>
 
     <hbox align="end">
       <hbox class="actionButtons" flex="1">
         <button id="addFeed"
-                label="&button.addFeed.label;" accesskey="&button.addFeed.accesskey;"
+                label="&button.addFeed.label;"
+                accesskey="&button.addFeed.accesskey;"
                 oncommand="gFeedSubscriptionsWindow.addFeed();"/>
 
-        <button id="editFeed" disabled="true" default="true"
-                label="&button.editFeed.label;" accesskey="&button.editFeed.accesskey;"
+        <button id="editFeed"
+                disabled="true"
+                label="&button.editFeed.label;"
+                accesskey="&button.editFeed.accesskey;"
                 oncommand="gFeedSubscriptionsWindow.editFeed();"/>
 
-        <button id="removeFeed" disabled="true"
-                label="&button.removeFeed.label;" accesskey="&button.removeFeed.accesskey;"
-                oncommand="gFeedSubscriptionsWindow.removeFeed();"/>
+        <button id="removeFeed"
+                disabled="true"
+                label="&button.removeFeed.label;"
+                accesskey="&button.removeFeed.accesskey;"
+                oncommand="gFeedSubscriptionsWindow.removeFeed(true);"/>
+
+        <spacer flex="1"/>
 
         <button id="importOPML"
-                label="&button.importOPML.label;" accesskey="&button.importOPML.accesskey;"
+                label="&button.importOPML.label;"
+                accesskey="&button.importOPML.accesskey;"
                 oncommand="gFeedSubscriptionsWindow.importOPML();"/>
 
         <button id="exportOPML"
-                label="&button.exportOPML.label;" accesskey="&button.exportOPML.accesskey;"
+                label="&button.exportOPML.label;"
+                accesskey="&button.exportOPML.accesskey;"
                 oncommand="gFeedSubscriptionsWindow.exportOPML();"/>
-
-        <spacer flex="1"/>
       </hbox>
     </hbox>
   </vbox>
-
-  <hbox>
-    <spring flex="1"/>
-    <resizer dir="bottomright"/>
-  </hbox>
 </window>
--- a/mailnews/extensions/newsblog/content/newsblogOverlay.js
+++ b/mailnews/extensions/newsblog/content/newsblogOverlay.js
@@ -32,39 +32,35 @@
 # use your version of this file under the terms of the MPL, indicate your
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK ******
 
-function openSubscriptionsDialog(aFolder, aServer)
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function openSubscriptionsDialog(aFolder)
 {
-  if (!aServer)
-    aServer = aFolder.server;
-  //check for an existing subscriptions window and focus it.
-  const kWindowMediatorContractID = "@mozilla.org/appshell/window-mediator;1";
-  const kWindowMediatorIID = Components.interfaces.nsIWindowMediator;
-  const kWindowMediator = Components.classes[kWindowMediatorContractID]
-                                    .getService(kWindowMediatorIID);
-  var lastSubscriptionWindow =
-    kWindowMediator.getMostRecentWindow("Mail:News-BlogSubscriptions");
-  
-  if (lastSubscriptionWindow)
+  // Check for an existing feed subscriptions window and focus it.
+  let subscriptionsWindow =
+    Services.wm.getMostRecentWindow("Mail:News-BlogSubscriptions");
+
+  if (subscriptionsWindow)
   {
     if (aFolder)
-      lastSubscriptionWindow.gFeedSubscriptionsWindow.selectFolder(aFolder);
-    lastSubscriptionWindow.focus();
+      subscriptionsWindow.gFeedSubscriptionsWindow.selectFolder(aFolder);
+    subscriptionsWindow.focus();
   }
   else
   {
-    window.openDialog("chrome://messenger-newsblog/content/feed-subscriptions.xul", "",
-                      "centerscreen,chrome,dialog=no,resizable",
-                      { server: aServer, folder: aFolder});
+    window.openDialog("chrome://messenger-newsblog/content/feed-subscriptions.xul",
+                      "", "centerscreen,chrome,dialog=no,resizable",
+                      { folder: aFolder});
   }
 }
 
 // Special case attempts to reply/forward/edit as new RSS arrticles
 // Send the feed article URL instead of trying to load the feed inside of
 // an iframe. Bug #258278.
 function openComposeWindowForRSSArticle(msgHdr, type)
 {
--- a/mailnews/extensions/newsblog/jar.mn
+++ b/mailnews/extensions/newsblog/jar.mn
@@ -1,16 +1,13 @@
 newsblog.jar:
 % content messenger-newsblog %content/messenger-newsblog/
 *  content/messenger-newsblog/newsblogOverlay.js                (content/newsblogOverlay.js)
-*  content/messenger-newsblog/edittree.xml                      (content/edittree.xml)
 *  content/messenger-newsblog/debug-utils.js                    (content/debug-utils.js)
 *  content/messenger-newsblog/Feed.js                           (content/Feed.js)
 *  content/messenger-newsblog/FeedItem.js                       (content/FeedItem.js)
 *  content/messenger-newsblog/feed-parser.js                    (content/feed-parser.js)
 *  content/messenger-newsblog/file-utils.js                     (content/file-utils.js)
 *  content/messenger-newsblog/utils.js                          (content/utils.js)
-*  content/messenger-newsblog/feed-properties.xul               (content/feed-properties.xul)
-*  content/messenger-newsblog/feed-properties.js                (content/feed-properties.js) 
 *  content/messenger-newsblog/feed-subscriptions.js             (content/feed-subscriptions.js)
 *  content/messenger-newsblog/feed-subscriptions.xul            (content/feed-subscriptions.xul)
 *  content/messenger-newsblog/am-newsblog.xul                   (content/am-newsblog.xul)
 *  content/messenger-newsblog/am-newsblog.js                    (content/am-newsblog.js)
deleted file mode 100644
--- a/suite/locales/en-US/chrome/mailnews/newsblog/am-newsblog.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-## Strings used in the Account Manager
-prefPanel-newsblog=Feed Settings
deleted file mode 100644
--- a/suite/locales/en-US/chrome/mailnews/newsblog/feed-properties.dtd
+++ /dev/null
@@ -1,19 +0,0 @@
-<!-- Feed Properties Dialog -->
-<!ENTITY window.title             "Feed Properties">
-
-<!ENTITY feedFolder.label         "Store Articles in: ">
-<!ENTITY feedFolder.accesskey     "S">
-
-<!ENTITY feedLocation.label       "Feed URL: ">
-<!ENTITY feedLocation.accesskey   "F">
-
-<!ENTITY choosethisfolder.label   "choose this folder">
-
-<!ENTITY quickMode.label          "Show the article summary instead of loading the web page">
-<!ENTITY quickMode.accesskey      "h">
-
-<!ENTITY copyLinkCmd.label        "Copy Link Location">
-<!ENTITY copyLinkCmd.accesskey    "C">
-
-<!ENTITY pasteLinkCmd.label       "Paste">
-<!ENTITY pasteLinkCmd.accesskey   "P">
--- a/suite/locales/en-US/chrome/mailnews/newsblog/feed-subscriptions.dtd
+++ b/suite/locales/en-US/chrome/mailnews/newsblog/feed-subscriptions.dtd
@@ -1,18 +1,31 @@
 <!-- Subscription Dialog -->
-<!ENTITY feedSubscriptions.label "Feed Subscriptions">
-<!ENTITY subscriptionDesc.label "Note: Removing or changing the folder for a feed will not affect previously downloaded articles.">
-<!ENTITY feedTitle.label "Title:">
-<!ENTITY location.label  "Location:">
+<!ENTITY feedSubscriptions.label     "Feed Subscriptions">
+<!ENTITY subscriptionDesc.label      "Note: Removing or changing the folder for a feed will not affect previously downloaded articles.">
+
+<!ENTITY feedTitle.label             "Title:">
+<!ENTITY feedTitle.accesskey         "T">
+
+<!ENTITY feedLocation.label          "Feed URL:">
+<!ENTITY feedLocation.accesskey      "U">
+<!ENTITY feedLocation.placeholder    "Enter a valid feed url to Add">
+
+<!ENTITY feedFolder.label            "Store Articles in:">
+<!ENTITY feedFolder.accesskey        "S">
 
-<!ENTITY button.addFeed.label       "Add">
-<!ENTITY button.addFeed.accesskey   "A">
-<!ENTITY button.editFeed.label      "Edit">
-<!ENTITY button.editFeed.accesskey  "E">
-<!ENTITY button.removeFeed.label "Remove">
-<!ENTITY button.removeFeed.accesskey   "R">
-<!ENTITY button.importOPML.label "Import">
-<!ENTITY button.importOPML.accesskey   "I">
-<!ENTITY button.exportOPML.label "Export">
-<!ENTITY button.exportOPML.accesskey   "X">
+<!ENTITY quickMode.label             "Show the article summary instead of loading the web page">
+<!ENTITY quickMode.accesskey         "h">
+
+<!ENTITY validateText.label          "Check validation and retrieve a valid url.">
 
-<!ENTITY cmd.close.commandKey             "w">
+<!ENTITY button.addFeed.label        "Add">
+<!ENTITY button.addFeed.accesskey    "A">
+<!ENTITY button.editFeed.label       "Update">
+<!ENTITY button.editFeed.accesskey   "U">
+<!ENTITY button.removeFeed.label     "Remove">
+<!ENTITY button.removeFeed.accesskey "R">
+<!ENTITY button.importOPML.label     "Import">
+<!ENTITY button.importOPML.accesskey "I">
+<!ENTITY button.exportOPML.label     "Export">
+<!ENTITY button.exportOPML.accesskey "X">
+
+<!ENTITY cmd.close.commandKey        "w">
--- a/suite/locales/en-US/chrome/mailnews/newsblog/newsblog.properties
+++ b/suite/locales/en-US/chrome/mailnews/newsblog/newsblog.properties
@@ -1,14 +1,19 @@
 subscribe-validating-feed=Verifying the feed…
 subscribe-cancelSubscription=Are you sure you wish to cancel subscribing to the current feed?
 subscribe-cancelSubscriptionTitle=Subscribing to a Feed…
-subscribe-noFeedIsSelected=<no feed selected>
 subscribe-feedAlreadySubscribed=You already have a subscription for this feed.
 subscribe-errorOpeningFile=Could not open the file.
+subscribe-feedAdded=Feed added.
+subscribe-feedUpdated=Feed updated.
+subscribe-feedMoved=Feed subscription moved.
+subscribe-feedRemoved=Feed unsubscribed.
+subscribe-feedNotValid=The Feed URL is not a valid feed.
+subscribe-networkError=The Feed URL could not be found. Please check the name and try again.
 
 subscribe-OPMLImportTitle=Select OPML file to import
 subscribe-OPMLExportTitle=Export feeds as an OPML file
 ## LOCALIZATION NOTE(subscribe-OPMLExportFileDialogTitle): %S is the brandShortName
 subscribe-OPMLExportFileDialogTitle=%S OPML Export
 ## LOCALIZATION NOTE(subscribe-OPMLExportDefaultFileName): %S is the brandShortName
 subscribe-OPMLExportDefaultFileName=My%SFeeds.opml
 ## LOCALIZATION NOTE(subscribe-OPMLImportInvalidFile): %S is the name of the OPML file the user tried to import.
@@ -27,18 +32,16 @@ subscribe-confirmFeedDeletionTitle=Remov
 subscribe-confirmFeedDeletion=Are you sure you want to unsubscribe from the feed: \n %S?
 
 ## LOCALIZATION NOTE(subscribe-gettingFeedItems):
 ##  - The first %S is the number of articles processed so far;
 ##  - The second %S is the total number of items
 subscribe-gettingFeedItems=Downloading feed articles (%S of %S)…
 
 newsblog-noNewArticlesForFeed=There are no new articles for this feed.
-newsblog-networkErrorTitle=Network Error
 ## LOCALIZATION NOTE(newsblog-networkError): %S is the feed URL
 newsblog-networkError=%S could not be found. Please check the name and try again.
-newsblog-feedNotValidTitle=Invalid Feed
 ## LOCALIZATION NOTE(newsblog-feedNotValid): %S is the feed URL
 newsblog-feedNotValid=%S is not a valid feed.
 newsblog-getNewMsgsCheck=Checking feeds for new items…
 
 ## LOCALIZATION NOTE(feeds-accountname): This string should be the same as feeds.accountName in am-newsblog.dtd
 feeds-accountname=Blogs & News Feeds
--- a/suite/locales/jar.mn
+++ b/suite/locales/jar.mn
@@ -334,18 +334,16 @@
   locale/@AB_CD@/messenger-smime/msgCompSMIMEOverlay.properties             (%chrome/mailnews/smime/msgCompSMIMEOverlay.properties)
   locale/@AB_CD@/messenger-smime/msgReadSecurityInfo.dtd                    (%chrome/mailnews/smime/msgReadSecurityInfo.dtd)
   locale/@AB_CD@/messenger-smime/msgReadSMIMEOverlay.dtd                    (%chrome/mailnews/smime/msgReadSMIMEOverlay.dtd)
   locale/@AB_CD@/messenger-smime/msgReadSMIMEOverlay.properties             (%chrome/mailnews/smime/msgReadSMIMEOverlay.properties)
   locale/@AB_CD@/messenger-smime/msgSecurityInfo.properties                 (%chrome/mailnews/smime/msgSecurityInfo.properties)
 #endif
   locale/@AB_CD@/messenger-newsblog/newsblog.properties                     (%chrome/mailnews/newsblog/newsblog.properties)
   locale/@AB_CD@/messenger-newsblog/feed-subscriptions.dtd                  (%chrome/mailnews/newsblog/feed-subscriptions.dtd)
-  locale/@AB_CD@/messenger-newsblog/feed-properties.dtd                     (%chrome/mailnews/newsblog/feed-properties.dtd)
-  locale/@AB_CD@/messenger-newsblog/am-newsblog.properties                  (%chrome/mailnews/newsblog/am-newsblog.properties)
   locale/@AB_CD@/messenger-newsblog/am-newsblog.dtd                         (%chrome/mailnews/newsblog/am-newsblog.dtd)
   locale/@AB_CD@/mozldap/ldap.properties                                    (%chrome/mozldap/ldap.properties)
   locale/@AB_CD@/navigator/linkToolbar.dtd                                  (%chrome/browser/linkToolbar.dtd)
   locale/@AB_CD@/navigator/mailNavigatorOverlay.dtd                         (%chrome/browser/mailNavigatorOverlay.dtd)
   locale/@AB_CD@/navigator/metadata.dtd                                     (%chrome/browser/metadata.dtd)
   locale/@AB_CD@/navigator/metadata.properties                              (%chrome/browser/metadata.properties)
   locale/@AB_CD@/navigator/navigator.dtd                                    (%chrome/browser/navigator.dtd)
   locale/@AB_CD@/navigator/navigator.properties                             (%chrome/browser/navigator.properties)
--- a/suite/themes/classic/messenger/newsblog/feed-subscriptions.css
+++ b/suite/themes/classic/messenger/newsblog/feed-subscriptions.css
@@ -31,37 +31,29 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ****** */
 
-/* ::::: Feed Subscription UI icons :::::: */
+/* ::::: Feed Subscription styling :::::: */
+
+#subscriptionsDialog {
+  width: 40em;
+  height: 30em;
+}
 
 #contentPane {
   padding: 14px;
 }
 
-#subscriptionChildren::-moz-tree-image(folderNameCol) {
-  -moz-margin-end: 2px;
-  list-style-image: url("chrome://messenger/skin/icons/folder-newsgroup.png");
-}
-
-#subscriptionChildren::-moz-tree-image(folderNameCol, container) {
-  list-style-image: url("chrome://messenger/skin/icons/folder-closed.png");
+#rssFeedInfoBox {
+  border: 1px solid ThreeDShadow;
+  margin: 4px;
+  padding-top: 4px;
+  background-color: ThreeDLightShadow;
 }
 
-#subscriptionChildren::-moz-tree-image(folderNameCol, container, open) {
-  list-style-image: url("chrome://messenger/skin/icons/folder-open.png");
+#statusContainerBox {
+  height: 24px;
 }
-
-#rssFeedInfoBox {
-  border: 1px solid ThreeDShadow;
-  border-radius: 0px;
-  margin: 4px;
-  padding: 0px;
-}
-
-#backgroundBox {
-  background-color: ThreeDLightShadow;
-}
--- a/suite/themes/modern/messenger/newsblog/feed-subscriptions.css
+++ b/suite/themes/modern/messenger/newsblog/feed-subscriptions.css
@@ -31,37 +31,29 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ****** */
 
-/* ::::: Feed Subscription UI icons :::::: */
+/* ::::: Feed Subscription styling :::::: */
+
+#subscriptionsDialog {
+  width: 40em;
+  height: 30em;
+}
 
 #contentPane {
   padding: 14px;
 }
 
-#subscriptionChildren::-moz-tree-image(folderNameCol) {
-  -moz-margin-end: 2px;
-  list-style-image: url("chrome://messenger/skin/icons/folder-newsgroup.gif");
-}
-
-#subscriptionChildren::-moz-tree-image(folderNameCol, container) {
-  list-style-image: url("chrome://messenger/skin/icons/folder-closed.gif");
+#rssFeedInfoBox {
+  border: 1px solid #2D3B49;
+  margin: 4px;
+  padding-top: 4px;
+  background-color: #BBC6D1;
 }
 
-#subscriptionChildren::-moz-tree-image(folderNameCol, container, open) {
-  list-style-image: url("chrome://messenger/skin/icons/folder-open.gif");
+#statusContainerBox {
+  height: 24px;
 }
-
-#rssFeedInfoBox {
-  border: 1px solid #2D3B49;
-  border-radius: 0px;
-  margin: 4px;
-  padding: 0px;
-}
-
-#backgroundBox {
-  background-color: #BBC6D1;
-}