Bug 1594887 - Remove xpidl [array] use in nsIJunkMailPlugin. r=mkmelin DONTBUILD
authorBen Campbell <benc@thunderbird.net>
Tue, 26 Nov 2019 17:11:54 +1300
changeset 36748 30ad6ed6ebb611238dd64a64394e35289e5d19f2
parent 36747 86c11d411a013b767e62bd39c3c483c87ea548da
child 36749 e370eb044bf92725cc890db47d56b771ba1c09ce
push id2534
push userclokep@gmail.com
push dateMon, 02 Dec 2019 19:52:51 +0000
treeherdercomm-beta@055c50840778 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmkmelin
bugs1594887
Bug 1594887 - Remove xpidl [array] use in nsIJunkMailPlugin. r=mkmelin DONTBUILD
mailnews/base/search/public/nsIMsgFilterPlugin.idl
mailnews/base/test/unit/test_postPluginFilters.js
mailnews/base/util/nsMsgDBFolder.cpp
mailnews/extensions/bayesian-spam-filter/src/nsBayesianFilter.cpp
mailnews/extensions/bayesian-spam-filter/src/nsBayesianFilter.h
mailnews/extensions/bayesian-spam-filter/test/unit/test_customTokenization.js
mailnews/extensions/bayesian-spam-filter/test/unit/test_junkAsTraits.js
mailnews/extensions/bayesian-spam-filter/test/unit/test_msgCorpus.js
mailnews/extensions/bayesian-spam-filter/test/unit/test_traitAliases.js
mailnews/extensions/bayesian-spam-filter/test/unit/test_traits.js
--- a/mailnews/base/search/public/nsIMsgFilterPlugin.idl
+++ b/mailnews/base/search/public/nsIMsgFilterPlugin.idl
@@ -133,71 +133,64 @@ interface nsIJunkMailPlugin : nsIMsgFilt
 
     /**
      * Given a message URI, determine what its current classification is
      * according to the current training set.
      */
     void classifyMessage(in string aMsgURI, in nsIMsgWindow aMsgWindow,
                          in nsIJunkMailClassificationListener aListener);
 
