Bug 497488 - Implement verify mode in the subscribe dialog for existing feed urls. r=mkmelin
authoralta88 <alta88@gmail.com>
Tue, 18 Oct 2016 12:02:00 +0200
changeset 20584 141676b80c81e2a3daeeac6ad21da35086ae0240
parent 20583 9fcd32547793baeb1881ed251e796243e1e964e9
child 20585 324728b47409a90d5feeba83566db016b3da826c
push id12435
push usermozilla@jorgk.com
push dateWed, 19 Oct 2016 22:01:49 +0000
treeherdercomm-central@324728b47409 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmkmelin
bugs497488
Bug 497488 - Implement verify mode in the subscribe dialog for existing feed urls. r=mkmelin
mail/locales/en-US/chrome/messenger-newsblog/newsblog.properties
mailnews/extensions/newsblog/content/Feed.js
mailnews/extensions/newsblog/content/FeedUtils.jsm
mailnews/extensions/newsblog/content/feed-subscriptions.js
mailnews/extensions/newsblog/content/feed-subscriptions.xul
--- a/mail/locales/en-US/chrome/messenger-newsblog/newsblog.properties
+++ b/mail/locales/en-US/chrome/messenger-newsblog/newsblog.properties
@@ -8,16 +8,17 @@ subscribe-cancelSubscriptionTitle=Subscribing to a Feed…
 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-feedCopied=Feed subscription copied.
 subscribe-feedRemoved=Feed unsubscribed.
 subscribe-feedNotValid=The Feed URL is not a valid feed.
+subscribe-feedVerified=The Feed URL has been verified.
 subscribe-networkError=The Feed URL could not be found. Please check the name and try again.
 subscribe-noAuthError=The Feed URL is not authorized.
 subscribe-loading=Loading, please wait…
 
 subscribe-OPMLImportTitle=Select OPML file to import
 ## LOCALIZATION NOTE(subscribe-OPMLExportTitleList):
 ## %S is the name of the feed account folder name.
 subscribe-OPMLExportTitleList=Export %S as an OPML file - Feeds list
