Bug 1466705 - Restore IMAP and NNTP subscribe to use a C++ nsITreeView. r=jorgk,aceman a=jorgk BETA_60_CONTINUATION
authorJoshua Cranmer <Pidgeot18@gmail.com>, aceman <acelists@atlas.sk> and Jorg K <jorgk@jorgk.com>
Thu, 24 Mar 2011 12:24:48 -0400
branchBETA_60_CONTINUATION
changeset 31994 f87c98a8bb92efc0eb490d9721983315e920a396
parent 31993 be58f31115b63d29b5ceef414bb2447c4c138903
child 31995 df5d0c0ef77f45e9f8a0af7b6cc960f02ac36ba9
push id384
push userclokep@gmail.com
push dateTue, 26 Jun 2018 01:38:29 +0000
reviewersjorgk, aceman, jorgk
bugs1466705
Bug 1466705 - Restore IMAP and NNTP subscribe to use a C++ nsITreeView. r=jorgk,aceman a=jorgk
mail/locales/en-US/chrome/messenger/subscribe.properties
mailnews/base/content/subscribe.js
mailnews/base/content/subscribe.xul
mailnews/base/public/nsISubscribableServer.idl
mailnews/base/src/nsSubscribableServer.cpp
mailnews/base/src/nsSubscribableServer.h
mailnews/imap/src/nsImapIncomingServer.cpp
mailnews/news/src/nsNntpIncomingServer.cpp
--- a/mail/locales/en-US/chrome/messenger/subscribe.properties
+++ b/mail/locales/en-US/chrome/messenger/subscribe.properties
@@ -4,8 +4,10 @@
 
 subscribeLabel-nntp=Select the newsgroups to subscribe to:
 subscribeLabel-imap=Select the folders to subscribe to:
 currentListTab-nntp.label=Current Group List
 currentListTab-nntp.accesskey=L
 currentListTab-imap.label=Folder List
 currentListTab-imap.accesskey=L
 pleaseWaitString=Please wait…
+offlineState=You are offline. Items could not be retrieved from the server.
+errorPopulating=Error retrieving items from the server.
--- a/mailnews/base/content/subscribe.js
+++ b/mailnews/base/content/subscribe.js
@@ -13,22 +13,20 @@ var gChangeTable = {};
 var gServerURI = null;
 var gSubscribableServer = null;
 var gNameField = null;
 var gNameFieldLabel = null;
 var gStatusFeedback;
 var gSubscribeDeck = null;
 var gSearchView = null;
 var gSearchTreeBoxObject = null;
-var gItemsFound = new Map();
 var gSubscribeBundle;
 
 function Stop()
 {
-  //dump("Stop()\n")
   if (gSubscribableServer) {
     gSubscribableServer.stopPopulating(msgWindow);
   }
 }
 
 function SetServerTypeSpecificTextValues()
 {
   if (!gServerURI)
@@ -54,131 +52,86 @@ function onServerClick(aFolder)
   let serverMenu = document.getElementById("serverMenu");
   serverMenu.menupopup.selectFolder(aFolder);
 
   SetServerTypeSpecificTextValues();
   ShowCurrentList();
 }
 
 var MySubscribeListener = {
-  OnItemDiscovered: function(aPath, aSubscribable) {
-    if (!gItemsFound.has(aPath))
-      gItemsFound.set(aPath, gSubscribableServer.getLeafName(aPath));
-  },
-
   OnDonePopulating: function() {
-    createSubscribeTree();
     gStatusFeedback._stopMeteors();
     document.getElementById("stopButton").disabled = true;
-
-// XXX what does this do? Is this needed without RDF/template?
-    // Only re-root the tree, if it is null.
-    // Otherwise, we are in here because we are populating a part of the tree.
-/*    let refValue = gSubscribeTree.getAttribute('ref');
-    if (!refValue) {
-      //dump("root subscribe tree at: "+ gServerURI +"\n");
-      gSubscribeTree.database.AddDataSource(subscribeDS);
-      gSubscribeTree.setAttribute('ref',gServerURI);
-    }
-*/
-
     document.getElementById("refreshButton").disabled = false;
     document.getElementById("currentListTab").disabled = false;
     document.getElementById("newGroupsTab").disabled = false;
   }
 };
 