-    void classifyMessages(in unsigned long aCount,
-                          [array, size_is(aCount)] in string aMsgURIs,
+    void classifyMessages(in Array<ACString> aMsgURIs,
                           in nsIMsgWindow aMsgWindow,
                           in nsIJunkMailClassificationListener aListener);
 
     /**
      * Given a message URI, evaluate its relative match to a list of
      * traits according to the current training set.
      *
      * @param aMsgURI          URI of the message to be evaluated
-     * @param aTraitCount      length of aProTraits, aAntiTraits arrays
      * @param aProTraits       array of trait ids for trained messages that
      *                         match the tested trait (for example,
      *                         JUNK_TRAIT if testing for junk)
      * @param aAntiTraits      array of trait ids for trained messages that
      *                         do not match the tested trait (for example,
      *                         GOOD_TRAIT if testing for junk)
      * @param aTraitListener   trait-oriented callback listener (may be null)
      * @param aMsgWindow       current message window (may be null)
      * @param aJunkListener    junk-oriented callback listener (may be null)
      */
 
     void classifyTraitsInMessage(
-           in string aMsgURI,
-           in unsigned long aTraitCount,
-           [array, size_is(aTraitCount)] in unsigned long aProTraits,
-           [array, size_is(aTraitCount)] in unsigned long aAntiTraits,
+           in ACString aMsgURI,
+           in Array<unsigned long> aProTraits,
+           in Array<unsigned long> aAntiTraits,
            in nsIMsgTraitClassificationListener aTraitListener,
            [optional] in nsIMsgWindow aMsgWindow,
            [optional] in nsIJunkMailClassificationListener aJunkListener);
 
     /**
      * Given an array of message URIs, evaluate their relative match to a
      * list of traits according to the current training set.
      *
-     * @param aCount           Number of messages to evaluate
      * @param aMsgURIs         array of URIs of the messages to be evaluated
-     * @param aTraitCount      length of aProTraits, aAntiTraits arrays
      * @param aProTraits       array of trait ids for trained messages that
      *                         match the tested trait (for example,
      *                         JUNK_TRAIT if testing for junk)
      * @param aAntiTraits      array of trait ids for trained messages that
      *                         do not match the tested trait (for example,
      *                         GOOD_TRAIT if testing for junk)
      * @param aTraitListener   trait-oriented callback listener (may be null)
      * @param aMsgWindow       current message window (may be null)
      * @param aJunkListener    junk-oriented callback listener (may be null)
      */
 
     void classifyTraitsInMessages(
-           in unsigned long aCount,
-           [array, size_is(aCount)] in string aMsgURIs,
-           in unsigned long aTraitCount,
-           [array, size_is(aTraitCount)] in unsigned long aProTraits,
-           [array, size_is(aTraitCount)] in unsigned long aAntiTraits,
+           in Array<ACString> aMsgURIs,
+           in Array<unsigned long> aProTraits,
+           in Array<unsigned long> aAntiTraits,
            in nsIMsgTraitClassificationListener aTraitListener,
            [optional] in nsIMsgWindow aMsgWindow,
            [optional] in nsIJunkMailClassificationListener aJunkListener);
 
     /**
      * Called when a user forces the classification of a message. Should
      * cause the training set to be updated appropriately.
      *
@@ -213,32 +206,28 @@ interface nsIJunkMailPlugin : nsIMsgFilt
         in nsIMsgWindow aMsgWindow,
         in nsIJunkMailClassificationListener aListener);
 
     /**
      * Called when a user forces a change in the classification of a message.
      * Should cause the training set to be updated appropriately.
      *
      * @param aMsgURI           URI of the message to be classified
-     * @param aOldCount         length of aOldTraits array
      * @param aOldTraits        array of trait IDs of the old
      *                          message classification(s), if any
-     * @param aNewCount         length of aNewTraits array
      * @param aNewTraits        array of trait IDs of the new
      *                          message classification(s), if any
      * @param aTraitListener    trait-oriented listener (may be null)
      * @param aMsgWindow        current message window (may be null)
      * @param aJunkListener     junk-oriented listener (may be null)
      */
     void setMsgTraitClassification(
         in string aMsgURI,
-        in unsigned long aOldCount,
-        [array, size_is(aOldCount)] in unsigned long  aOldTraits,
-        in unsigned long aNewCount,
-        [array, size_is(aNewCount)] in unsigned long  aNewTraits,
+        in Array<unsigned long> aOldTraits,
+        in Array<unsigned long> aNewTraits,
         [optional] in nsIMsgTraitClassificationListener aTraitListener,
         [optional] in nsIMsgWindow aMsgWindow,
         [optional] in nsIJunkMailClassificationListener aJunkListener);
 
     readonly attribute boolean userHasClassified;
 
     /** Removes the training file and clears out any in memory training tokens.
         User must retrain after doing this.
@@ -280,51 +269,47 @@ interface nsIMsgCorpus : nsISupports
    * Clear the corpus data for a trait id.
    *
    * @param aTrait       trait id
    */
    void clearTrait(in unsigned long aTrait);
 
   /**
    * Update corpus data from a file.
+   * Uses the parallel arrays aFromTraits and aToTraits. These arrays allow
+   * conversion of the trait id stored in the file (which may be originated
+   * externally) to the trait id used in the local corpus (which is defined
+   * locally using nsIMsgTraitService, and mapped by that interface to a
+   * globally unique trait id string).
    *
    * @param aFile       the file with the data, in the format:
    *
    *                    Format of the trait file for version 1:
    *                    [0xFCA93601]  (the 01 is the version)
    *                    for each trait to write:
    *                    [id of trait to write] (0 means end of list)
    *                    [number of messages per trait]
    *                    for each token with non-zero count
    *                    [count]
    *                    [length of word]word
    *
    * @param aIsAdd      should the data be added, or removed? True if
    *                    adding, false if removing.
    *
-   * @param aRemapCount number of items in the parallel arrays aFromTraits,
-   *                    aToTraits. These arrays allow conversion of the
-   *                    trait id stored in the file (which may be originated
-   *                    externally) to the trait id used in the local corpus
-   *                    (which is defined locally using nsIMsgTraitService, and
-   *                    mapped by that interface to a globally unique trait
-   *                    id string).
-   *
    * @param aFromTraits array of trait ids used in aFile. If aFile contains
    *                    trait ids that are not in this array, they are not
    *                    remapped, but assumed to be local trait ids.
    *
    * @param aToTraits   array of trait ids, corresponding to elements of
    *                    aFromTraits, that represent the local trait ids to
    *                    be used in storing data from aFile into the local corpus.
    */
   void updateData(in nsIFile aFile, in boolean aIsAdd,
-                  [optional] in unsigned long aRemapCount,
-                  [optional, array, size_is(aRemapCount)] in unsigned long aFromTraits,
-                  [optional, array, size_is(aRemapCount)] in unsigned long aToTraits);
+                  [optional] in Array<unsigned long> aFromTraits,
+                  [optional] in Array<unsigned long> aToTraits);
 
   /**
    * Get the corpus count for a token as a string.
    *
    * @param aWord    string of characters representing the token
    * @param aTrait   trait id
    *
    * @return         count of that token in the corpus
--- a/mailnews/base/test/unit/test_postPluginFilters.js
+++ b/mailnews/base/test/unit/test_postPluginFilters.js
@@ -190,23 +190,21 @@ function startCommand() {
   gTest = gTests.shift();
   switch (gTest.command) {
     case kTrain:
       // train message
       var proArray = [];
       proArray.push(gTest.traitId);
 
       MailServices.junk.setMsgTraitClassification(
-        getSpec(gTest.fileName), // in string aMsgURI
-        0,
-        null, // in nsIArray aOldTraits
-        proArray.length,
-        proArray, // in nsIArray aNewTraits
-        classifyListener
-      ); // [optional] in nsIMsgTraitClassificationListener aTraitListener
+        getSpec(gTest.fileName), // aMsgURI
+        [], // aOldTraits
+        proArray, // aNewTraits
+        classifyListener // aTraitListener
+      );
       // null,      // [optional] in nsIMsgWindow aMsgWindow
       // null,      // [optional] in nsIJunkMailClassificationListener aJunkListener
       break;
 
     case kClass:
       // classify message
       gPOP3Pump.files = [gTest.fileName];
       gPOP3Pump.onDone = function() {
--- a/mailnews/base/util/nsMsgDBFolder.cpp
+++ b/mailnews/base/util/nsMsgDBFolder.cpp
@@ -2131,19 +2131,19 @@ nsresult nsMsgDBFolder::SpamFilterClassi
 
   nsTArray<uint32_t> proIndices;
   rv = traitService->GetEnabledProIndices(proIndices);
   NS_ENSURE_SUCCESS(rv, rv);
   nsTArray<uint32_t> antiIndices;
   rv = traitService->GetEnabledAntiIndices(antiIndices);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = aJunkMailPlugin->ClassifyTraitsInMessage(
-      aURI, proIndices.Length(), proIndices.Elements(), antiIndices.Elements(),
-      this, aMsgWindow, this);
+  rv = aJunkMailPlugin->ClassifyTraitsInMessage(nsDependentCString(aURI),
+                                                proIndices, antiIndices, this,
+                                                aMsgWindow, this);
   return rv;
 }
 
 nsresult nsMsgDBFolder::SpamFilterClassifyMessages(
     const char **aURIArray, uint32_t aURICount, nsIMsgWindow *aMsgWindow,
     nsIJunkMailPlugin *aJunkMailPlugin) {
   MOZ_LOG(FILTERLOGMODULE, LogLevel::Info,
           ("Running Spam classification on %" PRIu32 " messages", aURICount));
@@ -2155,19 +2155,21 @@ nsresult nsMsgDBFolder::SpamFilterClassi
 
   nsTArray<uint32_t> proIndices;
   rv = traitService->GetEnabledProIndices(proIndices);
   NS_ENSURE_SUCCESS(rv, rv);
   nsTArray<uint32_t> antiIndices;
   rv = traitService->GetEnabledAntiIndices(antiIndices);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  nsTArray<nsCString> tmpURIs(aURICount);
+  tmpURIs.AppendElements(aURIArray, aURICount);
+
   rv = aJunkMailPlugin->ClassifyTraitsInMessages(
-      aURICount, aURIArray, proIndices.Length(), proIndices.Elements(),
-      antiIndices.Elements(), this, aMsgWindow, this);
+      tmpURIs, proIndices, antiIndices, this, aMsgWindow, this);
   return rv;
 }
 
 NS_IMETHODIMP
 nsMsgDBFolder::OnMessageClassified(const char *aMsgURI,
                                    nsMsgJunkStatus aClassification,
                                    uint32_t aJunkPercent) {
   if (!aMsgURI)  // This signifies end of batch.
--- a/mailnews/extensions/bayesian-spam-filter/src/nsBayesianFilter.cpp
+++ b/mailnews/extensions/bayesian-spam-filter/src/nsBayesianFilter.cpp
@@ -1186,75 +1186,66 @@ nsBayesianFilter::~nsBayesianFilter() {
 // memory.
 class MessageClassifier : public TokenAnalyzer {
  public:
   // full classifier with arbitrary traits
   MessageClassifier(nsBayesianFilter* aFilter,
                     nsIJunkMailClassificationListener* aJunkListener,
                     nsIMsgTraitClassificationListener* aTraitListener,
                     nsIMsgTraitDetailListener* aDetailListener,
-                    nsTArray<uint32_t>& aProTraits,
-                    nsTArray<uint32_t>& aAntiTraits, nsIMsgWindow* aMsgWindow,
-                    uint32_t aNumMessagesToClassify, const char** aMessageURIs)
+                    const nsTArray<uint32_t>& aProTraits,
+                    const nsTArray<uint32_t>& aAntiTraits,
+                    nsIMsgWindow* aMsgWindow,
+                    const nsTArray<nsCString>& aMessageURIs)
       : mFilter(aFilter),
         mJunkMailPlugin(aFilter),
         mJunkListener(aJunkListener),
         mTraitListener(aTraitListener),
         mDetailListener(aDetailListener),
         mProTraits(aProTraits),
         mAntiTraits(aAntiTraits),
-        mMsgWindow(aMsgWindow) {
-    mCurMessageToClassify = 0;
-    mNumMessagesToClassify = aNumMessagesToClassify;
-    mMessageURIs = (char**)moz_xmalloc(sizeof(char*) * aNumMessagesToClassify);
-    for (uint32_t i = 0; i < aNumMessagesToClassify; i++)
-      mMessageURIs[i] = PL_strdup(aMessageURIs[i]);
+        mMsgWindow(aMsgWindow),
+        mMessageURIs(aMessageURIs),
+        mCurMessageToClassify(0) {
+    MOZ_ASSERT(aProTraits.Length() == aAntiTraits.Length());
   }
 
   // junk-only classifier
   MessageClassifier(nsBayesianFilter* aFilter,
                     nsIJunkMailClassificationListener* aJunkListener,
-                    nsIMsgWindow* aMsgWindow, uint32_t aNumMessagesToClassify,
-                    const char** aMessageURIs)
+                    nsIMsgWindow* aMsgWindow,
+                    const nsTArray<nsCString>& aMessageURIs)
       : mFilter(aFilter),
         mJunkMailPlugin(aFilter),
         mJunkListener(aJunkListener),
         mTraitListener(nullptr),
         mDetailListener(nullptr),
-        mMsgWindow(aMsgWindow) {
-    mCurMessageToClassify = 0;
-    mNumMessagesToClassify = aNumMessagesToClassify;
-    mMessageURIs = (char**)moz_xmalloc(sizeof(char*) * aNumMessagesToClassify);
-    for (uint32_t i = 0; i < aNumMessagesToClassify; i++)
-      mMessageURIs[i] = PL_strdup(aMessageURIs[i]);
+        mMsgWindow(aMsgWindow),
+        mMessageURIs(aMessageURIs),
+        mCurMessageToClassify(0) {
     mProTraits.AppendElement(kJunkTrait);
     mAntiTraits.AppendElement(kGoodTrait);
   }
 
-  virtual ~MessageClassifier() {
-    if (mMessageURIs) {
-      NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mNumMessagesToClassify,
-                                            mMessageURIs);
-    }
-  }
+  virtual ~MessageClassifier() {}
   virtual void analyzeTokens(Tokenizer& tokenizer) {
     mFilter->classifyMessage(tokenizer, mTokenSource.get(), mProTraits,
                              mAntiTraits, mJunkListener, mTraitListener,
                              mDetailListener);
     tokenizer.clearTokens();
     classifyNextMessage();
   }
 
   virtual void classifyNextMessage() {
-    if (++mCurMessageToClassify < mNumMessagesToClassify &&
-        mMessageURIs[mCurMessageToClassify]) {
+    if (++mCurMessageToClassify < mMessageURIs.Length()) {
       MOZ_LOG(BayesianFilterLogModule, LogLevel::Warning,
-              ("classifyNextMessage(%s)", mMessageURIs[mCurMessageToClassify]));
-      mFilter->tokenizeMessage(mMessageURIs[mCurMessageToClassify], mMsgWindow,
-                               this);
+              ("classifyNextMessage(%s)",
+               mMessageURIs[mCurMessageToClassify].get()));
+      mFilter->tokenizeMessage(mMessageURIs[mCurMessageToClassify].get(),
+                               mMsgWindow, this);
     } else {
       // call all listeners with null parameters to signify end of batch
       if (mJunkListener)
         mJunkListener->OnMessageClassified(nullptr,
                                            nsIJunkMailPlugin::UNCLASSIFIED, 0);
       if (mTraitListener) {
         nsTArray<uint32_t> nullTraits;
         nsTArray<uint32_t> nullPercents;
@@ -1271,19 +1262,18 @@ class MessageClassifier : public TokenAn
   nsBayesianFilter* mFilter;
   nsCOMPtr<nsIJunkMailPlugin> mJunkMailPlugin;
   nsCOMPtr<nsIJunkMailClassificationListener> mJunkListener;
   nsCOMPtr<nsIMsgTraitClassificationListener> mTraitListener;
   nsCOMPtr<nsIMsgTraitDetailListener> mDetailListener;
   nsTArray<uint32_t> mProTraits;
   nsTArray<uint32_t> mAntiTraits;
   nsCOMPtr<nsIMsgWindow> mMsgWindow;
-  int32_t mNumMessagesToClassify;
-  int32_t mCurMessageToClassify;  // 0-based index
-  char** mMessageURIs;
+  nsTArray<nsCString> mMessageURIs;
+  uint32_t mCurMessageToClassify;  // 0-based index
 };
 
 nsresult nsBayesianFilter::tokenizeMessage(const char* aMessageURI,
                                            nsIMsgWindow* aMsgWindow,
                                            TokenAnalyzer* aAnalyzer) {
   NS_ENSURE_ARG_POINTER(aMessageURI);
 
   nsCOMPtr<nsIMsgMessageService> msgService;
@@ -1695,39 +1685,39 @@ NS_IMETHODIMP nsBayesianFilter::GetShoul
   return NS_OK;
 }
 
 /* void classifyMessage (in string aMsgURL, in nsIJunkMailClassificationListener
  * aListener); */
 NS_IMETHODIMP nsBayesianFilter::ClassifyMessage(
     const char* aMessageURL, nsIMsgWindow* aMsgWindow,
     nsIJunkMailClassificationListener* aListener) {
+  AutoTArray<nsCString, 1> urls = {nsDependentCString(aMessageURL)};
   MessageClassifier* analyzer =
-      new MessageClassifier(this, aListener, aMsgWindow, 1, &aMessageURL);
+      new MessageClassifier(this, aListener, aMsgWindow, urls);
   NS_ENSURE_TRUE(analyzer, NS_ERROR_OUT_OF_MEMORY);
   TokenStreamListener* tokenListener = new TokenStreamListener(analyzer);
   NS_ENSURE_TRUE(tokenListener, NS_ERROR_OUT_OF_MEMORY);
   analyzer->setTokenListener(tokenListener);
   return tokenizeMessage(aMessageURL, aMsgWindow, analyzer);
 }
 