--- a/mailnews/extensions/newsblog/content/Feed.js
+++ b/mailnews/extensions/newsblog/content/Feed.js
@@ -15,16 +15,17 @@ var FeedCache =
     this.mFeeds[this.normalizeHost(aFeed.url)] = aFeed;
   },
 
   getFeed: function (aUrl)
   {
     let index = this.normalizeHost(aUrl);
     if (index in this.mFeeds)
       return this.mFeeds[index];
+
     return null;
   },
 
   removeFeed: function (aUrl)
   {
     let index = this.normalizeHost(aUrl);
     if (index in this.mFeeds)
       delete this.mFeeds[index];
@@ -106,23 +107,20 @@ Feed.prototype =
     // Whether or not to parse items when downloading and parsing the feed.
     // Defaults to true, but setting to false is useful for obtaining
     // just the title of the feed when the user subscribes to it.
     this.parseItems = aParseItems == null ? true : aParseItems ? true : false;
 
     // Before we do anything, make sure the url is an http url.  This is just
     // a sanity check so we don't try opening mailto urls, imap urls, etc. that
     // the user may have tried to subscribe to as an rss feed.
-    let uri = Cc["@mozilla.org/network/standard-url;1"].
-              createInstance(Ci.nsIURI);
-    uri.spec = this.url;
-    if (!FeedUtils.isValidScheme(uri))
+    if (!FeedUtils.isValidScheme(this.url))
     {
        // Simulate an invalid feed error.
-      FeedUtils.log.info("Feed.download: invalid protocol for - " + uri.spec);
+      FeedUtils.log.info("Feed.download: invalid protocol for - " + this.url);
       this.onParseError(this);
       return;
     }
 
     // Before we try to download the feed, make sure we aren't already
     // processing the feed by looking up the url in our feed cache.
     if (FeedCache.getFeed(this.url))
     {
@@ -169,17 +167,17 @@ Feed.prototype =
     this.request.ontimeout = this.onDownloadError;
     FeedCache.putFeed(this);
     this.request.send(null);
   },
 
   onDownloaded: function(aEvent)
   {
     let request = aEvent.target;
-    let isHttp = /^http(s?)/.test(request.channel.originalURI.scheme);
+    let isHttp = request.channel.originalURI.scheme.startsWith("http");
     let url = request.channel.originalURI.spec;
     if (isHttp && (request.status < 200 || request.status >= 300))
     {
       Feed.prototype.onDownloadError(aEvent);
       return;
     }
 
     FeedUtils.log.debug("Feed.onDownloaded: got a download - " + url);
@@ -312,16 +310,17 @@ Feed.prototype =
   get lastModified()
   {
     let ds = FeedUtils.getSubscriptionsDS(this.server);
     let lastModified = ds.GetTarget(this.resource,
                                     FeedUtils.DC_LASTMODIFIED,
                                     true);
     if (lastModified)
       lastModified = lastModified.QueryInterface(Ci.nsIRDFLiteral).Value;
+
     return lastModified;
   },
 
   set lastModified(aLastModified)
   {
     let ds = FeedUtils.getSubscriptionsDS(this.server);
     aLastModified = FeedUtils.rdf.GetLiteral(aLastModified);
     let old_lastmodified = ds.GetTarget(this.resource,
@@ -581,25 +580,30 @@ Feed.prototype =
       this.cleanupParsingState(this, FeedUtils.kNewsBlogSuccess);
     }
   },
 
   cleanupParsingState: function(aFeed, aCode)
   {
     // Now that we are done parsing the feed, remove the feed from the cache.
     FeedCache.removeFeed(aFeed.url);
-    aFeed.removeInvalidItems(false);
 
-    if (aCode == FeedUtils.kNewsBlogSuccess && aFeed.mLastModified)
-      aFeed.lastModified = aFeed.mLastModified;
+    if (aFeed.parseItems)
+    {
+      // Do this only if we're in parse/store mode.
+      aFeed.removeInvalidItems(false);
 
-    // Flush any feed item changes to disk.
-    let ds = FeedUtils.getItemsDS(aFeed.server);
-    ds.Flush();
-    FeedUtils.log.debug("Feed.cleanupParsingState: items stored - " + this.itemsStored);
+      if (aCode == FeedUtils.kNewsBlogSuccess && aFeed.mLastModified)
+        aFeed.lastModified = aFeed.mLastModified;
+
+      // Flush any feed item changes to disk.
+      let ds = FeedUtils.getItemsDS(aFeed.server);
+      ds.Flush();
+      FeedUtils.log.debug("Feed.cleanupParsingState: items stored - " + this.itemsStored);
+    }
 
     // Force the xml http request to go away.  This helps reduce some nasty
     // assertions on shut down.
     this.request = null;
     this.itemsToStore = "";
     this.itemsToStoreIndex = 0;
     this.itemsStored = 0;
     this.storeItemsTimer = null;
--- a/mailnews/extensions/newsblog/content/FeedUtils.jsm
+++ b/mailnews/extensions/newsblog/content/FeedUtils.jsm
@@ -235,18 +235,18 @@ var FeedUtils = {
       // Add the base folder; it does not get returned by ListDescendants. Do not
       // add the account folder as it doesn't have the feedUrl property or even
       // a msgDatabase necessarily.
       allFolders.appendElement(aFolder, false);
     }
 
     aFolder.ListDescendants(allFolders);
 
+    let folder;
     function* feeder() {
-      let folder;
       let numFolders = allFolders.length;
       for (let i = 0; i < numFolders; i++) {
         folder = allFolders.queryElementAt(i, Ci.nsIMsgFolder);
         FeedUtils.log.debug("downloadFeed: START x/# foldername:uri - " +
                             (i+1) + "/" + numFolders + " " +
                             folder.name + ":" + folder.URI);
 
         // Ensure folder's msgDatabase is openable for new message processing.
--- a/mailnews/extensions/newsblog/content/feed-subscriptions.js
+++ b/mailnews/extensions/newsblog/content/feed-subscriptions.js
@@ -20,16 +20,17 @@ var FeedSubscriptions = {
   mFeedContainers: [],
   mRSSServer     : null,
   mActionMode    : null,
   kSubscribeMode : 1,
   kUpdateMode    : 2,
   kMoveMode      : 3,
   kCopyMode      : 4,
   kImportingOPML : 5,
+  kVerifyUrlMode : 6,
 
   get FOLDER_ACTIONS()
   {
     return Ci.nsIMsgFolderNotificationService.folderAdded |
            Ci.nsIMsgFolderNotificationService.folderDeleted |
            Ci.nsIMsgFolderNotificationService.folderRenamed |
            Ci.nsIMsgFolderNotificationService.folderMoveCopyCompleted;
   },
@@ -1011,19 +1012,20 @@ var FeedSubscriptions = {
     }
     else
     {
       target.setAttribute("showfilepath", true);
       target.value = target.getAttribute("filepath");
     }
   },
 
-  setNewFolder: function(aFolder)
+  setNewFolder: function(aEvent)
   {
-    this.setFolderPicker(aFolder, true);
+    aEvent.stopPropagation();
+    this.setFolderPicker(aEvent.target._folder, true);
     this.editFeed();
   },
 
   setSummary: function(aChecked)
   {
     let item = this.mView.currentItem;
     if (!item || !item.folder)
       // Not a folder.
@@ -1280,16 +1282,23 @@ var FeedSubscriptions = {
 
     if (!feedLocation)
     {
       message = locationValue.getAttribute("placeholder");
       this.updateStatusItem("statusText", message);
       return false;
     }
 
+    if (!FeedUtils.isValidScheme(feedLocation))
+    {
+      message = FeedUtils.strings.GetStringFromName("subscribe-feedNotValid");
+      this.updateStatusItem("statusText", message);
+      return false;
+    }
+
     let addFolder;
     if (aFolder)
     {
       // For Update or if passed a folder.
       if (aFolder instanceof Ci.nsIMsgFolder)
         addFolder = aFolder;
     }
     else
@@ -1397,18 +1406,17 @@ var FeedSubscriptions = {
     }
 
     if (!itemToEdit || itemToEdit.container || !itemToEdit.parentFolder)
       return;
 
     let resource = FeedUtils.rdf.GetResource(itemToEdit.url);
     let currentFolderServer = itemToEdit.parentFolder.server;
     let ds = FeedUtils.getSubscriptionsDS(currentFolderServer);
-    let currentFolder = ds.GetTarget(resource, FeedUtils.FZ_DESTFOLDER, true);
-    let currentFolderURI = currentFolder.QueryInterface(Ci.nsIRDFResource).ValueUTF8;
+    let currentFolderURI = itemToEdit.parentFolder.URI;
     let feed = new Feed(resource, currentFolderServer);
     feed.folder = itemToEdit.parentFolder;
 
     let editNameValue = document.getElementById("nameValue").value;
     let editFeedLocation = document.getElementById("locationValue").value.trim();
     let selectFolder = document.getElementById("selectFolder");
     let editQuickMode = document.getElementById("quickMode").checked;
     let editAutotagPrefix = document.getElementById("autotagPrefix").value;
@@ -1416,17 +1424,48 @@ var FeedSubscriptions = {
     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, null, this.kUpdateMode)
       return;
     }
 
+    // Did the user change the folder URI for storing the feed?
+    let editFolderURI = selectFolder.getAttribute("uri");
+    if (currentFolderURI != editFolderURI)
+    {
+      // Make sure the new folderpicked folder is visible.
+      this.selectFolder(selectFolder._folder);
+      // Now go back to the feed item.
+      this.selectFeed(feed, null);
+      // 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++)
+      {
+        let item = this.mView.getItemAtIndex(index);
+        if (item && item.container && item.url == editFolderURI)
+        {
+          newParentIndex = index;
+          break;
+        }
+      }
+
+      if (newParentIndex != this.mView.kRowIndexUndefined)
+        this.moveCopyFeed(seln.currentIndex, newParentIndex, "move");
+
+      return;
+    }
+
     let updated = false;
+    let message = "";
+    // Disable the button until the update completes and we process the async
+    // verify response.
+    document.getElementById("editFeed").setAttribute("disabled", true);
+
     // Check to see if the title value changed, no blank title allowed.
     if (feed.title != editNameValue)
     {
       if (!editNameValue)
       {
         document.getElementById("nameValue").value = feed.title;
       }
       else
@@ -1442,54 +1481,42 @@ var FeedSubscriptions = {
     if (feed.quickMode != editQuickMode)
     {
       feed.quickMode = editQuickMode;
       itemToEdit.quickMode = editQuickMode;
       updated = true;
     }
 
     // Check to see if the categoryPrefs custom prefix string value changed.
-    if (itemToEdit.options.category.prefix != editAutotagPrefix)
+    if (itemToEdit.options.category.prefix != editAutotagPrefix &&
+        itemToEdit.options.category.prefix != null &&
+        editAutotagPrefix != "")
     {
       itemToEdit.options.category.prefix = editAutotagPrefix;
       feed.options = itemToEdit.options;
       updated = true;
     }
 
-    // Did the user change the folder URI for storing the feed?
-    let editFolderURI = selectFolder.getAttribute("uri");
-    if (currentFolderURI != editFolderURI)
-    {
-      // Make sure the new folderpicked folder is visible.
-      this.selectFolder(selectFolder._folder);
-      // Now go back to the feed item.
-      this.selectFeed(feed, null);
-      // 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++)
-      {
-        let item = this.mView.getItemAtIndex(index);
-        if (item && item.container && item.url == editFolderURI)
-        {
-          newParentIndex = index;
-          break;
-        }
-      }
-
-      if (newParentIndex != this.mView.kRowIndexUndefined)
-        this.moveCopyFeed(seln.currentIndex, newParentIndex, "move");
+    let verifyDelay = 0;
+    if (updated) {
+      ds.Flush();
+      message = FeedUtils.strings.GetStringFromName("subscribe-feedUpdated");
+      this.updateStatusItem("statusText", message);
+      verifyDelay = 1500;
     }
 
-    if (!updated)
-      return;
-
-    ds.Flush();
-
-    let message = FeedUtils.strings.GetStringFromName("subscribe-feedUpdated");
-    this.updateStatusItem("statusText", message);
+    // Now we want to verify if the stored feed url still works. If it
+    // doesn't, show the error. Delay a bit to leave Updated message visible.
+    message = FeedUtils.strings.GetStringFromName("subscribe-validating-feed");
+    this.mActionMode = this.kVerifyUrlMode;
+    setTimeout(() => {
+      this.updateStatusItem("statusText", message);
+      this.updateStatusItem("progressMeter", "?");
+      feed.download(false, this.mFeedDownloadCallback);
+    }, verifyDelay);
   },
 
 /**
  * Moves or copies a feed to another folder or account.
  * 
  * @param  int aOldFeedIndex    - index in tree of target feed item.
  * @param  int aNewParentIndex  - index in tree of target parent folder item.
  * @param  string aMoveCopy     - either "move" or "copy".
@@ -1647,20 +1674,32 @@ var FeedSubscriptions = {
     downloaded: function(feed, aErrorCode)
     {
       // Offline check is done in the context of 3pane, return to the subscribe
       // window once the modal prompt is dispatched.
       window.focus();
       // Feed is null if our attempt to parse the feed failed.
       let message = "";
       let win = FeedSubscriptions;
-      if (aErrorCode == FeedUtils.kNewsBlogSuccess)
+      if (aErrorCode == FeedUtils.kNewsBlogSuccess ||
+          aErrorCode == FeedUtils.kNewsBlogNoNewItems)
       {
         win.updateStatusItem("progressMeter", 100);
 
+        if (win.mActionMode == win.kVerifyUrlMode) {
+          // Just checking for errors, if none bye. The (non error) code
+          // kNewsBlogNoNewItems can only happen in verify mode.
+          win.mActionMode = null;
+          win.clearStatusInfo();
+          message = FeedUtils.strings.GetStringFromName("subscribe-feedVerified");
+          win.updateStatusItem("statusText", message);
+          document.getElementById("editFeed").removeAttribute("disabled");
+          return;
+        }
+
         // Add the feed to the databases.
         FeedUtils.addFeed(feed);
 
         // 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);
@@ -1726,17 +1765,19 @@ var FeedSubscriptions = {
                         "subscribe-feedCopied");
 
           win.selectFeed(feed, parentIndex);
         }
       }
       else
       {
         // Non success.  Remove intermediate traces from the feeds database.
-        if (feed && feed.url && feed.server)
+        // But only if we're not in verify mode.
+        if (win.mActionMode != win.kVerifyUrlMode &&
+            feed && feed.url && feed.server)
           FeedUtils.deleteFeed(FeedUtils.rdf.GetResource(feed.url),
                                feed.server,
                                feed.server.rootFolder);
 
         if (aErrorCode == FeedUtils.kNewsBlogInvalidFeed)
           message = FeedUtils.strings.GetStringFromName(
                       "subscribe-feedNotValid");
         if (aErrorCode == FeedUtils.kNewsBlogRequestFailure)
@@ -1749,19 +1790,23 @@ var FeedSubscriptions = {
           let host = Services.io.newURI(feed.url, null, null).host;
           message = FeedUtils.strings.formatStringFromName(
                       "newsblog-badCertError", [host], 1);
         }
         if (aErrorCode == FeedUtils.kNewsBlogNoAuthError)
           message = FeedUtils.strings.GetStringFromName(
                       "subscribe-noAuthError");
 
-        if (win.mActionMode != win.kUpdateMode)
+        if (win.mActionMode != win.kUpdateMode &&
+            win.mActionMode != win.kVerifyUrlMode)
           // Re-enable the add button if subscribe failed.
           document.getElementById("addFeed").removeAttribute("disabled");
+        if (win.mActionMode == win.kVerifyUrlMode)
+          // Re-enable the update button if verify failed.
+          document.getElementById("editFeed").removeAttribute("disabled");
       }
 
       win.mActionMode = null;
       win.clearStatusInfo();
       let code = feed.url.startsWith("http") ? aErrorCode : null;
       win.updateStatusItem("statusText", message, code);
     },
 
--- a/mailnews/extensions/newsblog/content/feed-subscriptions.xul
+++ b/mailnews/extensions/newsblog/content/feed-subscriptions.xul
@@ -129,17 +129,17 @@
                           class="folderMenuItem"
                           hidden="true">
                   <menupopup id="selectFolderPopup"
                              class="menulist-menupopup"
                              type="folder"
                              mode="feeds"
                              showFileHereLabel="true"
                              showAccountsFileHere="true"
-                             oncommand="FeedSubscriptions.setNewFolder(event.target._folder)"/>
+                             oncommand="FeedSubscriptions.setNewFolder(event)"/>
                 </menulist>
                 <textbox id="selectFolderValue"
                          flex="1"
                          readonly="true"
                          onkeypress="FeedSubscriptions.onClickSelectFolderValue(event)"
                          onclick="FeedSubscriptions.onClickSelectFolderValue(event)"/>
               </hbox>
             </row>