-function setSubscribableCellState(aSubscribeCell, aSubscribed, aSubscribable) {
-  aSubscribeCell.setAttribute("properties", "subscribed-" + aSubscribed +
-                                            " subscribable-" + aSubscribable);
-  aSubscribeCell.setAttribute("value", aSubscribed);
-}
-
-/**
- * Populate the subscribe tree with the items found on the server.
- */
-function createSubscribeTree() {
-  let items = toArray(gItemsFound.entries());
-  items.sort((a,b) => a[0].localeCompare(b[0]));
-
-  for (let [path, name] of items) {
-    let subscribed = gSubscribableServer.isSubscribed(path);
-    let subscribable = gSubscribableServer.isSubscribable(path);
-    let itemEl = document.createElement("treeitem");
-    let rowEl = document.createElement("treerow");
-    let nameEl = document.createElement("treecell");
-    let subscribeEl = document.createElement("treecell");
-    nameEl.setAttribute("label", name);
-    nameEl.setAttribute("value", path);
-    nameEl.setAttribute("properties", "serverType-" + gSubscribableServer.type +
-                                      " subscribable-" + subscribable);
-    setSubscribableCellState(subscribeEl, subscribed, subscribable);
-    rowEl.appendChild(nameEl);
-    rowEl.appendChild(subscribeEl);
-    itemEl.appendChild(rowEl);
-    gSubscribeBody.appendChild(itemEl);
- }
-}
-
 function SetUpTree(forceToServer, getOnlyNew)
 {
   if (!gServerURI)
     return;
 
   var server = MailUtils.getFolderForURI(gServerURI, true).server;
   try
   {
     CleanUpSearchView();
     gSubscribableServer = server.QueryInterface(Ci.nsISubscribableServer);
-    gSubscribeTree.setAttribute('ref', ''); // XXX: what is this?
 
     // enable (or disable) the search related UI
     EnableSearchUI();
 
     // clear out the text field when switching server
     gNameField.value = "";
 
     // since there is no text, switch to the non-search view...
     SwitchToNormalView();
 
-    // Clear the subscribe tree.
-    while (gSubscribeBody.hasChildNodes())
-      gSubscribeBody.lastChild.remove();
-
-    gSubscribableServer.subscribeListener = MySubscribeListener;
+    if (!gSubscribableServer.subscribeListener) {
+      gSubscribeTree.view = gSubscribableServer.folderView;
+      gSubscribableServer.subscribeListener = MySubscribeListener;
+    }
 
     var currentListTab = document.getElementById("currentListTab");
     if (currentListTab.selected)
       document.getElementById("newGroupsTab").disabled = true;
     else
       currentListTab.disabled = true;
 
     document.getElementById("refreshButton").disabled = true;
 
     gStatusFeedback._startMeteors();
+    gStatusFeedback.setStatusString("");
     gStatusFeedback.showStatusString(gSubscribeBundle.getString("pleaseWaitString"));
-    document.getElementById("stopButton").removeAttribute("disabled");
+    document.getElementById("stopButton").disabled = false;
 
-    gItemsFound.clear();
     gSubscribableServer.startPopulating(msgWindow, forceToServer, getOnlyNew);
   }
-  catch (ex)
+  catch (e)
   {
-    dump("failed to populate subscribe ds: " + ex + "\n");
+    if (e.result == 0x80550014) {  // NS_MSG_ERROR_OFFLINE
+      // Hack for TB 60 reusing a string from offline.properties.
+      let offlineBundle = document.getElementById("bundle_offlinePrompts");
+      gStatusFeedback.setStatusString(offlineBundle.getString("offlineTooltip"));
+    } else {
+      Cu.reportError("Failed to populate subscribe tree: " + e);
+    }
+    Stop();
   }
 }
 
 
 function SubscribeOnUnload()
 {
   try {
     CleanUpSearchView();
   }
   catch (ex) {
-    dump("failed to remove the subscribe ds: " + ex + "\n");
+    dump("Failed to remove the subscribe tree: " + ex + "\n");
   }
 }
 
 function EnableSearchUI()
 {
   if (gSubscribableServer.supportsSubscribeSearch) {
     gNameField.removeAttribute('disabled');
     gNameFieldLabel.removeAttribute('disabled');
@@ -186,17 +139,16 @@ function EnableSearchUI()
   else {
     gNameField.setAttribute('disabled',true);
     gNameFieldLabel.setAttribute('disabled',true);
   }
 }
 
 function SubscribeOnLoad()
 {
-  //dump("SubscribeOnLoad()\n");
   gSubscribeBundle = document.getElementById("bundle_subscribe");
 
   gSubscribeTree = document.getElementById("subscribeTree");
   gSubscribeBody = document.getElementById("subscribeTreeBody");
   gSearchTree = document.getElementById("searchTree");
   gSearchTreeBoxObject = document.getElementById("searchTree").treeBoxObject;
   gNameField = document.getElementById("namefield");
   gNameFieldLabel = document.getElementById("namefieldlabel");
@@ -259,17 +211,16 @@ function SubscribeOnLoad()
 
   ShowCurrentList();
 
   gNameField.focus();
 }
 
 function subscribeOK()
 {
-  //dump("in subscribeOK()\n")
   if (top.okCallback) {
     top.okCallback(top.gChangeTable);
   }
   Stop();
   if (gSubscribableServer) {
     gSubscribableServer.subscribeCleanup();
   }
   return true;
@@ -279,42 +230,21 @@ function subscribeCancel()
 {
   Stop();
   if (gSubscribableServer) {
     gSubscribableServer.subscribeCleanup();
   }
   return true;
 }
 
-function SetState(name, state, aRow)
+function SetState(name, state)
 {
   var changed = gSubscribableServer.setState(name, state);
   if (changed)
     StateChanged(name, state);
-
-  let subscribeCell;
-  if (aRow === undefined) {
-    // XXX This won't work when multiple levels of children are implemented
-    for (let row of gSubscribeBody.children) {
-      if (row.children[0].children[0].getAttribute("value") == name) {
-        subscribeCell = row;
-        break;
-      }
-    }
-  } else {
-    subscribeCell = gSubscribeBody.children[aRow];
-  }
-  setSubscribableCellState(subscribeCell.children[0].children[1], state, true);
-}
-
-function changeTableRecord(server, name, state)
-{
-  this.server = server;
-  this.name = name;
-  this.state = state;
 }
 
 function StateChanged(name,state)
 {
   if (gServerURI in gChangeTable) {
     if (name in gChangeTable[gServerURI]) {
       var oldValue = gChangeTable[gServerURI][name];
       if (oldValue != state)
@@ -413,17 +343,16 @@ function SetSubscribeState(state)
   catch (ex) {
     dump("SetSubscribedState failed:  " + ex + "\n");
   }
 }
 
 function ReverseStateFromNode(row)
 {
   let name = gSubscribeTree.view.getCellValue(row, gSubscribeTree.columns["nameColumn"]);
-
   SetState(name, !gSubscribableServer.isSubscribed(name), row);
 }
 
 function SubscribeOnClick(event)
 {
   // we only care about button 0 (left click) events
   if (event.button != 0 || event.originalTarget.localName != "treechildren")
    return;
@@ -438,31 +367,19 @@ function SubscribeOnClick(event)
     // that isn't a container
     if (!gSubscribeTree.view.isContainer(row.value)) {
       ReverseStateFromNode(row.value);
       return;
     }
   }
   else if (event.detail == 1)
   {
-    if (obj.value == "twisty") {
-        if (gSubscribeTree.view.isContainerOpen(row.value)) {
-          var uri = gSubscribeTree.builderView.getResourceAtIndex(row.value).Value;
-
-          gStatusFeedback._startMeteors();
-          gStatusFeedback.showStatusString(gSubscribeBundle.getString("pleaseWaitString"));
-
-          gSubscribableServer.startPopulatingWithUri(msgWindow, true /* force to server */, uri);
-        }
-    }
-    else {
-      // if the user single clicks on the subscribe check box, we handle it here
-      if (col.value.id == "subscribedColumn")
-        ReverseStateFromNode(row.value);
-    }
+    // if the user single clicks on the subscribe check box, we handle it here
+    if (col.value.id == "subscribedColumn")
+      ReverseStateFromNode(row.value);
   }
 }
 
 function Refresh()
 {
   // clear out the textfield's entry
   gNameField.value = "";
 
--- a/mailnews/base/content/subscribe.xul
+++ b/mailnews/base/content/subscribe.xul
@@ -2,32 +2,34 @@
 
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <?xml-stylesheet href="chrome://messenger/skin/subscribe.css" type="text/css"?>
 <!-- <?xml-stylesheet href="chrome://messenger/skin/messenger.css" type="text/css"?> -->
 <?xml-stylesheet href="chrome://messenger/skin/folderMenus.css" type="text/css"?>
+<?xml-stylesheet href="chrome://messenger/content/bindings.css" type="text/css"?>
 
 <!DOCTYPE window SYSTEM "chrome://messenger/locale/subscribe.dtd">
 
 <dialog id="subscribeWindow"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         title="&subscribeDialog.title;"
         style="width: 44em; height: 33em;"
         persist="width height screenX screenY"
         onload="SubscribeOnLoad()"
         onunload="SubscribeOnUnload()"
         windowtype="mailnews:subscribe"
         ondialogaccept="return subscribeOK();"
         ondialogcancel="return subscribeCancel();">
 
   <stringbundle id="bundle_subscribe" src="chrome://messenger/locale/subscribe.properties"/>
   <stringbundle id="bundle_messenger" src="chrome://messenger/locale/messenger.properties"/>
+  <stringbundle id="bundle_offlinePrompts" src="chrome://messenger/locale/offline.properties"/>
 
   <script type="application/javascript"
           src="chrome://global/content/globalOverlay.js"/>
   <script type="application/javascript"
           src="chrome://messenger/content/mailWindow.js"/>
   <script type="application/javascript"
           src="chrome://messenger/content/subscribe.js"/>
 
--- a/mailnews/base/public/nsISubscribableServer.idl
+++ b/mailnews/base/public/nsISubscribableServer.idl
@@ -5,28 +5,32 @@
 
 #include "nsISupports.idl"
 
 interface nsIMsgWindow;
 interface nsIMsgIncomingServer;
 interface nsIRDFResource;
 interface nsIRDFNode;
 interface nsISimpleEnumerator;
+interface nsITreeView;
 
 [scriptable, uuid(61a08c3a-1dd2-11b2-b64f-c4b2de1cf129)]
 interface nsISubscribeDataSource : nsISupports {
     readonly attribute boolean hasObservers;
     void NotifyObservers(in nsIRDFResource subject, in nsIRDFResource property, in nsIRDFNode object, in boolean isAssert, in boolean isChange);
 };
 
+/**
+ * A listener to receive notification of the subscribable folders of a server.
+ */
 [scriptable, uuid(f337b84a-1dd1-11b2-97c7-fb8b2e3f2280)]
 interface nsISubscribeListener : nsISupports {
-  // Announce a new item found on the server.
-  void OnItemDiscovered(in AUTF8String aPath, in boolean aSubscribable);
-  // Announce finish of discovering server items.
+  /**
+   * The server has finished finding all folders to subscribe to.
+   */
   void OnDonePopulating();
 };
 
 [scriptable, uuid(14b8597a-755b-4e93-b364-e0903801e6ea)]
 interface nsISubscribableServer : nsISupports {
   attribute nsISubscribeListener subscribeListener;
   attribute char delimiter;
 
@@ -70,10 +74,11 @@ interface nsISubscribableServer : nsISup
    */
   nsISimpleEnumerator getChildren(in AUTF8String aPath);
   // if path is null, use the root
   AUTF8String getFirstChildURI(in AUTF8String path);
 
   // for searching
   void setSearchValue(in AString searchValue);
   readonly attribute boolean supportsSubscribeSearch;
+  readonly attribute nsITreeView folderView;
 };
 
--- a/mailnews/base/src/nsSubscribableServer.cpp
+++ b/mailnews/base/src/nsSubscribableServer.cpp
@@ -9,16 +9,17 @@
 #include "rdf.h"
 #include "nsRDFCID.h"
 #include "nsIServiceManager.h"
 #include "nsMsgI18N.h"
 #include "nsMsgUtils.h"
 #include "nsCOMArray.h"
 #include "nsArrayEnumerator.h"
 #include "nsServiceManagerUtils.h"
+#include "nsITreeColumns.h"
 
 static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
 
 nsSubscribableServer::nsSubscribableServer(void)
 {
     mDelimiter = '.';
     mShowFullName = true;
     mTreeRoot = nullptr;
@@ -46,42 +47,46 @@ nsSubscribableServer::Init()
 
     rv = mRDFService->GetLiteral(u"false", getter_AddRefs(kFalseLiteral));
     NS_ENSURE_SUCCESS(rv,rv);
     return NS_OK;
 }
 
 nsSubscribableServer::~nsSubscribableServer(void)
 {
-  mozilla::DebugOnly<nsresult> rv = FreeSubtree(mTreeRoot);
+  mozilla::DebugOnly<nsresult> rv = FreeRows();
+  NS_ASSERTION(NS_SUCCEEDED(rv), "failed to free tree rows");
+  rv = FreeSubtree(mTreeRoot);
   NS_ASSERTION(NS_SUCCEEDED(rv),"failed to free tree");
 }
 
-NS_IMPL_ISUPPORTS(nsSubscribableServer, nsISubscribableServer)
+NS_IMPL_ISUPPORTS(nsSubscribableServer, nsISubscribableServer, nsITreeView)
 
 NS_IMETHODIMP
 nsSubscribableServer::SetIncomingServer(nsIMsgIncomingServer *aServer)
 {
   if (!aServer) {
     mIncomingServerUri.AssignLiteral("");
+    mServerType.Truncate();
     return NS_OK;
   }
+  aServer->GetType(mServerType);
 
   // We intentionally do not store a pointer to the aServer here
   // as it would create reference loops, because nsIImapIncomingServer
   // and nsINntpIncomingServer keep a reference to an internal
   // nsISubscribableServer object.
   // We only need the URI of the server anyway.
   return aServer->GetServerURI(mIncomingServerUri);
 }
 
 NS_IMETHODIMP
 nsSubscribableServer::GetDelimiter(char *aDelimiter)
 {
-  if (!aDelimiter) return NS_ERROR_NULL_POINTER;
+  NS_ENSURE_ARG_POINTER(aDelimiter);
   *aDelimiter = mDelimiter;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSubscribableServer::SetDelimiter(char aDelimiter)
 {
   mDelimiter = aDelimiter;
@@ -94,61 +99,53 @@ nsSubscribableServer::SetAsSubscribed(co
     nsresult rv = NS_OK;
 
     SubscribeTreeNode *node = nullptr;
     rv = FindAndCreateNode(path, &node);
     NS_ENSURE_SUCCESS(rv,rv);
 
     NS_ASSERTION(node,"didn't find the node");
     if (!node) return NS_ERROR_FAILURE;
-
     node->isSubscribable = true;
     node->isSubscribed = true;
 
     rv = NotifyChange(node, kNC_Subscribed, node->isSubscribed);
     NS_ENSURE_SUCCESS(rv,rv);
 
     return rv;
 }
 
 NS_IMETHODIMP
 nsSubscribableServer::AddTo(const nsACString& aName, bool aAddAsSubscribed,
                             bool aSubscribable, bool aChangeIfExists)
 {
     nsresult rv = NS_OK;
 
     if (mStopped) {
-#ifdef DEBUG_seth
-        printf("stopped!\n");
-#endif
         return NS_ERROR_FAILURE;
     }
 
     SubscribeTreeNode *node = nullptr;
 
     // todo, shouldn't we pass in aAddAsSubscribed, for the
     // default value if we create it?
     rv = FindAndCreateNode(aName, &node);
     NS_ENSURE_SUCCESS(rv,rv);
-
     NS_ASSERTION(node,"didn't find the node");
     if (!node) return NS_ERROR_FAILURE;
 
     if (aChangeIfExists) {
         node->isSubscribed = aAddAsSubscribed;
         rv = NotifyChange(node, kNC_Subscribed, node->isSubscribed);
         NS_ENSURE_SUCCESS(rv,rv);
     }
 
     node->isSubscribable = aSubscribable;
 
-    if (mSubscribeListener)
-        mSubscribeListener->OnItemDiscovered(aName, aSubscribable);
-
-    return rv;
+    return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSubscribableServer::SetState(const nsACString &aPath, bool aState,
                                bool *aStateChanged)
 {
     nsresult rv = NS_OK;
     NS_ASSERTION(!aPath.IsEmpty() && aStateChanged, "no path or stateChanged");
@@ -173,19 +170,26 @@ nsSubscribableServer::SetState(const nsA
     if (node->isSubscribed == aState) {
         return NS_OK;
     }
     else {
         node->isSubscribed = aState;
         *aStateChanged = true;
         rv = NotifyChange(node, kNC_Subscribed, node->isSubscribed);
         NS_ENSURE_SUCCESS(rv,rv);
+
+        // Repaint the tree row to show/clear the check mark.
+        if (mTree) {
+          bool dummy;
+          int32_t index = GetRow(node, &dummy);
+          if (index >= 0)
+            mTree->InvalidateRow(index);
+        }
     }
-
-    return rv;
+    return NS_OK;
 }
 
 void
 nsSubscribableServer::BuildURIFromNode(SubscribeTreeNode *node, nsACString &uri)
 {
     if (node->parent) {
         BuildURIFromNode(node->parent, uri);
         if (node->parent == mTreeRoot) {
@@ -329,17 +333,17 @@ nsSubscribableServer::SetSubscribeListen
 {
   mSubscribeListener = aListener;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSubscribableServer::GetSubscribeListener(nsISubscribeListener **aListener)
 {
-  if (!aListener) return NS_ERROR_NULL_POINTER;
+  NS_ENSURE_ARG_POINTER(aListener);
   NS_IF_ADDREF(*aListener = mSubscribeListener);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSubscribableServer::SubscribeCleanup()
 {
   NS_ASSERTION(false,"override this.");
@@ -351,31 +355,63 @@ nsSubscribableServer::StartPopulatingWit
 {
     mStopped = false;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSubscribableServer::StartPopulating(nsIMsgWindow *aMsgWindow, bool aForceToServer, bool aGetOnlyNew /*ignored*/)
 {
-    nsresult rv = NS_OK;
+    mStopped = false;
 
-    mStopped = false;
+    nsresult rv = FreeRows();
+    NS_ENSURE_SUCCESS(rv, rv);
 
     rv = FreeSubtree(mTreeRoot);
     mTreeRoot = nullptr;
     NS_ENSURE_SUCCESS(rv,rv);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSubscribableServer::StopPopulating(nsIMsgWindow *aMsgWindow)
 {
-    mStopped = true;
-    return NS_OK;
+  mStopped = true;
+
+  nsresult rv = FreeRows();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (mTreeRoot) {
+    SubscribeTreeNode *node = mTreeRoot->lastChild;
+    // Add top level items as closed.
+    while (node)
+    {
+      node->isOpen = false;
+      mRowMap.AppendElement(node);
+      node = node->prevSibling;
+    }
+
+    // Invalidate the whole thing.
+    if (mTree)
+      mTree->RowCountChanged(0, mRowMap.Length());
+
+    // Open all the top level items if they are containers.
+    uint32_t topRows = mRowMap.Length();
+    for (int32_t i = topRows - 1; i >= 0; i--) {
+      bool isContainer = false;
+      IsContainer(i, &isContainer);
+      if (isContainer)
+        ToggleOpenState(i);
+    }
+  }
+
+  if (mSubscribeListener)
+    mSubscribeListener->OnDonePopulating();
+
+  return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsSubscribableServer::UpdateSubscribed()
 {
   NS_ASSERTION(false,"override this.");
   return NS_ERROR_FAILURE;
@@ -430,34 +466,48 @@ nsSubscribableServer::FreeSubtree(Subscr
         free(node->name);
 #if 0
         node->name = nullptr;
         node->parent = nullptr;
         node->lastChild = nullptr;
         node->cachedChild = nullptr;
 #endif
 
-        PR_Free(node);
+        delete node;
     }
 
     return NS_OK;
 }
 
 nsresult
-nsSubscribableServer::CreateNode(SubscribeTreeNode *parent, const char *name, SubscribeTreeNode **result)
+nsSubscribableServer::FreeRows()
+{
+  int32_t rowCount = mRowMap.Length();
+  mRowMap.Clear();
+  if (mTree)
+    mTree->RowCountChanged(0, -rowCount);
+
+  return NS_OK;
+}
+
+nsresult
+nsSubscribableServer::CreateNode(SubscribeTreeNode *parent, const char *name, const nsACString &aPath, SubscribeTreeNode **result)
 {
     NS_ASSERTION(result && name, "result or name is null");
-    if (!result || !name) return NS_ERROR_NULL_POINTER;
+    NS_ENSURE_ARG_POINTER(result);
+    NS_ENSURE_ARG_POINTER(name);
 
-    *result = (SubscribeTreeNode *) PR_Malloc(sizeof(SubscribeTreeNode));
+    *result = new SubscribeTreeNode();
     if (!*result) return NS_ERROR_OUT_OF_MEMORY;
 
     (*result)->name = strdup(name);
     if (!(*result)->name) return NS_ERROR_OUT_OF_MEMORY;
 
+    (*result)->path.Assign(aPath);
+
     (*result)->parent = parent;
     (*result)->prevSibling = nullptr;
     (*result)->nextSibling = nullptr;
     (*result)->firstChild = nullptr;
     (*result)->lastChild = nullptr;
     (*result)->isSubscribed = false;
     (*result)->isSubscribable = false;
 #ifdef HAVE_SUBSCRIBE_DESCRIPTION
@@ -467,29 +517,31 @@ nsSubscribableServer::CreateNode(Subscri
     (*result)->messages = 0;
 #endif
     (*result)->cachedChild = nullptr;
 
     if (parent) {
         parent->cachedChild = *result;
     }
 
+    (*result)->isOpen = true;
+
     return NS_OK;
 }
 
 nsresult
-nsSubscribableServer::AddChildNode(SubscribeTreeNode *parent, const char *name, SubscribeTreeNode **child)
+nsSubscribableServer::AddChildNode(SubscribeTreeNode *parent, const char *name, const nsACString &aPath, SubscribeTreeNode **child)
 {
     nsresult rv = NS_OK;
-    NS_ASSERTION(parent && child && name, "parent, child or name is null");
-    if (!parent || !child || !name) return NS_ERROR_NULL_POINTER;
+    NS_ASSERTION(parent && child && name && !aPath.IsEmpty(), "parent, child or name is null");
+    if (!parent || !child || !name || aPath.IsEmpty()) return NS_ERROR_NULL_POINTER;
 
     if (!parent->firstChild) {
         // CreateNode will set the parent->cachedChild
-        rv = CreateNode(parent, name, child);
+        rv = CreateNode(parent, name, aPath, child);
         NS_ENSURE_SUCCESS(rv,rv);
 
         parent->firstChild = *child;
         parent->lastChild = *child;
 
         rv = NotifyAssert(parent, kNC_Child, *child);
         NS_ENSURE_SUCCESS(rv,rv);
 
@@ -518,17 +570,17 @@ nsSubscribableServer::AddChildNode(Subsc
      * we can efficiently reverse the order when dumping to hostinfo.dat
      * or to GetTargets()
      */
     int32_t compare = strcmp(current->name, name);
 
     while (current && (compare != 0)) {
         if (compare < 0) {
             // CreateNode will set the parent->cachedChild
-            rv = CreateNode(parent, name, child);
+            rv = CreateNode(parent, name, aPath, child);
             NS_ENSURE_SUCCESS(rv,rv);
 
             (*child)->nextSibling = current;
             (*child)->prevSibling = current->prevSibling;
             current->prevSibling = (*child);
             if (!(*child)->prevSibling) {
                 parent->firstChild = (*child);
             }
@@ -555,17 +607,17 @@ nsSubscribableServer::AddChildNode(Subsc
         *child = current;
 
         // set the cachedChild
         parent->cachedChild = *child;
         return NS_OK;
     }
 
     // CreateNode will set the parent->cachedChild
-    rv = CreateNode(parent, name, child);
+    rv = CreateNode(parent, name, aPath, child);
     NS_ENSURE_SUCCESS(rv,rv);
 
     (*child)->prevSibling = parent->lastChild;
     (*child)->nextSibling = nullptr;
     parent->lastChild->nextSibling = *child;
     parent->lastChild = *child;
 
     rv = NotifyAssert(parent, kNC_Child, *child);
@@ -574,85 +626,81 @@ nsSubscribableServer::AddChildNode(Subsc
 }
 
 nsresult
 nsSubscribableServer::FindAndCreateNode(const nsACString &aPath,
                                         SubscribeTreeNode **aResult)
 {
   nsresult rv = NS_OK;
   NS_ASSERTION(aResult, "no result");
-  if (!aResult) return NS_ERROR_NULL_POINTER;
+  NS_ENSURE_ARG_POINTER(aResult);
 
   if (!mTreeRoot) {
       // the root has no parent, and its name is server uri
-      rv = CreateNode(nullptr, mIncomingServerUri.get(), &mTreeRoot);
+      rv = CreateNode(nullptr, mIncomingServerUri.get(), EmptyCString(), &mTreeRoot);
       NS_ENSURE_SUCCESS(rv,rv);
   }
 
   if (aPath.IsEmpty()) {
       *aResult = mTreeRoot;
       return NS_OK;
   }
 
-  char *token = nullptr;
-  nsCString pathStr(aPath);
-  char *rest = pathStr.BeginWriting();
-
-  // todo do this only once
-  char delimstr[2];
-  delimstr[0] = mDelimiter;
-  delimstr[1] = '\0';
-
   *aResult = nullptr;
 
   SubscribeTreeNode *parent = mTreeRoot;
   SubscribeTreeNode *child = nullptr;
 
-  token = NS_strtok(delimstr, &rest);
-  // special case paths that start with the hierarchy delimiter.
+  uint32_t tokenStart = 0;
+  // Special case paths that start with the hierarchy delimiter.
   // We want to include that delimiter in the first token name.
-  if (token && pathStr[0] == mDelimiter)
-    --token;
-  while (token && *token) {
-    rv = AddChildNode(parent, token, &child);
+  // So start from position 1.
+  int32_t tokenEnd = aPath.FindChar(mDelimiter, tokenStart + 1);
+  while (true) {
+    if (tokenEnd == kNotFound) {
+      if (tokenStart >= aPath.Length())
+        break;
+      tokenEnd = aPath.Length();
+    }
+    nsCString token(Substring(aPath, tokenStart, tokenEnd - tokenStart));
+    rv = AddChildNode(parent, token.BeginReading(), Substring(aPath, 0, tokenEnd), &child);
     if (NS_FAILED(rv))
       return rv;
-    token = NS_strtok(delimstr, &rest);
+    tokenStart = tokenEnd + 1;
+    tokenEnd = aPath.FindChar(mDelimiter, tokenStart);
     parent = child;
   }
 
   // the last child we add is the result
   *aResult = child;
   return rv;
 }
 
 NS_IMETHODIMP
 nsSubscribableServer::HasChildren(const nsACString &aPath, bool *aHasChildren)
 {
-    nsresult rv = NS_OK;
     NS_ASSERTION(aHasChildren, "no hasChildren");
-    if (!aHasChildren) return NS_ERROR_NULL_POINTER;
+    NS_ENSURE_ARG_POINTER(aHasChildren);
 
     *aHasChildren = false;
 
     SubscribeTreeNode *node = nullptr;
-    rv = FindAndCreateNode(aPath, &node);
+    nsresult rv = FindAndCreateNode(aPath, &node);
     NS_ENSURE_SUCCESS(rv,rv);
 
     NS_ASSERTION(node,"didn't find the node");
     if (!node) return NS_ERROR_FAILURE;
 
     *aHasChildren = (node->firstChild != nullptr);
     return NS_OK;
 }
 
 
 NS_IMETHODIMP
-nsSubscribableServer::IsSubscribed(const nsACString &aPath,
-                                   bool *aIsSubscribed)
+nsSubscribableServer::IsSubscribed(const nsACString &aPath, bool *aIsSubscribed)
 {
     NS_ENSURE_ARG_POINTER(aIsSubscribed);
 
     *aIsSubscribed = false;
 
     SubscribeTreeNode *node = nullptr;
     nsresult rv = FindAndCreateNode(aPath, &node);
     NS_ENSURE_SUCCESS(rv,rv);
@@ -797,8 +845,360 @@ nsSubscribableServer::SetSearchValue(con
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsSubscribableServer::GetSupportsSubscribeSearch(bool *retVal)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
+
+NS_IMETHODIMP
+nsSubscribableServer::GetFolderView(nsITreeView **aView)
+{
+  NS_ENSURE_ARG_POINTER(aView);
+  return this->QueryInterface(NS_GET_IID(nsITreeView), (void**)aView);
+}
+
+int32_t
+nsSubscribableServer::GetRow(SubscribeTreeNode *node, bool *open)
+{
+  int32_t parentRow = -1;
+  if (node->parent)
+    parentRow = GetRow(node->parent, open);
+
+  // If the parent wasn't opened, we're not in the row map
+  if (open && *open == false)
+    return -1;
+
+  if (open)
+    *open = node->isOpen;
+
+  for (uint32_t row = parentRow + 1; row < mRowMap.Length(); row++)
+  {
+    if (mRowMap[row] == node)
+      return row;
+  }
+
+  // Apparently, we're not in the map
+  return -1;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::GetSelection(nsITreeSelection **selection)
+{
+  NS_IF_ADDREF(*selection = mSelection);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::SetSelection(nsITreeSelection *selection)
+{
+  mSelection = selection;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::GetRowCount(int32_t *rowCount)
+{
+  *rowCount = mRowMap.Length();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::SetTree(nsITreeBoxObject *aTree)
+{
+  mTree = aTree;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::IsContainer(int32_t aIndex, bool *retval)
+{
+  *retval = !!mRowMap[aIndex]->firstChild;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::IsContainerEmpty(int32_t aIndex, bool *retval)
+{
+  *retval = false;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::IsContainerOpen(int32_t aIndex, bool *retval)
+{
+  *retval = mRowMap[aIndex]->isOpen;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::GetParentIndex(int32_t aIndex, int32_t *retval)
+{
+  SubscribeTreeNode *parent = mRowMap[aIndex]->parent;
+  if (!parent)
+  {
+    *retval = -1;
+    return NS_OK;
+  }
+
+  int32_t index;
+  for (index = aIndex - 1; index >= 0; index--)
+  {
+    if (mRowMap[index] == parent)
+    {
+      *retval = index;
+      return NS_OK;
+    }
+  }
+  *retval = -1;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::HasNextSibling(int32_t aRowIndex, int32_t aAfterIndex,
+                                     bool *retval)
+{
+  // This looks odd, but is correct. Using ->nextSibling gives a bad tree.
+  *retval = !!mRowMap[aRowIndex]->prevSibling;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::GetLevel(int32_t aIndex, int32_t *retval)
+{
+  // When starting with -2, we increase twice and return 0 for a top level node.
+  int32_t level = -2;
+  SubscribeTreeNode *node = mRowMap[aIndex];
+  while (node)
+  {
+    node = node->parent;
+    level++;
+  }
+
+  *retval = level;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::ToggleOpenState(int32_t aIndex)
+{
+  SubscribeTreeNode *node = mRowMap[aIndex];
+  if (node->isOpen)
+  {
+    // Close the node by finding the next sibling
+    node->isOpen = false;
+    int32_t count = node->prevSibling
+      ? (mRowMap.IndexOf(node->prevSibling, aIndex) - aIndex - 1)
+      : (mRowMap.Length() - aIndex - 1);
+    mRowMap.RemoveElementsAt(aIndex + 1, count);
+    if (mTree)
+    {
+      mTree->RowCountChanged(aIndex + 1, -count);
+      mTree->InvalidateRow(aIndex);
+    }
+  }
+  else
+  {
+    // Recursively add the children nodes (i.e., remember open)
+    node->isOpen = true;
+    int32_t total = 0;
+    node = node->lastChild;
+    while (node)
+    {
+      total += AddSubtree(node, aIndex + 1 + total);
+      node = node->prevSibling;
+    }
+    if (mTree)
+    {
+      mTree->RowCountChanged(aIndex + 1, total);
+      mTree->InvalidateRow(aIndex);
+    }
+  }
+  return NS_OK;
+}
+
+int32_t
+nsSubscribableServer::AddSubtree(SubscribeTreeNode *node, int32_t index)
+{
+  mRowMap.InsertElementAt(index, node);
+  int32_t total = 1;
+  if (node->isOpen)
+  {
+    node = node->lastChild;
+    while (node)
+    {
+      total += AddSubtree(node, index + total);
+      node = node->prevSibling;
+    }
+  }
+  return total;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::GetCellText(int32_t aRow, nsITreeColumn *aCol,
+                                  nsAString &retval)
+{
+  nsString colId;
+  aCol->GetId(colId);
+  if (colId.EqualsLiteral("nameColumn")) {
+    nsCString path(mRowMap[aRow]->path);
+    GetLeafName(path, retval);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::GetCellValue(int32_t aRow, nsITreeColumn *aCol,
+                                   nsAString &retval)
+{
+  nsString colId;
+  aCol->GetId(colId);
+  if (colId.EqualsLiteral("nameColumn"))
+    retval = NS_ConvertUTF8toUTF16(mRowMap[aRow]->path);
+  if (colId.EqualsLiteral("subscribedColumn"))
+  {
+    retval = mRowMap[aRow]->isSubscribed ? NS_LITERAL_STRING("true")
+                                         : NS_LITERAL_STRING("false");
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::SetCellText(int32_t aRow, nsITreeColumn *aCol,
+                                  const nsAString &aText)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::SetCellValue(int32_t aRow, nsITreeColumn *aCol,
+                                   const nsAString &aText)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::GetCellProperties(int32_t aRow, nsITreeColumn *aCol,
+                                        nsAString& aProps)
+{
+  SubscribeTreeNode *node = mRowMap[aRow];
+  if (node->isSubscribable)
+    aProps.AssignLiteral("subscribable-true");
+  else
+    aProps.AssignLiteral("subscribable-false");
+
+  nsString colId;
+  aCol->GetId(colId);
+  if (colId.EqualsLiteral("subscribedColumn")) {
+    if (node->isSubscribed)
+      aProps.AppendLiteral(" subscribed-true");
+    else
+      aProps.AppendLiteral(" subscribed-false");
+  } else if (colId.EqualsLiteral("nameColumn")) {
+    aProps.AppendLiteral(" serverType-");
+    aProps.Append(NS_ConvertUTF8toUTF16(mServerType));
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::GetRowProperties(int32_t aRow, nsAString& aProps)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::GetColumnProperties(nsITreeColumn *aCol,
+                                          nsAString& aProps)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::IsEditable(int32_t aRow, nsITreeColumn *aCol,
+                                 bool *retval)
+{
+  *retval = false;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::IsSelectable(int32_t aRow, nsITreeColumn *aCol,
+                                   bool *retval)
+{
+  *retval = false;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::IsSeparator(int32_t aRowIndex, bool *retval)
+{
+  *retval = false;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::IsSorted(bool *retval)
+{
+  *retval = false;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::CanDrop(int32_t aIndex, int32_t aOrientation,
+                              nsIDOMDataTransfer *aData, bool *retval)
+{
+  *retval = false;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::Drop(int32_t aRow, int32_t aOrientation,
+                           nsIDOMDataTransfer *aData)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::GetImageSrc(int32_t aRow, nsITreeColumn *aCol,
+                                  nsAString &retval)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::CycleHeader(nsITreeColumn *aCol)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::SelectionChanged()
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::CycleCell(int32_t aRow, nsITreeColumn *aCol)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::PerformAction(const char16_t *aAction)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::PerformActionOnRow(const char16_t *aAction, int32_t aRow)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSubscribableServer::PerformActionOnCell(const char16_t *aAction,
+                                          int32_t aRow, nsITreeColumn *aCol)
+{
+  return NS_OK;
+}
--- a/mailnews/base/src/nsSubscribableServer.h
+++ b/mailnews/base/src/nsSubscribableServer.h
@@ -3,78 +3,96 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsSubscribableServer_h__
 #define nsSubscribableServer_h__
 
 #include "nsCOMPtr.h"
 #include "nsString.h"
+#include "nsIMsgIncomingServer.h"
+#include "nsITreeBoxObject.h"
+#include "nsITreeSelection.h"
+#include "nsITreeView.h"
 #include "nsISubscribableServer.h"
 #include "nsIRDFService.h"
 #include "nsSubscribeDataSource.h"
 #include "nsIRDFResource.h"
+#include "nsTArray.h"
 
+/**
+ * The basic structure for the tree of the implementation.
+ *
+ * These elements are stored in reverse alphabetical order.
+ */
 typedef struct _subscribeTreeNode {
   char *name;
+  nsCString path;
   bool isSubscribed;
   struct _subscribeTreeNode *prevSibling;
   struct _subscribeTreeNode *nextSibling;
   struct _subscribeTreeNode *firstChild;
   struct _subscribeTreeNode *lastChild;
   struct _subscribeTreeNode *parent;
   struct _subscribeTreeNode *cachedChild;
 #ifdef HAVE_SUBSCRIBE_DESCRIPTION
   char16_t *description;
 #endif
 #ifdef HAVE_SUBSCRIBE_MESSAGES
   uint32_t messages;
 #endif
   bool isSubscribable;
+  bool isOpen;
 } SubscribeTreeNode;
 
-#if defined(DEBUG_sspitzer) || defined(DEBUG_seth)
-#define DEBUG_SUBSCRIBE 1
-#endif
 
-class nsSubscribableServer : public nsISubscribableServer
+class nsSubscribableServer : public nsISubscribableServer,
+                             public nsITreeView
 {
- public:
+public:
   nsSubscribableServer();
 
   nsresult Init();
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSISUBSCRIBABLESERVER
+  NS_DECL_NSITREEVIEW
 
 private:
   virtual ~nsSubscribableServer();
 
   nsresult ConvertNameToUnichar(const char *inStr, char16_t **outStr);
   nsCOMPtr <nsISubscribeListener> mSubscribeListener;
   nsCString mIncomingServerUri;
   nsCOMPtr <nsISubscribeDataSource> mSubscribeDS;
   char mDelimiter;
   bool mShowFullName;
   bool mStopped;
+  nsCString mServerType;
 
   nsCOMPtr <nsIRDFResource>      kNC_Child;
   nsCOMPtr <nsIRDFResource>      kNC_Subscribed;
   nsCOMPtr <nsIRDFLiteral>       kTrueLiteral;
   nsCOMPtr <nsIRDFLiteral>       kFalseLiteral;
 
   nsCOMPtr <nsIRDFService>       mRDFService;
 
-  SubscribeTreeNode *mTreeRoot;
+  SubscribeTreeNode *mTreeRoot;          // root of the folder tree while items are discovered on the server
+  nsTArray<SubscribeTreeNode*> mRowMap;  // array of nodes representing the rows for the tree element
+  nsCOMPtr<nsITreeSelection> mSelection;
+  nsCOMPtr<nsITreeBoxObject> mTree;
   nsresult FreeSubtree(SubscribeTreeNode *node);
-  nsresult CreateNode(SubscribeTreeNode *parent, const char *name, SubscribeTreeNode **result);
-  nsresult AddChildNode(SubscribeTreeNode *parent, const char *name, SubscribeTreeNode **child);
-  nsresult FindAndCreateNode(const nsACString &aPath,
-                             SubscribeTreeNode **aResult);
+  nsresult FreeRows();
+  nsresult CreateNode(SubscribeTreeNode *parent, const char *name, const nsACString &aPath, SubscribeTreeNode **result);
+  nsresult AddChildNode(SubscribeTreeNode *parent, const char *name, const nsACString &aPath, SubscribeTreeNode **child);
+  nsresult FindAndCreateNode(const nsACString &aPath, SubscribeTreeNode **aResult);
   nsresult NotifyAssert(SubscribeTreeNode *subjectNode, nsIRDFResource *property, SubscribeTreeNode *objectNode);
   nsresult NotifyChange(SubscribeTreeNode *subjectNode, nsIRDFResource *property, bool value);
   nsresult Notify(nsIRDFResource *subject, nsIRDFResource *property, nsIRDFNode *object, bool isAssert, bool isChange);
   void BuildURIFromNode(SubscribeTreeNode *node, nsACString &uri);
   nsresult EnsureSubscribeDS();
   nsresult EnsureRDFService();
+
+  int32_t GetRow(SubscribeTreeNode *node, bool *open);
+  int32_t AddSubtree(SubscribeTreeNode *node, int32_t index);
 };
 
 #endif // nsSubscribableServer_h__
--- a/mailnews/imap/src/nsImapIncomingServer.cpp
+++ b/mailnews/imap/src/nsImapIncomingServer.cpp
@@ -2499,22 +2499,16 @@ nsImapIncomingServer::AddTo(const nsACSt
     return mInner->AddTo(name, addAsSubscribed, aSubscribable, changeIfExists);
   }
   return mInner->AddTo(aName, addAsSubscribed, aSubscribable, changeIfExists);
 }
 
 NS_IMETHODIMP
 nsImapIncomingServer::StopPopulating(nsIMsgWindow *aMsgWindow)
 {
-  nsCOMPtr<nsISubscribeListener> listener;
-  (void) GetSubscribeListener(getter_AddRefs(listener));
-
-  if (listener)
-    listener->OnDonePopulating();
-
   nsresult rv = EnsureInner();
   NS_ENSURE_SUCCESS(rv,rv);
   return mInner->StopPopulating(aMsgWindow);
 }
 
 
 NS_IMETHODIMP
 nsImapIncomingServer::SubscribeCleanup()
@@ -2989,16 +2983,24 @@ NS_IMETHODIMP
 nsImapIncomingServer::GetSupportsSubscribeSearch(bool *retVal)
 {
   NS_ENSURE_ARG_POINTER(retVal);
   *retVal = false;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsImapIncomingServer::GetFolderView(nsITreeView **aView)
+{
+  nsresult rv = EnsureInner();
+  NS_ENSURE_SUCCESS(rv,rv);
+  return mInner->GetFolderView(aView);
+}
+
+NS_IMETHODIMP
 nsImapIncomingServer::GetFilterScope(nsMsgSearchScopeValue *filterScope)
 {
   NS_ENSURE_ARG_POINTER(filterScope);
   // If the inbox is enabled for offline use, then use the offline filter
   // scope, else use the online filter scope.
   //
   // XXX We use the same scope for all folders with the same incoming server,
   // yet it is possible to set the offline flag separately for each folder.
--- a/mailnews/news/src/nsNntpIncomingServer.cpp
+++ b/mailnews/news/src/nsNntpIncomingServer.cpp
@@ -943,34 +943,34 @@ nsNntpIncomingServer::StartPopulating(ns
   NS_ENSURE_SUCCESS(rv,rv);
 
   mHostInfoLoaded = false;
   mVersion = INVALID_VERSION;
   mGroupsOnServer.Clear();
   mGetOnlyNew = aGetOnlyNew;
 
   if (!aForceToServer) {
-  rv = LoadHostInfoFile();
+    rv = LoadHostInfoFile();
     if (NS_FAILED(rv)) return rv;
   }
 
   // mHostInfoLoaded can be false if we failed to load anything
   if (aForceToServer || !mHostInfoLoaded || (mVersion != VALID_VERSION)) {
     // set these to true, so when we are done and we call WriteHostInfoFile()
     // we'll write out to hostinfo.dat
-  mHostInfoHasChanged = true;
-  mVersion = VALID_VERSION;
+    mHostInfoHasChanged = true;
+    mVersion = VALID_VERSION;
 
-  mGroupsOnServer.Clear();
-  rv = nntpService->GetListOfGroupsOnServer(this, aMsgWindow, aGetOnlyNew);
-  if (NS_FAILED(rv)) return rv;
+    mGroupsOnServer.Clear();
+    rv = nntpService->GetListOfGroupsOnServer(this, aMsgWindow, aGetOnlyNew);
+    if (NS_FAILED(rv)) return rv;
   }
   else {
-  rv = StopPopulating(aMsgWindow);
-  if (NS_FAILED(rv)) return rv;
+    rv = StopPopulating(aMsgWindow);
+    if (NS_FAILED(rv)) return rv;
   }
 
   return NS_OK;
 }
 
 /**
  * This method is the entry point for |nsNNTPProtocol| class. |aName| is now
  * encoded in the serverside character encoding, but we need to handle
@@ -1112,26 +1112,16 @@ nsNntpIncomingServer::AddTo(const nsACSt
   return rv;
 }
 
 NS_IMETHODIMP
 nsNntpIncomingServer::StopPopulating(nsIMsgWindow *aMsgWindow)
 {
   nsresult rv = NS_OK;
 
-  nsCOMPtr<nsISubscribeListener> listener;
-  rv = GetSubscribeListener(getter_AddRefs(listener));
-  NS_ENSURE_SUCCESS(rv,rv);
-
-  if (!listener)
-    return NS_ERROR_FAILURE;
-
-  rv = listener->OnDonePopulating();
-  NS_ENSURE_SUCCESS(rv,rv);
-
   rv = EnsureInner();
   NS_ENSURE_SUCCESS(rv,rv);
   rv = mInner->StopPopulating(aMsgWindow);
   NS_ENSURE_SUCCESS(rv,rv);
 
   if (!mGetOnlyNew && !mHostInfoLoaded)
   {
     rv = WriteHostInfoFile();
@@ -1692,16 +1682,24 @@ nsNntpIncomingServer::SetSearchValue(con
 NS_IMETHODIMP
 nsNntpIncomingServer::GetSupportsSubscribeSearch(bool *retVal)
 {
   *retVal = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsNntpIncomingServer::GetFolderView(nsITreeView **aView)
+{
+  nsresult rv = EnsureInner();
+  NS_ENSURE_SUCCESS(rv,rv);
+  return mInner->GetFolderView(aView);
+}
+
+NS_IMETHODIMP
 nsNntpIncomingServer::GetRowCount(int32_t *aRowCount)
 {
   *aRowCount = mSubscribeSearchResult.Length();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNntpIncomingServer::GetSelection(nsITreeSelection * *aSelection)