-/* void classifyMessages (in unsigned long aCount, [array, size_is (aCount)] in
- * string aMsgURLs, in nsIJunkMailClassificationListener aListener); */
+/* void classifyMessages(in Array<ACString> aMsgURIs,
+ *                       in nsIMsgWindow aMsgWindow,
+ *                       in nsIJunkMailClassificationListener aListener); */
 NS_IMETHODIMP nsBayesianFilter::ClassifyMessages(
-    uint32_t aCount, const char** aMsgURLs, nsIMsgWindow* aMsgWindow,
+    const nsTArray<nsCString>& aMsgURLs, nsIMsgWindow* aMsgWindow,
     nsIJunkMailClassificationListener* aListener) {
-  NS_ENSURE_ARG_POINTER(aMsgURLs);
-
   TokenAnalyzer* analyzer =
-      new MessageClassifier(this, aListener, aMsgWindow, aCount, aMsgURLs);
+      new MessageClassifier(this, aListener, aMsgWindow, aMsgURLs);
   NS_ENSURE_TRUE(analyzer, NS_ERROR_OUT_OF_MEMORY);
   TokenStreamListener* tokenListener = new TokenStreamListener(analyzer);
   NS_ENSURE_TRUE(tokenListener, NS_ERROR_OUT_OF_MEMORY);
   analyzer->setTokenListener(tokenListener);
-  return tokenizeMessage(aMsgURLs[0], aMsgWindow, analyzer);
+  return tokenizeMessage(aMsgURLs[0].get(), aMsgWindow, analyzer);
 }
 
 nsresult nsBayesianFilter::setAnalysis(Token& token, uint32_t aTraitIndex,
                                        double aDistance, double aProbability) {
   uint32_t nextLink = token.mAnalysisLink;
   uint32_t lastLink = 0;
   uint32_t linkCount = 0, maxLinks = 100;
 
@@ -1778,56 +1768,46 @@ uint32_t nsBayesianFilter::getAnalysisIn
   }
   NS_ASSERTION(linkCount < maxLinks, "corrupt analysis store");
 
   // Trait not found, indicate by zero
   return 0;
 }
 
 NS_IMETHODIMP nsBayesianFilter::ClassifyTraitsInMessage(
-    const char* aMsgURI, uint32_t aTraitCount, uint32_t* aProTraits,
-    uint32_t* aAntiTraits, nsIMsgTraitClassificationListener* aTraitListener,
-    nsIMsgWindow* aMsgWindow,
+    const nsACString& aMsgURI, const nsTArray<uint32_t>& aProTraits,
+    const nsTArray<uint32_t>& aAntiTraits,
+    nsIMsgTraitClassificationListener* aTraitListener, nsIMsgWindow* aMsgWindow,
     nsIJunkMailClassificationListener* aJunkListener) {
-  return ClassifyTraitsInMessages(1, &aMsgURI, aTraitCount, aProTraits,
-                                  aAntiTraits, aTraitListener, aMsgWindow,
-                                  aJunkListener);
+  AutoTArray<nsCString, 1> uris = {PromiseFlatCString(aMsgURI)};
+  return ClassifyTraitsInMessages(uris, aProTraits, aAntiTraits, aTraitListener,
+                                  aMsgWindow, aJunkListener);
 }
 
 NS_IMETHODIMP nsBayesianFilter::ClassifyTraitsInMessages(
-    uint32_t aCount, const char** aMsgURIs, uint32_t aTraitCount,
-    uint32_t* aProTraits, uint32_t* aAntiTraits,
+    const nsTArray<nsCString>& aMsgURIs, const nsTArray<uint32_t>& aProTraits,
+    const nsTArray<uint32_t>& aAntiTraits,
     nsIMsgTraitClassificationListener* aTraitListener, nsIMsgWindow* aMsgWindow,
     nsIJunkMailClassificationListener* aJunkListener) {
-  AutoTArray<uint32_t, kTraitAutoCapacity> proTraits;
-  AutoTArray<uint32_t, kTraitAutoCapacity> antiTraits;
-  if (aTraitCount > kTraitAutoCapacity) {
-    proTraits.SetCapacity(aTraitCount);
-    antiTraits.SetCapacity(aTraitCount);
-  }
-  proTraits.AppendElements(aProTraits, aTraitCount);
-  antiTraits.AppendElements(aAntiTraits, aTraitCount);
-
-  MessageClassifier* analyzer = new MessageClassifier(
-      this, aJunkListener, aTraitListener, nullptr, proTraits, antiTraits,
-      aMsgWindow, aCount, aMsgURIs);
-  NS_ENSURE_TRUE(analyzer, NS_ERROR_OUT_OF_MEMORY);
+  MOZ_ASSERT(aProTraits.Length() == aAntiTraits.Length());
+  MessageClassifier* analyzer =
+      new MessageClassifier(this, aJunkListener, aTraitListener, nullptr,
+                            aProTraits, aAntiTraits, aMsgWindow, aMsgURIs);
 
   TokenStreamListener* tokenListener = new TokenStreamListener(analyzer);
-  NS_ENSURE_TRUE(tokenListener, NS_ERROR_OUT_OF_MEMORY);
 
   analyzer->setTokenListener(tokenListener);
-  return tokenizeMessage(aMsgURIs[0], aMsgWindow, analyzer);
+  return tokenizeMessage(aMsgURIs[0].get(), aMsgWindow, analyzer);
 }
 
 class MessageObserver : public TokenAnalyzer {
  public:
   MessageObserver(nsBayesianFilter* filter,
-                  nsTArray<uint32_t>& aOldClassifications,
-                  nsTArray<uint32_t>& aNewClassifications,
+                  const nsTArray<uint32_t>& aOldClassifications,
+                  const nsTArray<uint32_t>& aNewClassifications,
                   nsIJunkMailClassificationListener* aJunkListener,
                   nsIMsgTraitClassificationListener* aTraitListener)
       : mFilter(filter),
         mJunkMailPlugin(filter),
         mJunkListener(aJunkListener),
         mTraitListener(aTraitListener),
         mOldClassifications(aOldClassifications),
         mNewClassifications(aNewClassifications) {}
@@ -1844,29 +1824,22 @@ class MessageObserver : public TokenAnal
   nsCOMPtr<nsIJunkMailPlugin> mJunkMailPlugin;
   nsCOMPtr<nsIJunkMailClassificationListener> mJunkListener;
   nsCOMPtr<nsIMsgTraitClassificationListener> mTraitListener;
   nsTArray<uint32_t> mOldClassifications;
   nsTArray<uint32_t> mNewClassifications;
 };
 
 NS_IMETHODIMP nsBayesianFilter::SetMsgTraitClassification(
-    const char* aMsgURI, uint32_t aOldCount, uint32_t* aOldTraits,
-    uint32_t aNewCount, uint32_t* aNewTraits,
+    const char* aMsgURI, const nsTArray<uint32_t>& aOldTraits,
+    const nsTArray<uint32_t>& aNewTraits,
     nsIMsgTraitClassificationListener* aTraitListener, nsIMsgWindow* aMsgWindow,
     nsIJunkMailClassificationListener* aJunkListener) {
-  AutoTArray<uint32_t, kTraitAutoCapacity> oldTraits;
-  AutoTArray<uint32_t, kTraitAutoCapacity> newTraits;
-  if (aOldCount > kTraitAutoCapacity) oldTraits.SetCapacity(aOldCount);
-  if (aNewCount > kTraitAutoCapacity) newTraits.SetCapacity(aNewCount);
-  oldTraits.AppendElements(aOldTraits, aOldCount);
-  newTraits.AppendElements(aNewTraits, aNewCount);
-
   MessageObserver* analyzer = new MessageObserver(
-      this, oldTraits, newTraits, aJunkListener, aTraitListener);
+      this, aOldTraits, aNewTraits, aJunkListener, aTraitListener);
   NS_ENSURE_TRUE(analyzer, NS_ERROR_OUT_OF_MEMORY);
 
   TokenStreamListener* tokenListener = new TokenStreamListener(analyzer);
   NS_ENSURE_TRUE(tokenListener, NS_ERROR_OUT_OF_MEMORY);
 
   analyzer->setTokenListener(tokenListener);
   return tokenizeMessage(aMsgURI, aMsgWindow, analyzer);
 }
