Bug 759422 - Remove all uses of e4x from comm-central to reopen a CLOSED TREE, r=BenB.
authorJoshua Cranmer <Pidgeot18@gmail.com>
Tue, 07 Aug 2012 14:58:47 -0500
changeset 14779 078dd301fa0c065beab7898a99fc60205b3b14f1
parent 14778 eb1a4a4c5a76c040279c84fe6c1950c5d76c9585
child 14780 8c02df6a242dbe3dc3166a4f2921e74df6cac2d0
push id867
push userbugzilla@standard8.plus.com
push dateMon, 01 Apr 2013 20:44:27 +0000
treeherdercomm-beta@797726b8d244 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersBenB
bugs759422
Bug 759422 - Remove all uses of e4x from comm-central to reopen a CLOSED TREE, r=BenB.
mail/app/profile/all-thunderbird.js
mail/components/cloudfile/nsYouSendIt.js
mail/components/newmailaccount/content/uriListener.js
mailnews/base/prefs/content/accountcreation/fetchConfig.js
mailnews/base/prefs/content/accountcreation/fetchhttp.js
mailnews/base/prefs/content/accountcreation/readFromXML.js
mailnews/base/test/unit/test_autoconfigXML.js
mailnews/base/test/unit/test_searchCustomTerm.js
mailnews/base/util/JXON.js
mailnews/base/util/Makefile.in
--- a/mail/app/profile/all-thunderbird.js
+++ b/mail/app/profile/all-thunderbird.js
@@ -6,18 +6,16 @@
 #filter substitution
 
 #ifdef XP_UNIX
 #ifndef XP_MACOSX
 #define UNIX_BUT_NOT_MAC
 #endif
 #endif
 
-pref("javascript.options.xml.chrome", true);
-
 pref("general.useragent.locale", "@AB_CD@");
 pref("general.skins.selectedSkin", "classic/1.0");
 
 #ifdef XP_MACOSX
 pref("browser.chromeURL", "chrome://messenger/content/messengercompose/messengercompose.xul");
 pref("mail.biff.animate_dock_icon", false);
 #endif
 