@@ -1994,24 +1967,23 @@ NS_IMETHODIMP nsBayesianFilter::SetMessa
 
 NS_IMETHODIMP nsBayesianFilter::ResetTrainingData() {
   return mCorpus.resetTrainingData();
 }
 
 NS_IMETHODIMP nsBayesianFilter::DetailMessage(
     const char* aMsgURI, uint32_t aProTrait, uint32_t aAntiTrait,
     nsIMsgTraitDetailListener* aDetailListener, nsIMsgWindow* aMsgWindow) {
-  AutoTArray<uint32_t, 1> proTraits;
-  AutoTArray<uint32_t, 1> antiTraits;
-  proTraits.AppendElement(aProTrait);
-  antiTraits.AppendElement(aAntiTrait);
+  AutoTArray<uint32_t, 1> proTraits = {aProTrait};
+  AutoTArray<uint32_t, 1> antiTraits = {aAntiTrait};
+  AutoTArray<nsCString, 1> uris = {nsDependentCString(aMsgURI)};
 
   MessageClassifier* analyzer =
       new MessageClassifier(this, nullptr, nullptr, aDetailListener, proTraits,
-                            antiTraits, aMsgWindow, 1, &aMsgURI);
+                            antiTraits, aMsgWindow, uris);
   NS_ENSURE_TRUE(analyzer, NS_ERROR_OUT_OF_MEMORY);
 
   TokenStreamListener* tokenListener = new TokenStreamListener(analyzer);
   NS_ENSURE_TRUE(tokenListener, NS_ERROR_OUT_OF_MEMORY);
 
   analyzer->setTokenListener(tokenListener);
   return tokenizeMessage(aMsgURI, aMsgWindow, analyzer);
 }
@@ -2027,19 +1999,21 @@ NS_IMETHODIMP nsBayesianFilter::CorpusCo
   return NS_OK;
 }
 
 NS_IMETHODIMP nsBayesianFilter::ClearTrait(uint32_t aTrait) {
   return mCorpus.ClearTrait(aTrait);
 }
 
 NS_IMETHODIMP
-nsBayesianFilter::UpdateData(nsIFile* aFile, bool aIsAdd, uint32_t aRemapCount,
-                             uint32_t* aFromTraits, uint32_t* aToTraits) {
-  return mCorpus.UpdateData(aFile, aIsAdd, aRemapCount, aFromTraits, aToTraits);
+nsBayesianFilter::UpdateData(nsIFile* aFile, bool aIsAdd,
+                             const nsTArray<uint32_t>& aFromTraits,
+                             const nsTArray<uint32_t>& aToTraits) {
+  MOZ_ASSERT(aFromTraits.Length() == aToTraits.Length());
+  return mCorpus.UpdateData(aFile, aIsAdd, aFromTraits, aToTraits);
 }
 
 NS_IMETHODIMP
 nsBayesianFilter::GetTokenCount(const nsACString& aWord, uint32_t aTrait,
                                 uint32_t* aCount) {
   NS_ENSURE_ARG_POINTER(aCount);
   CorpusToken* t = mCorpus.get(PromiseFlatCString(aWord).get());
   uint32_t count = mCorpus.getTraitCount(t, aTrait);
@@ -2372,17 +2346,18 @@ void CorpusStore::readTrainingData() {
   if (!mTraitFile) {
     getTraitFile(getter_AddRefs(mTraitFile));
     if (!mTraitFile) return;
   }
 
   rv = mTraitFile->Exists(&exists);
   if (NS_FAILED(rv) || !exists) return;
 
-  rv = UpdateData(mTraitFile, true, 0, nullptr, nullptr);
+  nsTArray<uint32_t> empty;
+  rv = UpdateData(mTraitFile, true, empty, empty);
 
   if (NS_FAILED(rv)) {
     NS_WARNING("failed to read training data.");
     MOZ_LOG(BayesianFilterLogModule, LogLevel::Error,
             ("failed to read training data."));
   }
   return;
 }
@@ -2496,23 +2471,20 @@ void CorpusStore::setMessageCount(uint32
     mMessageCounts.AppendElement(aCount);
     mMessageCountsId.AppendElement(aTraitId);
   } else {
     mMessageCounts[index] = aCount;
   }
 }
 
 nsresult CorpusStore::UpdateData(nsIFile* aFile, bool aIsAdd,
-                                 uint32_t aRemapCount, uint32_t* aFromTraits,
-                                 uint32_t* aToTraits) {
+                                 const nsTArray<uint32_t>& aFromTraits,
+                                 const nsTArray<uint32_t>& aToTraits) {
   NS_ENSURE_ARG_POINTER(aFile);
-  if (aRemapCount) {
-    NS_ENSURE_ARG_POINTER(aFromTraits);
-    NS_ENSURE_ARG_POINTER(aToTraits);
-  }
+  MOZ_ASSERT(aFromTraits.Length() == aToTraits.Length());
 
   int64_t fileSize;
   nsresult rv = aFile->GetFileSize(&fileSize);
   NS_ENSURE_SUCCESS(rv, rv);
 
   FILE* stream;
   rv = aFile->OpenANSIFileDesc("rb", &stream);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -2527,17 +2499,17 @@ nsresult CorpusStore::UpdateData(nsIFile
 
     uint32_t fileTrait;
     while (!(error = (readUInt32(stream, &fileTrait) != 1)) && fileTrait) {
       uint32_t count;
       if ((error = (readUInt32(stream, &count) != 1))) break;
 
       uint32_t localTrait = fileTrait;
       // remap the trait
-      for (uint32_t i = 0; i < aRemapCount; i++) {
+      for (uint32_t i = 0; i < aFromTraits.Length(); i++) {
         if (aFromTraits[i] == fileTrait) localTrait = aToTraits[i];
       }
 
       uint32_t messageCount = getMessageCount(localTrait);
       if (aIsAdd)
         messageCount += count;
       else if (count > messageCount)
         messageCount = 0;
--- a/mailnews/extensions/bayesian-spam-filter/src/nsBayesianFilter.h
+++ b/mailnews/extensions/bayesian-spam-filter/src/nsBayesianFilter.h
@@ -250,33 +250,28 @@ class CorpusStore : public TokenHash {
    *                    [number of messages per trait]
    *                    for each token with non-zero count
    *                    [count]
    *                    [length of word]word
    *
    * @param aIsAdd      should the data be added, or removed? true if adding,
    *                    else removing.
    *
-   * @param aRemapCount number of items in the parallel arrays aFromTraits,
-   *                    aToTraits. These arrays allow conversion of the
-   *                    trait id stored in the file (which may be originated
-   *                    externally) to the trait id used in the local corpus
-   *                    (which is defined locally using nsIMsgTraitService).
-   *
    * @param aFromTraits array of trait ids used in aFile. If aFile contains
    *                    trait ids that are not in this array, they are not
    *                    remapped, but assumed to be local trait ids.
    *
    * @param aToTraits   array of trait ids, corresponding to elements of
    *                    aFromTraits, that represent the local trait ids to be
    *                    used in storing data from aFile into the local corpus.
    *
    */
-  nsresult UpdateData(nsIFile* aFile, bool aIsAdd, uint32_t aRemapCount,
-                      uint32_t* aFromTraits, uint32_t* aToTraits);
+  nsresult UpdateData(nsIFile* aFile, bool aIsAdd,
+                      const nsTArray<uint32_t>& aFromTraits,
+                      const nsTArray<uint32_t>& aToTraits);
 
   /**
    * remove all counts (message and tokens) for a trait id
    *
    * @param aTrait  trait id for the trait to remove
    */
   nsresult ClearTrait(uint32_t aTrait);
 
--- a/mailnews/extensions/bayesian-spam-filter/test/unit/test_customTokenization.js
+++ b/mailnews/extensions/bayesian-spam-filter/test/unit/test_customTokenization.js
@@ -165,21 +165,19 @@ function startCommand() {
 
   gTest = tests.shift();
   // print("StartCommand command = " + gTest.command + ", remaining tests " + tests.length);
   switch (gTest.command) {
     case kTrain:
       // train message
 
       MailServices.junk.setMsgTraitClassification(
-        getSpec(gTest.fileName), // in string aMsgURI
-        0,
-        null, // in nsIArray aOldTraits
-        kProArray.length,
-        kProArray, // in nsIArray aNewTraits
+        getSpec(gTest.fileName), // aMsgURI
+        [], // aOldTraits
+        kProArray, // aNewTraits
         listener
       ); // [optional] in nsIMsgTraitClassificationListener aTraitListener
       // null,      // [optional] in nsIMsgWindow aMsgWindow
       // null,      // [optional] in nsIJunkMailClassificationListener aJunkListener
       break;
 
     case kTest:
       // test headers from detail message
--- a/mailnews/extensions/bayesian-spam-filter/test/unit/test_junkAsTraits.js
+++ b/mailnews/extensions/bayesian-spam-filter/test/unit/test_junkAsTraits.js
@@ -493,41 +493,38 @@ function startCommand() {
         null, // in nsIMsgWindow aMsgWindow
         junkListener
       ); // in nsIJunkMailClassificationListener aListener);
       break;
 
     case kTrainT:
       // train message using trait call
       MailServices.junk.setMsgTraitClassification(
-        getSpec(fileName), // in string aMsgURI
-        0, // length of aOldTraits array
-        null, // in array aOldTraits
-        gProArray.length, // length of array
-        junkPercent == kIsSpamScore ? gProArray : gAntiArray, // in array aNewTraits
-        tListener ? traitListener : null, // in nsIMsgTraitClassificationListener aTraitListener
-        null, // in nsIMsgWindow aMsgWindow
+        getSpec(fileName), // aMsgURI
+        [], // aOldTraits
+        junkPercent == kIsSpamScore ? gProArray : gAntiArray, // aNewTraits
+        tListener ? traitListener : null, // aTraitListener
+        null, // aMsgWindow
         jListener ? junkListener : null
-      ); // in nsIJunkMailClassificationListener aJunkListener
+      );
       break;
 
     case kClassJ:
       // classify message using junk call
       MailServices.junk.classifyMessage(
         getSpec(fileName), // in string aMsgURI
         null, // in nsIMsgWindow aMsgWindow
         junkListener
       ); // in nsIJunkMailClassificationListener aListener
       break;
 
     case kClassT:
       // classify message using trait call
       MailServices.junk.classifyTraitsInMessage(
         getSpec(fileName), // in string aMsgURI
-        gProArray.length, // length of traits arrays
         gProArray, // in array aProTraits,
         gAntiArray, // in array aAntiTraits
         tListener ? traitListener : null, // in nsIMsgTraitClassificationListener aTraitListener
         null, // in nsIMsgWindow aMsgWindow
         jListener ? junkListener : null
       ); // in nsIJunkMailClassificationListener aJunkListener
       break;
 
@@ -541,20 +538,18 @@ function startCommand() {
         junkListener
       ); // in nsIJunkMailClassificationListener aListener
       break;
 
     case kForgetT:
       // forget message using trait call
       MailServices.junk.setMsgTraitClassification(
         getSpec(fileName), // in string aMsgURI
-        gProArray.length, // length of aOldTraits array (1 in this test)
         junkPercent == kIsSpamScore ? gProArray : gAntiArray, // in array aOldTraits
-        0, // length of aNewTraits array
-        null, // in array aNewTraits
+        [], // in array aNewTraits
         tListener ? traitListener : null, // in nsIMsgTraitClassificationListener aTraitListener
         null, // in nsIMsgWindow aMsgWindow
         jListener ? junkListener : null
       ); // in nsIJunkMailClassificationListener aJunkListener
       break;
 
     case kCounts:
       // test counts
--- a/mailnews/extensions/bayesian-spam-filter/test/unit/test_msgCorpus.js
+++ b/mailnews/extensions/bayesian-spam-filter/test/unit/test_msgCorpus.js
@@ -66,17 +66,17 @@ var gTests = [
       let word = tokenData[i][2];
       Assert.equal(count, msgCorpus.getTokenCount(word, id));
     }
   },
   // remap the ids in the file to different local ids
   function loadWithRemap() {
     let fileName = "msgCorpus.dat";
     let file = do_get_file("resources/" + fileName);
-    msgCorpus.updateData(file, true, 2, [1001, 1003], [1, 3]);
+    msgCorpus.updateData(file, true, [1001, 1003], [1, 3]);
 
     for (let i = 0; i < tokenData.length; i++) {
       let id = tokenData[i][0] - 1000;
       let count = tokenData[i][1];
       let word = tokenData[i][2];
       Assert.equal(count, msgCorpus.getTokenCount(word, id));
     }
   },
--- a/mailnews/extensions/bayesian-spam-filter/test/unit/test_traitAliases.js
+++ b/mailnews/extensions/bayesian-spam-filter/test/unit/test_traitAliases.js
@@ -158,16 +158,15 @@ function startCommand() {
     traitService.addAlias(kProTrait, proAlias);
   }
   while ((antiAlias = gTest.antiAliases.pop())) {
     traitService.addAlias(kAntiTrait, antiAlias);
   }
 
   MailServices.junk.classifyTraitsInMessage(
     getSpec(gTest.fileName), // in string aMsgURI
-    proArray.length, // length of traits arrays
     proArray, // in array aProTraits,
     antiArray, // in array aAntiTraits
     listener
   ); // in nsIMsgTraitClassificationListener aTraitListener
   // null,      // [optional] in nsIMsgWindow aMsgWindow
   // null,      // [optional] in nsIJunkMailClassificationListener aJunkListener
 }
--- a/mailnews/extensions/bayesian-spam-filter/test/unit/test_traits.js
+++ b/mailnews/extensions/bayesian-spam-filter/test/unit/test_traits.js
@@ -203,21 +203,19 @@ function startCommand() {
       // train message
       let proArray = [];
       for (let i = 0; i < gTest.traitIds.length; i++) {
         proArray.push(gTest.traitIds[i]);
       }
       gTest.callbacks = 1;
 
       nsIJunkMailPlugin.setMsgTraitClassification(
-        getSpec(gTest.fileName), // in string aMsgURI
-        0,
-        null, // in nsIArray aOldTraits
-        proArray.length,
-        proArray, // in nsIArray aNewTraits
+        getSpec(gTest.fileName), // aMsgURI
+        [], // aOldTraits
+        proArray, // aNewTraits
         listener
       ); // [optional] in nsIMsgTraitClassificationListener aTraitListener
       // null,      // [optional] in nsIMsgWindow aMsgWindow
       // null,      // [optional] in nsIJunkMailClassificationListener aJunkListener
       break;
     }
     case kClass: {
       // classify message
@@ -232,29 +230,26 @@ function startCommand() {
       gTest.currentIndex = 0;
       for (let i = 0; i < gTest.files.length; i++) {
         gTest.files[i] = getSpec(gTest.files[i]);
       }
       if (gTest.files.length == 1) {
         // use the singular classifier
         nsIJunkMailPlugin.classifyTraitsInMessage(
           getSpec(gTest.fileName), // in string aMsgURI
-          proArray.length, // length of traits arrays
           proArray, // in array aProTraits,
           antiArray, // in array aAntiTraits
           listener
         ); // in nsIMsgTraitClassificationListener aTraitListener
         // null,      // [optional] in nsIMsgWindow aMsgWindow
         // null,      // [optional] in nsIJunkMailClassificationListener aJunkListener
       } else {
         // use the plural classifier
         nsIJunkMailPlugin.classifyTraitsInMessages(
-          gTest.files.length, // in unsigned long aCount,
-          gTest.files, // [array, size_is(aCount)] in string aMsgURIs,
-          proArray.length, // length of traits arrays
+          gTest.files, // in Array<ACString> aMsgURIs,
           proArray, // in array aProTraits,
           antiArray, // in array aAntiTraits
           listener
         ); // in nsIMsgTraitClassificationListener aTraitListener
         // null,      // [optional] in nsIMsgWindow aMsgWindow
         // null,      // [optional] in nsIJunkMailClassificationListener aJunkListener
       }
       break;