@@ -381,18 +379,16 @@ pref("browser.download.manager.showAlert
 pref("browser.download.manager.retention", 1);
 pref("browser.download.manager.showWhenStarting", true);
 pref("browser.download.manager.closeWhenDone", true);
 pref("browser.download.manager.openDelay", 100);
 pref("browser.download.manager.focusWhenStarting", false);
 pref("browser.download.manager.flashCount", 0);
 pref("browser.download.manager.addToRecentDocs", true);
 
-pref("javascript.options.showInConsole",    true);
-
 pref("spellchecker.dictionary", "");
 // Dictionary download preference
 pref("spellchecker.dictionaries.download.url", "https://addons.mozilla.org/%LOCALE%/%APP%/dictionaries/");
 
 // profile.force.migration can be used to bypass the migration wizard, forcing migration from a particular
 // mail application without any user intervention. Possible values are: 
 // seamonkey (mozilla suite), eudora, oexpress, outlook.
 pref("profile.force.migration", "");
--- a/mail/components/cloudfile/nsYouSendIt.js
+++ b/mail/components/cloudfile/nsYouSendIt.js
@@ -975,27 +975,17 @@ nsYouSendItFileUploader.prototype = {
     let curDate = Date.now().toString();
     this.log.info("upload url = " + this._urlInfo.uploadUrl[0]);
     this.request = req;
     req.open("POST", this._urlInfo.uploadUrl[0] + "?" + kUrlTail, true);
     req.onload = function() {
       this.cleanupTempFile();
       if (req.status >= 200 && req.status < 400) {
         try {
-          let response = req.responseText.replace(/<\?xml[^>]*\?>/, "");
-          this.log.info("upload response = " + response);
-          let docResponse = new XML(response);
-          this.log.info("docResponse = " + docResponse);
-          this._uploadResponse = docResponse;
-          let ysiError = docResponse['ysi-error'];
-          let uploadStatus = docResponse['upload-status'];
-          this.log.info("upload status = " + uploadStatus);
-          this.log.info("ysi error = " + ysiError);
-          let errorCode = docResponse['error-code'];
-          this.log.info("error code = " + errorCode);
+          this.log.info("upload response = " + req.responseText);
           this._commitSend();
         } catch (ex) {
           this.log.error(ex);
         }
       }
       else
         this.callback(this.requestObserver,
                       Ci.nsIMsgCloudFileProvider.uploadErr);
--- a/mail/components/newmailaccount/content/uriListener.js
+++ b/mail/components/newmailaccount/content/uriListener.js
@@ -11,16 +11,17 @@
 let Cu = Components.utils;
 let Cc = Components.classes;
 let Ci = Components.interfaces;
 let Cr = Components.results;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/JXON.js");
 
 /**
  * This is an observer that watches all HTTP requests for one where the
  * response contentType contains text/xml.  Once that observation is
  * made, we ensure that the associated window for that request matches
  * the window belonging to the content tab for the account order form.
  * If so, we attach an nsITraceableListener to read the contents of the
  * request response, and react accordingly if the contents can be turned
@@ -170,20 +171,19 @@ TracingListener.prototype = {
 
     let tabmail = document.getElementById('tabmail');
     let success = false;
     let account;
 
     try {
       // Attempt to construct the downloaded data into XML
       let data = this.chunks.join("");
-      let xml = new XML(data);
 
       // Attempt to derive email account information
-      let accountConfig = accountCreationFuncs.readFromXML(xml);
+      let accountConfig = accountCreationFuncs.readFromXML(JXON.build(data));
       accountCreationFuncs.replaceVariables(accountConfig,
                                             this.params.realName,
                                             this.params.email);
       account = accountCreationFuncs.createAccountInBackend(accountConfig);
       success = true;
     } catch (e) {
       // Something went wrong with account set up. Dump the error out to the
       // error console. The tab will be closed, and the Account Provisioner
--- a/mailnews/base/prefs/content/accountcreation/fetchConfig.js
+++ b/mailnews/base/prefs/content/accountcreation/fetchConfig.js
@@ -5,32 +5,35 @@
 
 /**
  * Tries to find a configuration for this ISP on the local harddisk, in the
  * application install directory's "isp" subdirectory.
  * Params @see fetchConfigFromISP()
  */
 
 Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/JXON.js");
+
 
 function fetchConfigFromDisk(domain, successCallback, errorCallback)
 {
   return new TimeoutAbortable(runAsync(function()
   {
     try {
       // <TB installdir>/isp/example.com.xml
       var configLocation = Services.dirsvc.get("CurProcD", Ci.nsIFile);
       configLocation.append("isp");
       configLocation.append(sanitize.hostname(domain) + ".xml");
 
       var contents =
         readURLasUTF8(Services.io.newFileURI(configLocation));
-       // Bug 336551 trips over <?xml ... >
-      contents = contents.replace(/<\?xml[^>]*\?>/, "");
-      successCallback(readFromXML(new XML(contents)));
+      let domParser = Cc["@mozilla.org/xmlextras/domparser;1"]
+                       .createInstance(Ci.nsIDOMParser);
+      successCallback(readFromXML(JXON.build(
+        domParser.parseFromString(contents, "text/xml"))));
     } catch (e) { errorCallback(e); }
   }));
 }
 
 /**
  * Tries to get a configuration from the ISP / mail provider directly.
  *
  * Disclaimers:
--- a/mailnews/base/prefs/content/accountcreation/fetchhttp.js
+++ b/mailnews/base/prefs/content/accountcreation/fetchhttp.js
@@ -10,16 +10,18 @@
  *
  * It does not provide download progress, but assumes that the
  * fetched resource is so small (<1 10 KB) that the roundtrip and
  * response generation is far more significant than the
  * download time of the response. In other words, it's fine for RPC,
  * but not for bigger file downloads.
  */
 
+Components.utils.import("resource://gre/modules/JXON.js");
+
 /**
  * Set up a fetch.
  *
  * @param url {String}   URL of the server function.
  *    ATTENTION: The caller needs to make sure that the URL is secure to call.
  * @param urlArgs {Object, associative array} Parameters to add
  *   to the end of the URL as query string. E.g.
  *   { foo: "bla", bar: "blub blub" } will add "?foo=bla&bar=blub%20blub"
@@ -130,21 +132,18 @@ FetchHTTP.prototype =
         var mimetype = this._request.getResponseHeader("Content-Type");
         if (!mimetype)
           mimetype = "";
         mimetype = mimetype.split(";")[0];
         if (mimetype == "text/xml" ||
             mimetype == "application/xml" ||
             mimetype == "text/rdf")
         {
-          // Bug 270553 prevents usage of .responseXML
-          var text = this._request.responseText;
-           // Bug 336551 trips over <?xml ... >
-          text = text.replace(/<\?xml[^>]*\?>/, "");
-          this.result = new XML(text);
+          this._request.overrideMimeType("text/xml");
+          this.result = JXON.build(this._request.responseXML);
         }
         else
         {
           //ddump("mimetype: " + mimetype + " only supported as text");
           this.result = this._request.responseText;
         }
         //ddump("result:\n" + this.result);
       }
--- a/mailnews/base/prefs/content/accountcreation/readFromXML.js
+++ b/mailnews/base/prefs/content/accountcreation/readFromXML.js
@@ -1,88 +1,92 @@
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 /**
- * Takes an XML snipplet (as E4X) and reads the values into
+ * Takes an XML snipplet (as JXON) and reads the values into
  * a new AccountConfig object.
  * It does so securely (or tries to), by trying to avoid remote execution
  * and similar holes which can appear when reading too naively.
  * Of course it cannot tell whether the actual values are correct,
  * e.g. it can't tell whether the host name is a good server.
  *
  * The XML format is documented at
  * <https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat>
  *
- * @param clientConfigXML {E4X}  The <clientConfig> node.
+ * @param clientConfigXML {JXON}  The <clientConfig> node.
  * @return AccountConfig   object filled with the data from XML
  */
 function readFromXML(clientConfigXML)
 {
+  function array_or_undef(value) {
+    return value === undefined ? [] : value;
+  }
   var exception;
-  if (typeof(clientConfigXML) != "xml" ||
-      !("emailProvider" in clientConfigXML))
+  if (typeof(clientConfigXML) != "object" ||
+      !("clientConfig" in clientConfigXML) ||
+      !("emailProvider" in clientConfigXML.clientConfig))
   {
-    dump("client config xml = " + clientConfigXML + "\n");
+    dump("client config xml = " + JSON.stringify(clientConfigXML) + "\n");
     var stringBundle = getStringBundle(
         "chrome://messenger/locale/accountCreationModel.properties");
     throw stringBundle.GetStringFromName("no_emailProvider.error");
   }
-  var xml = clientConfigXML.emailProvider;
+  var xml = clientConfigXML.clientConfig.emailProvider;
 
   var d = new AccountConfig();
   d.source = AccountConfig.kSourceXML;
 
-  d.id = sanitize.hostname(xml.@id);
+  d.id = sanitize.hostname(xml["@id"]);
   d.displayName = d.id;
   try {
-    d.displayName = sanitize.label(xml.displayName[0]);
+    d.displayName = sanitize.label(xml.displayName);
   } catch (e) { logException(e); }
-  for each (var domain in xml.domain)
+  for (var domain of xml.$domain)
   {
     try {
       d.domains.push(sanitize.hostname(domain));
     } catch (e) { logException(e); exception = e; }
   }
   if (domain.length == 0)
     throw exception ? exception : "need proper <domain> in XML";
   exception = null;
 
   // incoming server
-  for each (let iX in xml.incomingServer) // input (XML)
+  for (let iX of array_or_undef(xml.$incomingServer)) // input (XML)
   {
     let iO = d.createNewIncoming(); // output (object)
     try {
       // throws if not supported
-      iO.type = sanitize.enum(iX.@type, ["pop3", "imap", "nntp"]);
-      iO.hostname = sanitize.hostname(iX.hostname[0]);
-      iO.port = sanitize.integerRange(iX.port[0], 1, 65535);
+      iO.type = sanitize.enum(iX["@type"], ["pop3", "imap", "nntp"]);
+      iO.hostname = sanitize.hostname(iX.hostname);
+      iO.port = sanitize.integerRange(iX.port, 1, 65535);
       // We need a username even for Kerberos, need it even internally.
-      iO.username = sanitize.string(iX.username[0]); // may be a %VARIABLE%
+      iO.username = sanitize.string(iX.username); // may be a %VARIABLE%
 
       if ("password" in iX) {
         d.rememberPassword = true;
-        iO.password = sanitize.string(iX.password[0]);
+        iO.password = sanitize.string(iX.password);
       }
 
-      for each (let iXsocketType in iX.socketType)
+      for (let iXsocketType of array_or_undef(iX.$socketType))
       {
         try {
           iO.socketType = sanitize.translate(iXsocketType,
               { plain : 1, SSL: 2, STARTTLS: 3 });
           break; // take first that we support
         } catch (e) { exception = e; }
       }
       if (!iO.socketType)
         throw exception ? exception : "need proper <socketType> in XML";
       exception = null;
 
-      for each (let iXauth in iX.authentication)
+      for (let iXauth of array_or_undef(iX.$authentication))
       {
         try {
           iO.auth = sanitize.translate(iXauth,
               { "password-cleartext" : Ci.nsMsgAuthMethod.passwordCleartext,
                 // @deprecated TODO remove
                 "plain" : Ci.nsMsgAuthMethod.passwordCleartext,
                 "password-encrypted" : Ci.nsMsgAuthMethod.passwordEncrypted,
                 // @deprecated TODO remove
@@ -95,25 +99,25 @@ function readFromXML(clientConfigXML)
       if (!iO.auth)
         throw exception ? exception : "need proper <authentication> in XML";
       exception = null;
 
       // defaults are in accountConfig.js
       if (iO.type == "pop3" && "pop3" in iX)
       {
         try {
-          if ("leaveMessagesOnServer" in iX.pop3[0])
+          if ("leaveMessagesOnServer" in iX.pop3)
             iO.leaveMessagesOnServer =
                 sanitize.boolean(iX.pop3.leaveMessagesOnServer);
-          if ("daysToLeaveMessagesOnServer" in iX.pop3[0])
+          if ("daysToLeaveMessagesOnServer" in iX.pop3)
             iO.daysToLeaveMessagesOnServer =
                 sanitize.integer(iX.pop3.daysToLeaveMessagesOnServer);
         } catch (e) { logException(e); }
         try {
-          if ("downloadOnBiff" in iX.pop3[0])
+          if ("downloadOnBiff" in iX.pop3)
             iO.downloadOnBiff = sanitize.boolean(iX.pop3.downloadOnBiff);
         } catch (e) { logException(e); }
       }
 
       // processed successfully, now add to result object
       if (!d.incoming.hostname) // first valid
         d.incoming = iO;
       else
@@ -121,42 +125,42 @@ function readFromXML(clientConfigXML)
     } catch (e) { exception = e; }
   }
   if (!d.incoming.hostname)
     // throw exception for last server
     throw exception ? exception : "Need proper <incomingServer> in XML file";
   exception = null;
 
   // outgoing server
-  for each (let oX in xml.outgoingServer) // input (XML)
+  for (let oX of array_or_undef(xml.$outgoingServer)) // input (XML)
   {
     let oO = d.createNewOutgoing(); // output (object)
     try {
-      if (oX.@type != "smtp")
+      if (oX["@type"] != "smtp")
       {
         var stringBundle = getStringBundle(
             "chrome://messenger/locale/accountCreationModel.properties");
         throw stringBundle.GetStringFromName("outgoing_not_smtp.error");
       }
-      oO.hostname = sanitize.hostname(oX.hostname[0]);
-      oO.port = sanitize.integerRange(oX.port[0], 1, 65535);
+      oO.hostname = sanitize.hostname(oX.hostname);
+      oO.port = sanitize.integerRange(oX.port, 1, 65535);
 
-      for each (let oXsocketType in oX.socketType)
+      for (let oXsocketType of array_or_undef(oX.$socketType))
       {
         try {
           oO.socketType = sanitize.translate(oXsocketType,
               { plain : 1, SSL: 2, STARTTLS: 3 });
           break; // take first that we support
         } catch (e) { exception = e; }
       }
       if (!oO.socketType)
         throw exception ? exception : "need proper <socketType> in XML";
       exception = null;
 
-      for each (let oXauth in oX.authentication)
+      for (let oXauth of array_or_undef(oX.$authentication))
       {
         try {
           oO.auth = sanitize.translate(oXauth,
               { // open relay
                 "none" : Ci.nsMsgAuthMethod.none,
                 // inside ISP or corp network
                 "client-IP-address" : Ci.nsMsgAuthMethod.none,
                 // hope for the best
@@ -177,21 +181,21 @@ function readFromXML(clientConfigXML)
         throw exception ? exception : "need proper <authentication> in XML";
       exception = null;
 
       if ("username" in oX ||
           // if password-based auth, we need a username,
           // so go there anyways and throw.
           oO.auth == Ci.nsMsgAuthMethod.passwordCleartext ||
           oO.auth == Ci.nsMsgAuthMethod.passwordEncrypted)
-        oO.username = sanitize.string(oX.username[0]);
+        oO.username = sanitize.string(oX.username);
 
       if ("password" in oX) {
         d.rememberPassword = true;
-        oO.password = sanitize.string(oX.password[0]);
+        oO.password = sanitize.string(oX.password);
       }
 
       try {
         // defaults are in accountConfig.js
         if ("addThisServer" in oX)
           oO.addThisServer = sanitize.boolean(oX.addThisServer);
         if ("useGlobalPreferredServer" in oX)
           oO.useGlobalPreferredServer =
@@ -206,24 +210,24 @@ function readFromXML(clientConfigXML)
     } catch (e) { logException(e); exception = e; }
   }
   if (!d.outgoing.hostname)
     // throw exception for last server
     throw exception ? exception : "Need proper <outgoingServer> in XML file";
   exception = null;
 
   d.inputFields = new Array();
-  for each (let inputField in xml.inputField)
+  for (let inputField of array_or_undef(xml.$inputField))
   {
     try {
       var fieldset =
       {
-        varname : sanitize.alphanumdash(inputField.@key).toUpperCase(),
-        displayName : sanitize.label(inputField.@label),
-        exampleValue : sanitize.label(inputField.text())
+        varname : sanitize.alphanumdash(inputField["@key"]).toUpperCase(),
+        displayName : sanitize.label(inputField["@label"]),
+        exampleValue : sanitize.label(inputField.value)
       };
       d.inputFields.push(fieldset);
     } catch (e) { logException(e); } // for now, don't throw,
         // because we don't support custom fields yet anyways.
   }
 
   return d;
 }
--- a/mailnews/base/test/unit/test_autoconfigXML.js
+++ b/mailnews/base/test/unit/test_autoconfigXML.js
@@ -12,16 +12,19 @@
  * reading and allow fallback mechanisms. This test checks whether that works,
  * and of course also whether we can read a normal config and get the proper
  * values.
  */
 
 // Globals
 
 Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/JXON.js");
+
+var DOMParser = Components.Constructor("@mozilla.org/xmlextras/domparser;1");
 
 var xmlReader = {};
 try {
   Services.scriptloader.loadSubScript(
       "chrome://messenger/content/accountcreation/util.js", xmlReader);
   Services.scriptloader.loadSubScript(
       "chrome://messenger/content/accountcreation/accountConfig.js",
       xmlReader);
@@ -60,114 +63,115 @@ function assert_equal_config(aA, aB, fie
 
 /**
  * Test that the xml reader returns a proper config and
  * is also forwards-compatible to new additions to the data format.
  */
 function test_readFromXML_config1()
 {
   var clientConfigXML =
-    <clientConfig>
-      <emailProvider id="example.com">
-        <domain>example.com</domain>
-        <domain>example.net</domain>
-        <displayName>Example</displayName>
-        <displayShortName>Example Mail</displayShortName>
+    '<clientConfig>' +
+      '<emailProvider id="example.com">' +
+        '<domain>example.com</domain>' +
+        '<domain>example.net</domain>' +
+        '<displayName>Example</displayName>' +
+        '<displayShortName>Example Mail</displayShortName>' +
 
-        <!-- 1. - protocol not supported -->
-        <incomingServer type="imap5">
-          <hostname>badprotocol.example.com</hostname>
-          <port>993</port>
-          <socketType>SSL</socketType>
-          <username>%EMAILLOCALPART%</username>
-          <authentication>ssl-client-cert</authentication>
-        </incomingServer>
-        <!-- 2. - socket type not supported -->
-        <incomingServer type="imap">
-          <hostname>badsocket.example.com</hostname>
-          <port>993</port>
-          <socketType>key-from-DNSSEC</socketType>
-          <username>%EMAILLOCALPART%</username>
-          <authentication>password-cleartext</authentication>
-        </incomingServer>
-        <!-- 3. - first supported incoming server -->
-        <incomingServer type="imap">
-          <hostname>imapmail.example.com</hostname>
-          <port>993</port>
-          <socketType>SSL</socketType>
-          <username>%EMAILLOCALPART%</username>
-          <authentication>password-cleartext</authentication>
-        </incomingServer>
-        <!-- 4. - auth method not supported -->
-        <incomingServer type="imap">
-          <hostname>badauth.example.com</hostname>
-          <port>993</port>
-          <socketType>SSL</socketType>
-          <username>%EMAILLOCALPART%</username>
-          <authentication>ssl-client-cert</authentication>
-          <!-- Throw in some elements we don't support yet -->
-          <imap>
-            <rootFolder path="INBOX." />
-            <specialFolder id="sent" path="INBOX.Sent Mail" />
-          </imap>
-        </incomingServer>
-        <!-- 5. - second supported incoming server -->
-        <incomingServer type="pop3">
-          <hostname>popmail.example.com</hostname>
-          <!-- alternative hostname, not yet supported, should be ignored -->
-          <hostname>popbackup.example.com</hostname>
-          <port>110</port>
-          <port>7878</port>
-          <!-- unsupported socket type -->
-          <socketType>GSSAPI2</socketType>
-          <!-- but fall back -->
-          <socketType>plain</socketType>
-          <username>%EMAILLOCALPART%</username>
-          <username>%EMAILADDRESS%</username>
-          <!-- unsupported auth method -->
-          <authentication>GSSAPI2</authentication>
-          <!-- but fall back -->
-          <authentication>password-encrypted</authentication>
-          <pop3>
-            <leaveMessagesOnServer>true</leaveMessagesOnServer>
-            <daysToLeaveMessagesOnServer>999</daysToLeaveMessagesOnServer>
-          </pop3>
-        </incomingServer>
+        // 1. - protocol not supported
+        '<incomingServer type="imap5">' +
+          '<hostname>badprotocol.example.com</hostname>' +
+          '<port>993</port>' +
+          '<socketType>SSL</socketType>' +
+          '<username>%EMAILLOCALPART%</username>' +
+          '<authentication>ssl-client-cert</authentication>' +
+        '</incomingServer>' +
+        // 2. - socket type not supported
+        '<incomingServer type="imap">' +
+          '<hostname>badsocket.example.com</hostname>' +
+          '<port>993</port>' +
+          '<socketType>key-from-DNSSEC</socketType>' +
+          '<username>%EMAILLOCALPART%</username>' +
+          '<authentication>password-cleartext</authentication>' +
+        '</incomingServer>' +
+        // 3. - first supported incoming server
+        '<incomingServer type="imap">' +
+          '<hostname>imapmail.example.com</hostname>' +
+          '<port>993</port>' +
+          '<socketType>SSL</socketType>' +
+          '<username>%EMAILLOCALPART%</username>' +
+          '<authentication>password-cleartext</authentication>' +
+        '</incomingServer>' +
+        // 4. - auth method not supported
+        '<incomingServer type="imap">' +
+          '<hostname>badauth.example.com</hostname>' +
+          '<port>993</port>' +
+          '<socketType>SSL</socketType>' +
+          '<username>%EMAILLOCALPART%</username>' +
+          '<authentication>ssl-client-cert</authentication>' +
+          // Throw in some elements we don't support yet
+          '<imap>' +
+            '<rootFolder path="INBOX." />' +
+            '<specialFolder id="sent" path="INBOX.Sent Mail" />' +
+          '</imap>' +
+        '</incomingServer>' +
+        // 5. - second supported incoming server
+        '<incomingServer type="pop3">' +
+          '<hostname>popmail.example.com</hostname>' +
+          // alternative hostname, not yet supported, should be ignored
+          '<hostname>popbackup.example.com</hostname>' +
+          '<port>110</port>' +
+          '<port>7878</port>' +
+          // unsupported socket type
+          '<socketType>GSSAPI2</socketType>' +
+          // but fall back
+          '<socketType>plain</socketType>' +
+          '<username>%EMAILLOCALPART%</username>' +
+          '<username>%EMAILADDRESS%</username>' +
+          // unsupported auth method
+          '<authentication>GSSAPI2</authentication>' +
+          // but fall back
+          '<authentication>password-encrypted</authentication>' +
+          '<pop3>' +
+            '<leaveMessagesOnServer>true</leaveMessagesOnServer>' +
+            '<daysToLeaveMessagesOnServer>999</daysToLeaveMessagesOnServer>' +
+          '</pop3>' +
+        '</incomingServer>' +
 
-        <!-- outgoing server with invalid auth method -->
-        <outgoingServer type="smtp">
-          <hostname>badauth.example.com</hostname>
-          <port>587</port>
-          <socketType>STARTTLS</socketType>
-          <username>%EMAILADDRESS%</username>
-          <authentication>smtp-after-imap</authentication>
-        </outgoingServer>
-        <!-- outgoing server - supported -->
-        <outgoingServer type="smtp">
-          <hostname>smtpout.example.com</hostname>
-          <hostname>smtpfallback.example.com</hostname>
-          <port>587</port>
-          <port>7878</port>
-          <socketType>GSSAPI2</socketType>
-          <socketType>STARTTLS</socketType>
-          <username>%EMAILADDRESS%</username>
-          <username>%EMAILLOCALPART%</username>
-          <authentication>GSSAPI2</authentication>
-          <authentication>client-IP-address</authentication>
-          <smtp/>
-        </outgoingServer>
+        // outgoing server with invalid auth method
+        '<outgoingServer type="smtp">' +
+          '<hostname>badauth.example.com</hostname>' +
+          '<port>587</port>' +
+          '<socketType>STARTTLS</socketType>' +
+          '<username>%EMAILADDRESS%</username>' +
+          '<authentication>smtp-after-imap</authentication>' +
+        '</outgoingServer>' +
+        // outgoing server - supported
+        '<outgoingServer type="smtp">' +
+          '<hostname>smtpout.example.com</hostname>' +
+          '<hostname>smtpfallback.example.com</hostname>' +
+          '<port>587</port>' +
+          '<port>7878</port>' +
+          '<socketType>GSSAPI2</socketType>' +
+          '<socketType>STARTTLS</socketType>' +
+          '<username>%EMAILADDRESS%</username>' +
+          '<username>%EMAILLOCALPART%</username>' +
+          '<authentication>GSSAPI2</authentication>' +
+          '<authentication>client-IP-address</authentication>' +
+          '<smtp/>' +
+        '</outgoingServer>' +
+        // Throw in some more elements we don't support yet
+        '<enableURL url="http://foobar" />' +
+        '<instructionsURL url="http://foobar" />' +
 
-        <!-- Throw in some more elements we don't support yet -->
-        <enableURL url="http://foobar" />
-        <instructionsURL url="http://foobar" />
+      '</emailProvider>' +
+    '</clientConfig>';
 
-      </emailProvider>
-    </clientConfig>;
-
-  var config = xmlReader.readFromXML(clientConfigXML);
+  var domParser = new DOMParser();
+  var config = xmlReader.readFromXML(JXON.build(
+    domParser.parseFromString(clientConfigXML, "text/xml")));
 
   do_check_eq(config instanceof xmlReader.AccountConfig, true);
   do_check_eq("example.com", config.id);
   do_check_eq("Example", config.displayName);
   do_check_neq(-1, config.domains.indexOf("example.com"));
   // 1. incoming server skipped because of an unsupported protocol
   // 2. incoming server skipped because of an so-far unknown auth method
   // 3. incoming server is fine for us: IMAP, SSL, cleartext password
@@ -197,45 +201,47 @@ function test_readFromXML_config1()
 }
 
 /**
  * Test the replaceVariables method.
  */
 function test_replaceVariables()
 {
   var clientConfigXML =
-    <clientConfig>
-      <emailProvider id="example.com">
-        <domain>example.com</domain>
-        <displayName>example.com</displayName>
-        <displayShortName>example.com</displayShortName>
-        <incomingServer type="pop3">
-          <hostname>pop.%EMAILDOMAIN%</hostname>
-          <port>995</port>
-          <socketType>SSL</socketType>
-          <username>%EMAILLOCALPART%</username>
-          <authentication>plain</authentication>
-          <pop3>
-            <leaveMessagesOnServer>true</leaveMessagesOnServer>
-            <daysToLeaveMessagesOnServer>999</daysToLeaveMessagesOnServer>
-          </pop3>
-        </incomingServer>
-        <outgoingServer type="smtp">
-          <hostname>smtp.example.com</hostname>
-          <port>587</port>
-          <socketType>STARTTLS</socketType>
-          <username>%EMAILADDRESS%</username>
-          <authentication>plain</authentication>
-          <addThisServer>true</addThisServer>
-          <useGlobalPreferredServer>false</useGlobalPreferredServer>
-        </outgoingServer>
-      </emailProvider>
-    </clientConfig>;
+    '<clientConfig>' +
+      '<emailProvider id="example.com">' +
+        '<domain>example.com</domain>' +
+        '<displayName>example.com</displayName>' +
+        '<displayShortName>example.com</displayShortName>' +
+        '<incomingServer type="pop3">' +
+          '<hostname>pop.%EMAILDOMAIN%</hostname>' +
+          '<port>995</port>' +
+          '<socketType>SSL</socketType>' +
+          '<username>%EMAILLOCALPART%</username>' +
+          '<authentication>plain</authentication>' +
+          '<pop3>' +
+            '<leaveMessagesOnServer>true</leaveMessagesOnServer>' +
+            '<daysToLeaveMessagesOnServer>999</daysToLeaveMessagesOnServer>' +
+          '</pop3>' +
+        '</incomingServer>' +
+        '<outgoingServer type="smtp">' +
+          '<hostname>smtp.example.com</hostname>' +
+          '<port>587</port>' +
+          '<socketType>STARTTLS</socketType>' +
+          '<username>%EMAILADDRESS%</username>' +
+          '<authentication>plain</authentication>' +
+          '<addThisServer>true</addThisServer>' +
+          '<useGlobalPreferredServer>false</useGlobalPreferredServer>' +
+        '</outgoingServer>' +
+      '</emailProvider>' +
+    '</clientConfig>';
 
-  var config = xmlReader.readFromXML(clientConfigXML);
+  var domParser = new DOMParser();
+  var config = xmlReader.readFromXML(JXON.build(
+    domParser.parseFromString(clientConfigXML, "text/xml")));
 
   xmlReader.replaceVariables(config, 
                              "Yamato Nadeshiko",
                              "yamato.nadeshiko@example.com",
                              "abc12345");
 
   assert_equal_config(config.incoming.username,
                       "yamato.nadeshiko",
--- a/mailnews/base/test/unit/test_searchCustomTerm.js
+++ b/mailnews/base/test/unit/test_searchCustomTerm.js
@@ -29,22 +29,22 @@ var Tests =
 // nsIMsgSearchCustomTerm object
 var customTerm =
 {
   id: kCustomId,
   name: "term name",
   getEnabled: function(scope, op)
     {
       return scope == Ci.nsMsgSearchScope.offlineMail &&
-             op == Ci.nsMsgSearchOp::Is
+             op == Ci.nsMsgSearchOp.Is
     },
   getAvailable: function(scope, op)
     {
       return scope == Ci.nsMsgSearchScope.offlineMail &&
-             op == Ci.nsMsgSearchOp::Is
+             op == Ci.nsMsgSearchOp.Is
     },
   getAvailableOperators: function(scope, length)
     {
        length.value = 1;
        return [Ci.nsMsgSearchOp.Is];
     },
   match: function(msgHdr, searchValue, searchOp)
     {
new file mode 100644
--- /dev/null
+++ b/mailnews/base/util/JXON.js
@@ -0,0 +1,181 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This is a modification of the JXON parsers found on the page
+// <https://developer.mozilla.org/en-US/docs/JXON>
+
+var EXPORTED_SYMBOLS = ["JXON"];
+let Cc = Components.classes;
+let Ci = Components.interfaces;
+
+const JXON = new (function() {
+  const sValueProp = "value"; /* you can customize these values */
+  const sAttributesProp = "attr";
+  const sAttrPref = "@";
+  const sElementListPrefix = "$";
+  const sConflictSuffix = "_"; // used when there's a name conflict with special JXON properties
+  const aCache = [];
+  const rIsNull = /^\s*$/;
+  const rIsBool = /^(?:true|false)$/i;
+
+  function parseText(sValue) {
+    //if (rIsNull.test(sValue))
+    //  return null;
+    if (rIsBool.test(sValue)) 
+      return sValue.toLowerCase() === "true";
+    if (isFinite(sValue))
+      return parseFloat(sValue);
+    if (isFinite(Date.parse(sValue)))
+      return new Date(sValue);
+    return sValue;
+  };
+
+  function EmptyTree() {
+  }
+  EmptyTree.prototype = {
+    toString : function () {
+      return "null";
+    },
+    valueOf : function () {
+      return null;
+    },
+  };
+
+  function objectify(vValue) {
+    if (vValue === null)
+      return new EmptyTree();
+    else if (vValue instanceof Object)
+      return vValue;
+    else
+      return new vValue.constructor(vValue); // What does this? copy?
+  };
+
+  function createObjTree(oParentNode, nVerb, bFreeze, bNesteAttr) {
+    const nLevelStart = aCache.length;
+    const bChildren = oParentNode.hasChildNodes();
+    const bAttributes = oParentNode.hasAttributes();
+    const bHighVerb = Boolean(nVerb & 2);
+
+    var sProp = 0;
+    var vContent = 0;
+    var nLength = 0;
+    var sCollectedTxt = "";
+    var vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true;
+
+    if (bChildren) {
+      for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
+        oNode = oParentNode.childNodes.item(nItem);
+        if (oNode.nodeType === 4) // CDATASection
+          sCollectedTxt += oNode.nodeValue; 
+        else if (oNode.nodeType === 3) // Text
+          sCollectedTxt += oNode.nodeValue;
+        else if (oNode.nodeType === 1) // Element
+          aCache.push(oNode);
+      }
+    }
+
+    const nLevelEnd = aCache.length;
+    const vBuiltVal = parseText(sCollectedTxt);
+
+    if (!bHighVerb && (bChildren || bAttributes))
+      vResult = nVerb === 0 ? objectify(vBuiltVal) : {};
+
+    for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
+      sProp = aCache[nElId].nodeName;
+      if (sProp == sValueProp || sProp == sAttributesProp)
+        sProp = sProp + sConflictSuffix;
+      vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
+      if (!vResult.hasOwnProperty(sProp)) {
+        vResult[sProp] = vContent;
+        vResult[sElementListPrefix + sProp] = [];
+      }
+      vResult[sElementListPrefix + sProp].push(vContent);
+      nLength++;
+    }
+
+    if (bAttributes) {
+      const nAttrLen = oParentNode.attributes.length;
+      const sAPrefix = bNesteAttr ? "" : sAttrPref;
+      const oAttrParent = bNesteAttr ? {} : vResult;
+
+      for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
+        oAttrib = oParentNode.attributes.item(nAttrib);
+        oAttrParent[sAPrefix + oAttrib.name] = parseText(oAttrib.value);
+      }
+
+      if (bNesteAttr) {
+        if (bFreeze)
+          Object.freeze(oAttrParent);
+        vResult[sAttributesProp] = oAttrParent;
+        nLength -= nAttrLen - 1;
+      }
+    }
+
+    if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt)
+      vResult[sValueProp] = vBuiltVal;
+    else if (!bHighVerb && nLength === 0 && sCollectedTxt)
+      vResult = vBuiltVal;
+
+    if (bFreeze && (bHighVerb || nLength > 0))
+      Object.freeze(vResult);
+
+    aCache.length = nLevelStart;
+
+    return vResult;
+  };
+
+  function loadObjTree(oXMLDoc, oParentEl, oParentObj) {
+    var vValue, oChild;
+
+    if (oParentObj instanceof String || oParentObj instanceof Number ||
+        oParentObj instanceof Boolean)
+      oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */
+    else if (oParentObj.constructor === Date)
+      oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));
+
+    for (var sName in oParentObj) {
+      vValue = oParentObj[sName];
+      if (isFinite(sName) || vValue instanceof Function)
+        continue; /* verbosity level is 0 */
+      if (sName === sValueProp) {
+        if (vValue !== null && vValue !== true) {
+          oParentEl.appendChild(oXMLDoc.createTextNode(
+              vValue.constructor === Date ? vValue.toGMTString() : String(vValue)));
+        }
+      } else if (sName === sAttributesProp) { /* verbosity level is 3 */
+        for (var sAttrib in vValue)
+          oParentEl.setAttribute(sAttrib, vValue[sAttrib]);
+      } else if (sName.charAt(0) === sAttrPref) {
+        oParentEl.setAttribute(sName.slice(1), vValue);
+      } else if (vValue.constructor === Array) {
+        for (var nItem = 0; nItem < vValue.length; nItem++) {
+          oChild = oXMLDoc.createElement(sName);
+          loadObjTree(oXMLDoc, oChild, vValue[nItem]);
+          oParentEl.appendChild(oChild);
+        }
+      } else {
+        oChild = oXMLDoc.createElement(sName);
+        if (vValue instanceof Object)
+          loadObjTree(oXMLDoc, oChild, vValue);
+        else if (vValue !== null && vValue !== true)
+          oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
+        oParentEl.appendChild(oChild);
+     }
+   }
+  };
+
+  this.build = function(oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) {
+    const _nVerb = arguments.length > 1 &&
+        typeof nVerbosity === "number" ? nVerbosity & 3 :
+        /* put here the default verbosity level: */ 1;
+    return createObjTree(oXMLParent, _nVerb, bFreeze || false,
+        arguments.length > 3 ? bNesteAttributes : _nVerb === 3);
+  };
+
+  this.unbuild = function(oObjTree) {
+    const oNewDoc = document.implementation.createDocument("", "", null);
+    loadObjTree(oNewDoc, oNewDoc, oObjTree);
+    return oNewDoc;
+  };
+})();
--- a/mailnews/base/util/Makefile.in
+++ b/mailnews/base/util/Makefile.in
@@ -66,16 +66,20 @@ EXTRA_JS_MODULES = \
 		templateUtils.js \
 		IOUtils.js \
 		mailnewsMigrator.js \
 		mailServices.js \
 		msgDBCacheManager.js \
 		hostnameUtils.jsm \
 		$(NULL)
 
+ifdef MOZ_THUNDERBIRD
+EXTRA_JS_MODULES += JXON.js
+endif
+
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
 
 DEFINES		+= -D_IMPL_NS_MSG_BASE
 
 ifeq ($(OS_ARCH),WINNT)
 DEFINES		+= -DZLIB_DLL
 OS_CXXFLAGS	+= -DNOMINMAX