summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/jsaccount
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--comm/mailnews/jsaccount/modules/JSAccountUtils.jsm264
-rw-r--r--comm/mailnews/jsaccount/modules/JaBaseUrl.jsm83
-rw-r--r--comm/mailnews/jsaccount/moz.build20
-rw-r--r--comm/mailnews/jsaccount/public/moz.build15
-rw-r--r--comm/mailnews/jsaccount/public/msgIDelegateList.idl19
-rw-r--r--comm/mailnews/jsaccount/public/msgIJaUrl.idl27
-rw-r--r--comm/mailnews/jsaccount/public/msgIOverride.idl42
-rw-r--r--comm/mailnews/jsaccount/readme.html67
-rw-r--r--comm/mailnews/jsaccount/src/DelegateList.cpp24
-rw-r--r--comm/mailnews/jsaccount/src/DelegateList.h48
-rw-r--r--comm/mailnews/jsaccount/src/JaAbDirectory.cpp81
-rw-r--r--comm/mailnews/jsaccount/src/JaAbDirectory.h84
-rw-r--r--comm/mailnews/jsaccount/src/JaCompose.cpp89
-rw-r--r--comm/mailnews/jsaccount/src/JaCompose.h89
-rw-r--r--comm/mailnews/jsaccount/src/JaIncomingServer.cpp96
-rw-r--r--comm/mailnews/jsaccount/src/JaIncomingServer.h95
-rw-r--r--comm/mailnews/jsaccount/src/JaMsgFolder.cpp176
-rw-r--r--comm/mailnews/jsaccount/src/JaMsgFolder.h137
-rw-r--r--comm/mailnews/jsaccount/src/JaUrl.cpp205
-rw-r--r--comm/mailnews/jsaccount/src/JaUrl.h138
-rw-r--r--comm/mailnews/jsaccount/src/components.conf37
-rw-r--r--comm/mailnews/jsaccount/src/moz.build30
-rw-r--r--comm/mailnews/jsaccount/test/components.conf14
-rw-r--r--comm/mailnews/jsaccount/test/idl/moz.build15
-rw-r--r--comm/mailnews/jsaccount/test/idl/msgIFooUrl.idl17
-rw-r--r--comm/mailnews/jsaccount/test/moz.build15
-rw-r--r--comm/mailnews/jsaccount/test/unit/head_jsaccount.js60
-rw-r--r--comm/mailnews/jsaccount/test/unit/resources/TestJaMsgProtocolInfoComponent.jsm75
-rw-r--r--comm/mailnews/jsaccount/test/unit/resources/readme.html62
-rw-r--r--comm/mailnews/jsaccount/test/unit/resources/testComponents.manifest16
-rw-r--r--comm/mailnews/jsaccount/test/unit/resources/testJaBaseIncomingServer.jsm74
-rw-r--r--comm/mailnews/jsaccount/test/unit/resources/testJaBaseIncomingServerComponent.js20
-rw-r--r--comm/mailnews/jsaccount/test/unit/resources/testJaBaseMsgFolder.jsm70
-rw-r--r--comm/mailnews/jsaccount/test/unit/resources/testJaBaseMsgFolderComponent.js19
-rw-r--r--comm/mailnews/jsaccount/test/unit/resources/testJaFooUrlComponent.js88
-rw-r--r--comm/mailnews/jsaccount/test/unit/test_componentsExist.js90
-rw-r--r--comm/mailnews/jsaccount/test/unit/test_fooUrl.js93
-rw-r--r--comm/mailnews/jsaccount/test/unit/test_jaMsgFolder.js56
-rw-r--r--comm/mailnews/jsaccount/test/unit/xpcshell.ini10
39 files changed, 2660 insertions, 0 deletions
diff --git a/comm/mailnews/jsaccount/modules/JSAccountUtils.jsm b/comm/mailnews/jsaccount/modules/JSAccountUtils.jsm
new file mode 100644
index 0000000000..adef4710e0
--- /dev/null
+++ b/comm/mailnews/jsaccount/modules/JSAccountUtils.jsm
@@ -0,0 +1,264 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* 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/. */
+
+/**
+ * This file implements helper methods to make the transition of base mailnews
+ * objects from JS to C++ easier, and also to allow creating specialized
+ * versions of those accounts using only JS XPCOM implementations.
+ *
+ * In C++ land, the XPCOM component is a generic C++ class that does nothing
+ * but delegate any calls to interfaces known in C++ to either the generic
+ * C++ implementation (such as nsMsgIncomingServer.cpp) or a JavaScript
+ * implementation of those methods. Those delegations could be used for either
+ * method-by-method replacement of the generic C++ methods with JavaScript
+ * versions, or for specialization of the generic class using JavaScript to
+ * implement a particular class type. We use a C++ class as the main XPCOM
+ * version for two related reasons: First, we do not want to go through a
+ * C++->js->C++ XPCOM transition just to execute a C++ method. Second, C++
+ * inheritance is different from JS inheritance, and sometimes the C++ code
+ * will ignore the XPCOM parts of the JS, and just execute using C++
+ * inheritance.
+ *
+ * In JavaScript land, the implementation currently uses the XPCOM object for
+ * JavaScript calls, with the last object in the prototype chain defaulting
+ * to calling using the CPP object, specified in an instance-specific
+ * this.cppBase object.
+ *
+ * Examples of use can be found in the test files for jsaccount stuff.
+ */
+
+const EXPORTED_SYMBOLS = ["JSAccountUtils"];
+var JSAccountUtils = {};
+
+// Logging usage: set mailnews.jsaccount.loglevel to the word "Debug" to
+// increase logging level.
+var log = console.createInstance({
+ prefix: "mail.jsaccount",
+ maxLogLevel: "Warn",
+ maxLogLevelPref: "mail.jsaccount.loglevel",
+});
+
+/**
+ *
+ * Generic factory to create XPCOM components under JsAccount.
+ *
+ * @param aProperties This a a const JS object that describes the specific
+ * details of a particular JsAccount XPCOM object:
+ * {
+ * baseContractID: string contractID used to create the base generic C++
+ * object. This object must implement the interfaces in
+ * baseInterfaces, plus msgIOverride.
+ *
+ * baseInterfaces: JS array of interfaces implemented by the base, generic
+ * C++ object.
+ *
+ * extraInterfaces: JS array of additional interfaces implemented by the
+ * component (accessed using getInterface())
+ *
+ * contractID: string contract ID for the JS object that will be
+ * created by the factory.
+ *
+ * classID: Components.ID(CID) for the JS object that will be
+ * created by the factory, where CID is a string uuid.
+ * }
+ *
+ * @param aJsDelegateConstructor: a JS constructor class, called using new,
+ * that will create the JS object to which
+ * XPCOM methods calls will be delegated.
+ */
+
+JSAccountUtils.jaFactory = function (aProperties, aJsDelegateConstructor) {
+ let factory = {};
+ factory.QueryInterface = ChromeUtils.generateQI([Ci.nsIFactory]);
+
+ factory.createInstance = function (iid) {
+ // C++ delegator class.
+ let delegator = Cc[aProperties.baseContractID].createInstance(
+ Ci.msgIOverride
+ );
+
+ // Make sure the delegator JS wrapper knows its interfaces.
+ aProperties.baseInterfaces.forEach(iface => delegator instanceof iface);
+
+ // JavaScript overrides of base class functions.
+ let jsDelegate = new aJsDelegateConstructor(
+ delegator,
+ aProperties.baseInterfaces
+ );
+ delegator.jsDelegate = jsDelegate;
+
+ // Get the delegate list for this current class. Use OwnProperty in case it
+ // inherits from another JsAccount class.
+
+ let delegateList = null;
+ if (Object.getPrototypeOf(jsDelegate).hasOwnProperty("delegateList")) {
+ delegateList = Object.getPrototypeOf(jsDelegate).delegateList;
+ }
+ if (delegateList instanceof Ci.msgIDelegateList) {
+ delegator.methodsToDelegate = delegateList;
+ } else {
+ // Lazily create and populate the list of methods to delegate.
+ log.info(
+ "creating delegate list for contractID " + aProperties.contractID
+ );
+ let delegateList = delegator.methodsToDelegate;
+ Object.keys(delegator).forEach(name => {
+ log.debug("delegator has key " + name);
+ });
+
+ // jsMethods contains the methods that may be targets of the C++ delegation to JS.
+ let jsMethods = Object.getPrototypeOf(jsDelegate);
+ for (let name in jsMethods) {
+ log.debug("processing jsDelegate method: " + name);
+ if (name[0] == "_") {
+ // don't bother with methods explicitly marked as internal.
+ log.debug("skipping " + name);
+ continue;
+ }
+ // Other methods to skip.
+ if (
+ [
+ "QueryInterface", // nsISupports
+ "methodsToDelegate",
+ "jsDelegate",
+ "cppBase", // msgIOverride
+ "delegateList",
+ "wrappedJSObject", // non-XPCOM methods to skip
+ ].includes(name)
+ ) {
+ log.debug("skipping " + name);
+ continue;
+ }
+
+ let jsDescriptor = getPropertyDescriptor(jsMethods, name);
+ if (!jsDescriptor) {
+ log.debug("no jsDescriptor for " + name);
+ continue;
+ }
+ let cppDescriptor = Object.getOwnPropertyDescriptor(delegator, name);
+ if (!cppDescriptor) {
+ log.debug("no cppDescriptor found for " + name);
+ // It is OK for jsMethods to have methods that are not used in override of C++.
+ continue;
+ }
+
+ let upperCaseName = name[0].toUpperCase() + name.substr(1);
+ if ("value" in jsDescriptor) {
+ log.info("delegating " + upperCaseName);
+ delegateList.add(upperCaseName);
+ } else {
+ if (jsDescriptor.set) {
+ log.info("delegating Set" + upperCaseName);
+ delegateList.add("Set" + upperCaseName);
+ }
+ if (jsDescriptor.get) {
+ log.info("delegating Get" + upperCaseName);
+ delegateList.add("Get" + upperCaseName);
+ }
+ }
+ }
+
+ // Save the delegate list for reuse, statically for all instances.
+ Object.getPrototypeOf(jsDelegate).delegateList = delegateList;
+ }
+
+ for (let iface of aProperties.baseInterfaces) {
+ if (iid.equals(iface)) {
+ log.debug("Successfully returning delegator " + delegator);
+ return delegator;
+ }
+ }
+ throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
+ };
+
+ return factory;
+};
+
+/**
+ * Create a JS object that contains calls to each of the methods in a CPP
+ * base class, that will reference the cpp object defined on a particular
+ * instance of the object. This is intended to be the last item in the
+ * prototype chain for a JsAccount implementation.
+ *
+ * @param aProperties see definition in jsFactory above
+ *
+ * @returns a JS object suitable as the prototype of a JsAccount implementation.
+ */
+JSAccountUtils.makeCppDelegator = function (aProperties) {
+ log.info("Making cppDelegator for contractID " + aProperties.contractID);
+ let cppDelegator = {};
+ let cppDummy = Cc[aProperties.baseContractID].createInstance(Ci.nsISupports);
+ // Add methods from all interfaces.
+ for (let iface of aProperties.baseInterfaces) {
+ cppDummy instanceof Ci[iface];
+ }
+
+ for (let method in cppDummy) {
+ // skip nsISupports and msgIOverride methods
+ if (
+ [
+ "QueryInterface",
+ "methodsToDelegate",
+ "jsDelegate",
+ "cppBase",
+ "getInterface",
+ ].includes(method)
+ ) {
+ log.debug("Skipping " + method);
+ continue;
+ }
+ log.debug("Processing " + method);
+ let descriptor = Object.getOwnPropertyDescriptor(cppDummy, method);
+ let property = { enumerable: true };
+ // We must use Immediately Invoked Function Expressions to pass method, otherwise it is
+ // a closure containing just the last value it was set to.
+ if ("value" in descriptor) {
+ log.debug("Adding value for " + method);
+ property.value = (function (aMethod) {
+ return function (...args) {
+ return Reflect.apply(this.cppBase[aMethod], undefined, args);
+ };
+ })(method);
+ }
+ if (descriptor.set) {
+ log.debug("Adding setter for " + method);
+ property.set = (function (aMethod) {
+ return function (aVal) {
+ this.cppBase[aMethod] = aVal;
+ };
+ })(method);
+ }
+ if (descriptor.get) {
+ log.debug("Adding getter for " + method);
+ property.get = (function (aMethod) {
+ return function () {
+ return this.cppBase[aMethod];
+ };
+ })(method);
+ }
+ Object.defineProperty(cppDelegator, method, property);
+ }
+ return cppDelegator;
+};
+
+// Utility functions.
+
+// Iterate over an object and its prototypes to get a property descriptor.
+function getPropertyDescriptor(obj, name) {
+ let descriptor = null;
+
+ // Eventually we will hit an object that will delegate JS calls to a CPP
+ // object, which are not JS overrides of CPP methods. Locate this item, and
+ // skip, because it will not have _JsPrototypeToDelegate defined.
+ while (obj && "_JsPrototypeToDelegate" in obj) {
+ descriptor = Object.getOwnPropertyDescriptor(obj, name);
+ if (descriptor) {
+ break;
+ }
+ obj = Object.getPrototypeOf(obj);
+ }
+ return descriptor;
+}
diff --git a/comm/mailnews/jsaccount/modules/JaBaseUrl.jsm b/comm/mailnews/jsaccount/modules/JaBaseUrl.jsm
new file mode 100644
index 0000000000..03f2a851d3
--- /dev/null
+++ b/comm/mailnews/jsaccount/modules/JaBaseUrl.jsm
@@ -0,0 +1,83 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* 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/. */
+
+const EXPORTED_SYMBOLS = ["JaBaseUrlProperties", "JaBaseUrl"];
+
+const { JSAccountUtils } = ChromeUtils.import(
+ "resource:///modules/jsaccount/JSAccountUtils.jsm"
+);
+
+// A partial JavaScript implementation of the base server methods.
+
+const JaBaseUrlProperties = {
+ // The CPP object that delegates to CPP or JS.
+ baseContractID: "@mozilla.org/jacppurldelegator;1",
+
+ // Interfaces implemented by the base CPP version of this object.
+ baseInterfaces: [
+ Ci.nsIURI,
+ Ci.nsIURL,
+ Ci.nsIMsgMailNewsUrl,
+ Ci.nsIMsgMessageUrl,
+ Ci.msgIOverride,
+ Ci.nsISupports,
+ Ci.nsIInterfaceRequestor,
+ ],
+ // Don't pass Ci.nsISupports to generateQI().
+ baseInterfacesQI: [
+ Ci.nsIURI,
+ Ci.nsIURL,
+ Ci.nsIMsgMailNewsUrl,
+ Ci.nsIMsgMessageUrl,
+ Ci.msgIOverride,
+ Ci.nsIInterfaceRequestor,
+ ],
+
+ // We don't typically define this as a creatable component, but if we do use
+ // these. Subclasses for particular account types require these defined for
+ // that type.
+ contractID: "@mozilla.org/jsaccount/jaurl;1",
+ classID: Components.ID("{1E7B42CA-E6D9-408F-A4E4-8D2F82AECBBD}"),
+};
+
+// Typical boilerplate to include in all implementations.
+function JaBaseUrl(aDelegator, aBaseInterfaces) {
+ // Object delegating method calls to the appropriate XPCOM object.
+ // Weak because it owns us.
+ this._delegatorWeak = Cu.getWeakReference(aDelegator);
+
+ // Base implementation of methods with no overrides.
+ this.cppBase = aDelegator.cppBase;
+
+ // cppBase class sees all interfaces
+ aBaseInterfaces.forEach(iface => this.cppBase instanceof iface);
+}
+
+// Typical boilerplate to include in all implementations.
+JaBaseUrl.prototype = {
+ __proto__: JSAccountUtils.makeCppDelegator(JaBaseUrlProperties),
+
+ // Flag this item as CPP needs to delegate to JS.
+ _JsPrototypeToDelegate: true,
+
+ // QI to the interfaces.
+ QueryInterface: ChromeUtils.generateQI(JaBaseUrlProperties.baseInterfacesQI),
+
+ // Used to access an instance as JS, bypassing XPCOM.
+ get wrappedJSObject() {
+ return this;
+ },
+
+ // Accessor to the weak cpp delegator.
+ get delegator() {
+ return this._delegatorWeak.get();
+ },
+
+ // Dynamically-generated list of delegate methods.
+ delegateList: null,
+
+ // Implementation in JS (if any) of methods in XPCOM interfaces.
+};
diff --git a/comm/mailnews/jsaccount/moz.build b/comm/mailnews/jsaccount/moz.build
new file mode 100644
index 0000000000..57cdd9d119
--- /dev/null
+++ b/comm/mailnews/jsaccount/moz.build
@@ -0,0 +1,20 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += ["public", "src"]
+
+EXTRA_JS_MODULES.jsaccount += [
+ "modules/JaBaseUrl.jsm",
+ "modules/JSAccountUtils.jsm",
+ "test/unit/resources/TestJaMsgProtocolInfoComponent.jsm",
+]
+
+XPCOM_MANIFESTS += [
+ "test/components.conf",
+]
+
+
+TEST_DIRS += ["test"]
diff --git a/comm/mailnews/jsaccount/public/moz.build b/comm/mailnews/jsaccount/public/moz.build
new file mode 100644
index 0000000000..41715534ff
--- /dev/null
+++ b/comm/mailnews/jsaccount/public/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+XPIDL_SOURCES += [
+ "msgIDelegateList.idl",
+ "msgIJaUrl.idl",
+ "msgIOverride.idl",
+]
+
+EXPORTS += []
+
+XPIDL_MODULE = "msgjsaccount"
diff --git a/comm/mailnews/jsaccount/public/msgIDelegateList.idl b/comm/mailnews/jsaccount/public/msgIDelegateList.idl
new file mode 100644
index 0000000000..0a65596206
--- /dev/null
+++ b/comm/mailnews/jsaccount/public/msgIDelegateList.idl
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#include "nsISupports.idl"
+
+/**
+ * This interface provides a list of methods that should be delegated to
+ * a JsObject rather than a C++ XPCOM base object in JsAccount classes.
+ */
+
+[scriptable, builtinclass, uuid(627D3A34-F8A3-40eb-91FE-E413D6638D27)]
+interface msgIDelegateList : nsISupports
+{
+ /// Method name to delegate to JavaScript.
+ void add(in ACString aMethod);
+};
diff --git a/comm/mailnews/jsaccount/public/msgIJaUrl.idl b/comm/mailnews/jsaccount/public/msgIJaUrl.idl
new file mode 100644
index 0000000000..41379ab239
--- /dev/null
+++ b/comm/mailnews/jsaccount/public/msgIJaUrl.idl
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#include "nsISupports.idl"
+
+/**
+ * Additional interface supported by JsBaseUrl to allow setting variables that
+ * are typically done in C++ by overriding classes, which is not allowed in JS.
+ */
+
+[scriptable, uuid(f9148c17-9b67-43bb-b741-4fdfe1aef935)]
+interface msgIJaUrl : nsISupports
+{
+ /**
+ * Set the URL type, which will be checked by nsIMsgMailNewsUrl::IsUrlType. See
+ * IsUrlType for possible values.
+ */
+ void setUrlType(in unsigned long type);
+
+ /**
+ * Set the spec on a URL.
+ */
+ void setSpec(in AUTF8String spec);
+};
diff --git a/comm/mailnews/jsaccount/public/msgIOverride.idl b/comm/mailnews/jsaccount/public/msgIOverride.idl
new file mode 100644
index 0000000000..cad1bf4157
--- /dev/null
+++ b/comm/mailnews/jsaccount/public/msgIOverride.idl
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#include "nsISupports.idl"
+#include "msgIDelegateList.idl"
+
+/**
+ * Mailnews code typically has a C++ base class for objects, which is then
+ * specialized for each account type with a C++ subclass of the base class.
+ *
+ * This interface provides the ability of JavaScript-based account
+ * implementations to use the same C++ base classes as core objects, but
+ * use JavaScript to override methods instead of C++.
+ */
+
+[scriptable, uuid(68075269-8BBD-4a09-AC04-3241BF44F633)]
+interface msgIOverride : nsISupports
+{
+ /**
+ *
+ * A list of methods in the C++ base class that will be delegated to the JS
+ * delegate. This is calculated once, and then a fixed value is set to
+ * all subsequent instances so that it does not need to be recalculated each
+ * time. If the value has not yet been set, this will return a new instance.
+ */
+ attribute msgIDelegateList methodsToDelegate;
+
+ /**
+ * JavaScript-based xpcom object that overrides C++ methods.
+ */
+ attribute nsISupports jsDelegate;
+
+ /**
+ * C++ class used to implement default functionality. This is used when
+ * JavaScript methods want to call the base class default action, bypassing a
+ * possible JS override.
+ */
+ readonly attribute nsISupports cppBase;
+};
diff --git a/comm/mailnews/jsaccount/readme.html b/comm/mailnews/jsaccount/readme.html
new file mode 100644
index 0000000000..cb1b875dbe
--- /dev/null
+++ b/comm/mailnews/jsaccount/readme.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>JsAccount Usage and Architecture</title>
+ </head>
+ <body>
+ <h1>Overview</h1>
+ <p>
+ JsAccount is a technology that allows message account types to be created
+ in Mozilla Mailnews code using JavaScript. Although this is primarily
+ targeted at allowing extensions to create new accounts, it might also be
+ useful as a bridge to convert existing account types from being C++ based
+ to JavaScript based.
+ </p>
+ <h2>Existing C++-based architecture of mailnews accounts</h2>
+ <p>
+ In mailnews code, an account type is a set of classes that allow
+ implementation of a messaging particular protocol. The account type is
+ given a short string identifier ("imap", "news", "pop3") and is then used
+ to create objects of the appropriate type by appending that string to the
+ end of a base XPCOM contractID. So, for example, to create an imap server,
+ you generate a contractID using a base ID,
+ "@mozilla.org/messenger/server;1?type=", then append "imap" to get:
+ </p>
+ <p>@mozilla.org/messenger/server;1?type=imap</p>
+ <p>
+ In the C++ code, there is a base object implementing shared functionality.
+ An account-specific class inherits that base functionality, then extends
+ it to represent the account-specific behavior that is needed. This same
+ basic concept is used to represent a whole series of classes that are
+ necessary to implement a specific mailnews account type.
+ </p>
+ <p>
+ For the server example, there is a base class named
+ nsMsgIncomingServer.cpp that implements that base interface
+ nsIMsgIncomingServer.idl. For imap, there is a specific class
+ nsImapIncomingServer.cpp that inherits from nsMsgIncomingServer.cpp,
+ overrides some of the methods in nsIMsgIncomingServer.idl, and also
+ implements an imap-specific interface nsIImapIncomingServer.idl. All of
+ this works fine using C++ inheritance and polymorphism.
+ </p>
+ <p>
+ Although JsAccount is intended mostly for mailnews accounts, the same
+ basic method of using a base class extended for specific types is also
+ used in other ways in mailnews code, including for addressbook types and
+ views. The technology may also be applied to those other object types as
+ well.
+ </p>
+ <h2>Role of JsAccount</h2>
+ <p>
+ The JavaScript class system works very differently than the C++ system,
+ and you cannot use normal language constructs to override a C++ class with
+ a JavaScript class. What JsAccount allows you to do is to create XPCOM
+ objects in JavaScript, and use those objects to override or extend the
+ methods from the C++ base class in a way that will function correctly
+ whether those objects are executed from within C++ code or JavaScript
+ code. This allows you to create a new account using JavaScript code, while
+ using the same base class functionality that is used by the core C++
+ account types. Thus a new account type may be created in JavaScript-based
+ extension. The technology may also be used to create JavaScript versions
+ of existing account types in an incremental manner, slowly converting
+ methods from C++ to JavaScript.
+ </p>
+ <p><br /></p>
+ </body>
+</html>
diff --git a/comm/mailnews/jsaccount/src/DelegateList.cpp b/comm/mailnews/jsaccount/src/DelegateList.cpp
new file mode 100644
index 0000000000..cd146fe3dc
--- /dev/null
+++ b/comm/mailnews/jsaccount/src/DelegateList.cpp
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "DelegateList.h"
+
+// This class is used within JsAccount to allow static storage of a list
+// of methods to be overridden by JS implementations, in a way that can
+// be stored and manipulated in JS, but used efficiently in C++.
+
+namespace mozilla {
+namespace mailnews {
+
+NS_IMPL_ISUPPORTS(DelegateList, msgIDelegateList)
+
+NS_IMETHODIMP DelegateList::Add(const nsACString& aMethodName) {
+ mMethods.InsertOrUpdate(aMethodName, true);
+ return NS_OK;
+}
+
+} // namespace mailnews
+} // namespace mozilla
diff --git a/comm/mailnews/jsaccount/src/DelegateList.h b/comm/mailnews/jsaccount/src/DelegateList.h
new file mode 100644
index 0000000000..f59e178f0c
--- /dev/null
+++ b/comm/mailnews/jsaccount/src/DelegateList.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef _DelegateList_H_
+#define _DelegateList_H_
+
+#include "nsISupports.h"
+#include "msgIDelegateList.h"
+#include "nsTHashMap.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace mailnews {
+
+// This class provides a list of method names to delegate to another object.
+class DelegateList : public msgIDelegateList {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_MSGIDELEGATELIST
+ DelegateList() {}
+ nsTHashMap<nsCStringHashKey, bool> mMethods;
+
+ protected:
+ virtual ~DelegateList() {}
+};
+
+} // namespace mailnews
+} // namespace mozilla
+
+/*
+ * This macro is used in forwarding functions.
+ * _jsdelegate: the name of the JS pointer that implements a particular
+ * interface.
+ * _jsmethods: the DelegateList object
+ * _cppbase: the C++ base instance (used when call not delegated to js)
+ *
+ **/
+
+#define DELEGATE_JS(_jsdelegate, _jsmethods, _cppbase) \
+ (_jsdelegate && _jsmethods && \
+ _jsmethods->Contains(nsLiteralCString(__func__)) \
+ ? _jsdelegate \
+ : (_cppbase))
+
+#endif
diff --git a/comm/mailnews/jsaccount/src/JaAbDirectory.cpp b/comm/mailnews/jsaccount/src/JaAbDirectory.cpp
new file mode 100644
index 0000000000..11b29e99ab
--- /dev/null
+++ b/comm/mailnews/jsaccount/src/JaAbDirectory.cpp
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "JaAbDirectory.h"
+#include "nsComponentManagerUtils.h"
+
+namespace mozilla {
+namespace mailnews {
+
+NS_IMPL_ISUPPORTS_INHERITED(JaBaseCppAbDirectory, nsAbDirProperty,
+ nsIInterfaceRequestor)
+
+// nsIInterfaceRequestor implementation
+NS_IMETHODIMP JaBaseCppAbDirectory::GetInterface(const nsIID& aIID,
+ void** aSink) {
+ return QueryInterface(aIID, aSink);
+}
+
+// Delegator
+NS_IMPL_ISUPPORTS_INHERITED(JaCppAbDirectoryDelegator, JaBaseCppAbDirectory,
+ msgIOverride)
+
+// Delegator object to bypass JS method override.
+NS_IMPL_ISUPPORTS(JaCppAbDirectoryDelegator::Super, nsIAbDirectory,
+ nsIInterfaceRequestor)
+
+JaCppAbDirectoryDelegator::JaCppAbDirectoryDelegator()
+ : mCppBase(new Super(this)), mMethods(nullptr) {}
+
+NS_IMETHODIMP JaCppAbDirectoryDelegator::SetMethodsToDelegate(
+ msgIDelegateList* aDelegateList) {
+ if (!aDelegateList) {
+ NS_WARNING("Null delegate list");
+ return NS_ERROR_NULL_POINTER;
+ }
+ // We static_cast since we want to use the hash object directly.
+ mDelegateList = static_cast<DelegateList*>(aDelegateList);
+ mMethods = &(mDelegateList->mMethods);
+ return NS_OK;
+}
+NS_IMETHODIMP JaCppAbDirectoryDelegator::GetMethodsToDelegate(
+ msgIDelegateList** aDelegateList) {
+ if (!mDelegateList) mDelegateList = new DelegateList();
+ mMethods = &(mDelegateList->mMethods);
+ NS_ADDREF(*aDelegateList = mDelegateList);
+ return NS_OK;
+}
+
+NS_IMETHODIMP JaCppAbDirectoryDelegator::SetJsDelegate(
+ nsISupports* aJsDelegate) {
+ // If these QIs fail, then overrides are not provided for methods in that
+ // interface, which is OK.
+ mJsISupports = aJsDelegate;
+ mJsIAbDirectory = do_QueryInterface(aJsDelegate);
+ mJsIInterfaceRequestor = do_QueryInterface(aJsDelegate);
+ return NS_OK;
+}
+NS_IMETHODIMP JaCppAbDirectoryDelegator::GetJsDelegate(
+ nsISupports** aJsDelegate) {
+ NS_ENSURE_ARG_POINTER(aJsDelegate);
+ if (mJsISupports) {
+ NS_ADDREF(*aJsDelegate = mJsISupports);
+ return NS_OK;
+ }
+ return NS_ERROR_NOT_INITIALIZED;
+}
+
+NS_IMETHODIMP JaCppAbDirectoryDelegator::GetCppBase(nsISupports** aCppBase) {
+ nsCOMPtr<nsISupports> cppBaseSupports;
+ cppBaseSupports = NS_ISUPPORTS_CAST(nsIAbDirectory*, mCppBase);
+ NS_ENSURE_STATE(cppBaseSupports);
+ cppBaseSupports.forget(aCppBase);
+
+ return NS_OK;
+}
+
+} // namespace mailnews
+} // namespace mozilla
diff --git a/comm/mailnews/jsaccount/src/JaAbDirectory.h b/comm/mailnews/jsaccount/src/JaAbDirectory.h
new file mode 100644
index 0000000000..7a2903d3d4
--- /dev/null
+++ b/comm/mailnews/jsaccount/src/JaAbDirectory.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef _JaAbDirectory_H_
+#define _JaAbDirectory_H_
+
+#include "nsISupports.h"
+#include "DelegateList.h"
+#include "msgIOverride.h"
+#include "nsIAbDirectory.h"
+#include "nsAbDirProperty.h"
+#include "nsTHashMap.h"
+#include "nsIInterfaceRequestor.h"
+
+namespace mozilla {
+namespace mailnews {
+
+/* Header file */
+
+// This class is an XPCOM component, usable in JS, that calls the methods
+// in the C++ base class (bypassing any JS override).
+class JaBaseCppAbDirectory : public nsAbDirProperty,
+ public nsIInterfaceRequestor {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIINTERFACEREQUESTOR
+ JaBaseCppAbDirectory() {}
+
+ protected:
+ virtual ~JaBaseCppAbDirectory() {}
+};
+
+class JaCppAbDirectoryDelegator : public JaBaseCppAbDirectory,
+ public msgIOverride {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_MSGIOVERRIDE
+
+ // use mCppBase as a raw pointer where possible
+ NS_FORWARD_NSIABDIRECTORY(DELEGATE_JS(mJsIAbDirectory, mMethods, mCppBase)->)
+ NS_FORWARD_NSIINTERFACEREQUESTOR(
+ DELEGATE_JS(
+ mJsIInterfaceRequestor, mMethods,
+ (nsCOMPtr<nsIInterfaceRequestor>(do_QueryInterface(mCppBase))))
+ ->)
+
+ JaCppAbDirectoryDelegator();
+
+ private:
+ virtual ~JaCppAbDirectoryDelegator() {}
+
+ class Super : public nsIAbDirectory, public nsIInterfaceRequestor {
+ public:
+ explicit Super(JaCppAbDirectoryDelegator* aFakeThis) {
+ mFakeThis = aFakeThis;
+ }
+ NS_DECL_ISUPPORTS
+ NS_FORWARD_NSIABDIRECTORY(mFakeThis->JaBaseCppAbDirectory::)
+ NS_FORWARD_NSIINTERFACEREQUESTOR(mFakeThis->JaBaseCppAbDirectory::)
+ private:
+ virtual ~Super() {}
+ JaCppAbDirectoryDelegator* mFakeThis;
+ };
+
+ // Interfaces that may be overridden by JS.
+ nsCOMPtr<nsIAbDirectory> mJsIAbDirectory;
+ nsCOMPtr<nsIInterfaceRequestor> mJsIInterfaceRequestor;
+
+ nsCOMPtr<nsISupports> mJsISupports;
+
+ // Class to bypass JS delegates. nsCOMPtr for when we do cycle collection.
+ nsCOMPtr<nsIAbDirectory> mCppBase;
+
+ RefPtr<DelegateList> mDelegateList;
+ nsTHashMap<nsCStringHashKey, bool>* mMethods;
+};
+
+} // namespace mailnews
+} // namespace mozilla
+
+#endif
diff --git a/comm/mailnews/jsaccount/src/JaCompose.cpp b/comm/mailnews/jsaccount/src/JaCompose.cpp
new file mode 100644
index 0000000000..a44dac2c01
--- /dev/null
+++ b/comm/mailnews/jsaccount/src/JaCompose.cpp
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "JaCompose.h"
+#include "nsComponentManagerUtils.h"
+
+// This file specifies the implementation of nsIMsgCompose.idl objects
+// in the JsAccount system.
+
+namespace mozilla {
+namespace mailnews {
+
+NS_IMPL_ISUPPORTS_INHERITED(JaBaseCppCompose, nsMsgCompose,
+ nsIInterfaceRequestor)
+
+// nsIInterfaceRequestor implementation
+NS_IMETHODIMP
+JaBaseCppCompose::GetInterface(const nsIID& aIID, void** aSink) {
+ return QueryInterface(aIID, aSink);
+}
+
+// Delegator object to bypass JS method override.
+
+JaCppComposeDelegator::JaCppComposeDelegator() {
+ mCppBase =
+ do_QueryInterface(NS_ISUPPORTS_CAST(nsIMsgCompose*, new Super(this)));
+ mMethods = nullptr;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(JaCppComposeDelegator, JaBaseCppCompose,
+ msgIOverride)
+
+NS_IMPL_ISUPPORTS(JaCppComposeDelegator::Super, nsIMsgCompose,
+ nsIMsgSendListener, nsIInterfaceRequestor)
+
+NS_IMETHODIMP
+JaCppComposeDelegator::SetMethodsToDelegate(msgIDelegateList* aDelegateList) {
+ if (!aDelegateList) {
+ NS_WARNING("Null delegate list");
+ return NS_ERROR_NULL_POINTER;
+ }
+ // We static_cast since we want to use the hash object directly.
+ mDelegateList = static_cast<DelegateList*>(aDelegateList);
+ mMethods = &(mDelegateList->mMethods);
+ return NS_OK;
+}
+NS_IMETHODIMP
+JaCppComposeDelegator::GetMethodsToDelegate(msgIDelegateList** aDelegateList) {
+ if (!mDelegateList) mDelegateList = new DelegateList();
+ mMethods = &(mDelegateList->mMethods);
+ NS_ADDREF(*aDelegateList = mDelegateList);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+JaCppComposeDelegator::SetJsDelegate(nsISupports* aJsDelegate) {
+ // If these QIs fail, then overrides are not provided for methods in that
+ // interface, which is OK.
+ mJsISupports = aJsDelegate;
+ mJsIMsgCompose = do_QueryInterface(aJsDelegate);
+ mJsIMsgSendListener = do_QueryInterface(aJsDelegate);
+ mJsIInterfaceRequestor = do_QueryInterface(aJsDelegate);
+ return NS_OK;
+}
+NS_IMETHODIMP
+JaCppComposeDelegator::GetJsDelegate(nsISupports** aJsDelegate) {
+ NS_ENSURE_ARG_POINTER(aJsDelegate);
+ if (mJsISupports) {
+ NS_ADDREF(*aJsDelegate = mJsISupports);
+ return NS_OK;
+ }
+ return NS_ERROR_NOT_INITIALIZED;
+}
+
+NS_IMETHODIMP
+JaCppComposeDelegator::GetCppBase(nsISupports** aCppBase) {
+ nsCOMPtr<nsISupports> cppBaseSupports;
+ cppBaseSupports = NS_ISUPPORTS_CAST(nsIMsgCompose*, mCppBase);
+ NS_ENSURE_STATE(cppBaseSupports);
+ cppBaseSupports.forget(aCppBase);
+
+ return NS_OK;
+}
+
+} // namespace mailnews
+} // namespace mozilla
diff --git a/comm/mailnews/jsaccount/src/JaCompose.h b/comm/mailnews/jsaccount/src/JaCompose.h
new file mode 100644
index 0000000000..587e91738a
--- /dev/null
+++ b/comm/mailnews/jsaccount/src/JaCompose.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef _JaCompose_H_
+#define _JaCompose_H_
+
+#include "nsISupports.h"
+#include "DelegateList.h"
+#include "msgIOverride.h"
+#include "nsMsgCompose.h"
+#include "nsIMsgCompose.h"
+#include "nsTHashMap.h"
+#include "nsIInterfaceRequestor.h"
+
+// This file specifies the definition of nsIMsgCompose.idl objects
+// in the JsAccount system.
+
+namespace mozilla {
+namespace mailnews {
+
+/* Header file */
+
+// This class is an XPCOM component, usable in JS, that calls the methods
+// in the C++ base class (bypassing any JS override).
+class JaBaseCppCompose : public nsMsgCompose, public nsIInterfaceRequestor {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIINTERFACEREQUESTOR
+ JaBaseCppCompose() {}
+
+ protected:
+ virtual ~JaBaseCppCompose() {}
+};
+
+class JaCppComposeDelegator : public JaBaseCppCompose, public msgIOverride {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_MSGIOVERRIDE
+
+ NS_FORWARD_NSIMSGCOMPOSE(DELEGATE_JS(mJsIMsgCompose, mMethods, mCppBase)->)
+ NS_FORWARD_NSIMSGSENDLISTENER(
+ DELEGATE_JS(mJsIMsgSendListener, mMethods, mCppBase.get())->)
+ NS_FORWARD_NSIINTERFACEREQUESTOR(
+ DELEGATE_JS(
+ mJsIInterfaceRequestor, mMethods,
+ (nsCOMPtr<nsIInterfaceRequestor>(do_QueryInterface(mCppBase))))
+ ->)
+
+ JaCppComposeDelegator();
+
+ private:
+ virtual ~JaCppComposeDelegator() {}
+
+ // This class will call a method on the delegator, but force the use of the
+ // C++ cppBase class, bypassing any JS Delegate.
+ class Super : public nsIMsgCompose, public nsIInterfaceRequestor {
+ public:
+ explicit Super(JaCppComposeDelegator* aFakeThis) { mFakeThis = aFakeThis; }
+ NS_DECL_ISUPPORTS
+ // Forward all overridable methods, bypassing JS override.
+ NS_FORWARD_NSIMSGCOMPOSE(mFakeThis->JaBaseCppCompose::)
+ NS_FORWARD_NSIMSGSENDLISTENER(mFakeThis->JaBaseCppCompose::)
+ NS_FORWARD_NSIINTERFACEREQUESTOR(mFakeThis->JaBaseCppCompose::)
+ private:
+ virtual ~Super(){};
+ JaCppComposeDelegator* mFakeThis;
+ };
+
+ // Interfaces that may be overridden by JS.
+ nsCOMPtr<nsIMsgCompose> mJsIMsgCompose;
+ nsCOMPtr<nsIMsgSendListener> mJsIMsgSendListener;
+ nsCOMPtr<nsIInterfaceRequestor> mJsIInterfaceRequestor;
+
+ nsCOMPtr<nsISupports> mJsISupports;
+
+ // Class to bypass JS delegates.
+ nsCOMPtr<nsIMsgCompose> mCppBase;
+
+ RefPtr<DelegateList> mDelegateList;
+ nsTHashMap<nsCStringHashKey, bool>* mMethods;
+};
+
+} // namespace mailnews
+} // namespace mozilla
+
+#endif
diff --git a/comm/mailnews/jsaccount/src/JaIncomingServer.cpp b/comm/mailnews/jsaccount/src/JaIncomingServer.cpp
new file mode 100644
index 0000000000..8acdf9c655
--- /dev/null
+++ b/comm/mailnews/jsaccount/src/JaIncomingServer.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "JaIncomingServer.h"
+#include "nsComponentManagerUtils.h"
+
+// This file specifies the implementation of nsIMsgIncomingServer.idl objects
+// in the JsAccount system.
+
+namespace mozilla {
+namespace mailnews {
+
+NS_IMPL_ISUPPORTS_INHERITED(JaBaseCppIncomingServer, nsMsgIncomingServer,
+ nsIInterfaceRequestor)
+
+// nsMsgIncomingServer overrides
+nsresult JaBaseCppIncomingServer::CreateRootFolderFromUri(
+ const nsACString& serverUri, nsIMsgFolder** rootFolder) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIInterfaceRequestor implementation
+NS_IMETHODIMP
+JaBaseCppIncomingServer::GetInterface(const nsIID& aIID, void** aSink) {
+ return QueryInterface(aIID, aSink);
+}
+
+// Delegator object to bypass JS method override.
+
+JaCppIncomingServerDelegator::JaCppIncomingServerDelegator() {
+ mCppBase = do_QueryInterface(
+ NS_ISUPPORTS_CAST(nsIMsgIncomingServer*, new Super(this)));
+ mMethods = nullptr;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(JaCppIncomingServerDelegator,
+ JaBaseCppIncomingServer, msgIOverride)
+
+NS_IMPL_ISUPPORTS(JaCppIncomingServerDelegator::Super, nsIMsgIncomingServer,
+ nsIInterfaceRequestor)
+
+NS_IMETHODIMP
+JaCppIncomingServerDelegator::SetMethodsToDelegate(
+ msgIDelegateList* aDelegateList) {
+ if (!aDelegateList) {
+ NS_WARNING("Null delegate list");
+ return NS_ERROR_NULL_POINTER;
+ }
+ // We static_cast since we want to use the hash object directly.
+ mDelegateList = static_cast<DelegateList*>(aDelegateList);
+ mMethods = &(mDelegateList->mMethods);
+ return NS_OK;
+}
+NS_IMETHODIMP
+JaCppIncomingServerDelegator::GetMethodsToDelegate(
+ msgIDelegateList** aDelegateList) {
+ if (!mDelegateList) mDelegateList = new DelegateList();
+ mMethods = &(mDelegateList->mMethods);
+ NS_ADDREF(*aDelegateList = mDelegateList);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+JaCppIncomingServerDelegator::SetJsDelegate(nsISupports* aJsDelegate) {
+ // If these QIs fail, then overrides are not provided for methods in that
+ // interface, which is OK.
+ mJsISupports = aJsDelegate;
+ mJsIMsgIncomingServer = do_QueryInterface(aJsDelegate);
+ mJsIInterfaceRequestor = do_QueryInterface(aJsDelegate);
+ return NS_OK;
+}
+NS_IMETHODIMP
+JaCppIncomingServerDelegator::GetJsDelegate(nsISupports** aJsDelegate) {
+ NS_ENSURE_ARG_POINTER(aJsDelegate);
+ if (mJsISupports) {
+ NS_ADDREF(*aJsDelegate = mJsISupports);
+ return NS_OK;
+ }
+ return NS_ERROR_NOT_INITIALIZED;
+}
+
+NS_IMETHODIMP
+JaCppIncomingServerDelegator::GetCppBase(nsISupports** aCppBase) {
+ nsCOMPtr<nsISupports> cppBaseSupports;
+ cppBaseSupports = NS_ISUPPORTS_CAST(nsIMsgIncomingServer*, mCppBase);
+ NS_ENSURE_STATE(cppBaseSupports);
+ cppBaseSupports.forget(aCppBase);
+
+ return NS_OK;
+}
+
+} // namespace mailnews
+} // namespace mozilla
diff --git a/comm/mailnews/jsaccount/src/JaIncomingServer.h b/comm/mailnews/jsaccount/src/JaIncomingServer.h
new file mode 100644
index 0000000000..67c7fd48f6
--- /dev/null
+++ b/comm/mailnews/jsaccount/src/JaIncomingServer.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef _JaIncomingServer_H_
+#define _JaIncomingServer_H_
+
+#include "nsISupports.h"
+#include "DelegateList.h"
+#include "msgIOverride.h"
+#include "nsIMsgIncomingServer.h"
+#include "nsMsgIncomingServer.h"
+#include "nsTHashMap.h"
+#include "nsIInterfaceRequestor.h"
+
+// This file specifies the definition of nsIMsgIncomingServer.idl objects
+// in the JsAccount system.
+
+namespace mozilla {
+namespace mailnews {
+
+/* Header file */
+
+// This class is an XPCOM component, usable in JS, that calls the methods
+// in the C++ base class (bypassing any JS override).
+class JaBaseCppIncomingServer : public nsMsgIncomingServer,
+ public nsIInterfaceRequestor {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIINTERFACEREQUESTOR
+ JaBaseCppIncomingServer() {}
+
+ // nsMsgIncomingServer overrides
+ nsresult CreateRootFolderFromUri(const nsACString& serverUri,
+ nsIMsgFolder** rootFolder) override;
+
+ protected:
+ virtual ~JaBaseCppIncomingServer() {}
+};
+
+class JaCppIncomingServerDelegator : public JaBaseCppIncomingServer,
+ public msgIOverride {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_MSGIOVERRIDE
+
+ // use mCppBase as a raw pointer where possible
+ NS_FORWARD_NSIMSGINCOMINGSERVER(
+ DELEGATE_JS(mJsIMsgIncomingServer, mMethods, mCppBase)->)
+ NS_FORWARD_NSIINTERFACEREQUESTOR(
+ DELEGATE_JS(
+ mJsIInterfaceRequestor, mMethods,
+ (nsCOMPtr<nsIInterfaceRequestor>(do_QueryInterface(mCppBase))))
+ ->)
+
+ JaCppIncomingServerDelegator();
+
+ private:
+ virtual ~JaCppIncomingServerDelegator() {}
+
+ // This class will call a method on the delegator, but force the use of the
+ // C++ parent class, bypassing any JS Delegate.
+ class Super : public nsIMsgIncomingServer, public nsIInterfaceRequestor {
+ public:
+ explicit Super(JaCppIncomingServerDelegator* aFakeThis) {
+ mFakeThis = aFakeThis;
+ }
+ NS_DECL_ISUPPORTS
+ // Forward all overridable methods, bypassing JS override.
+ NS_FORWARD_NSIMSGINCOMINGSERVER(mFakeThis->JaBaseCppIncomingServer::)
+ NS_FORWARD_NSIINTERFACEREQUESTOR(mFakeThis->JaBaseCppIncomingServer::)
+ private:
+ virtual ~Super(){};
+ JaCppIncomingServerDelegator* mFakeThis;
+ };
+
+ // Interfaces that may be overridden by JS.
+ nsCOMPtr<nsIMsgIncomingServer> mJsIMsgIncomingServer;
+ nsCOMPtr<nsIInterfaceRequestor> mJsIInterfaceRequestor;
+
+ nsCOMPtr<nsISupports> mJsISupports;
+
+ // Class to bypass JS delegates.
+ nsCOMPtr<nsIMsgIncomingServer> mCppBase;
+
+ RefPtr<DelegateList> mDelegateList;
+ nsTHashMap<nsCStringHashKey, bool>* mMethods;
+};
+
+} // namespace mailnews
+} // namespace mozilla
+
+#endif
diff --git a/comm/mailnews/jsaccount/src/JaMsgFolder.cpp b/comm/mailnews/jsaccount/src/JaMsgFolder.cpp
new file mode 100644
index 0000000000..e10f9f1704
--- /dev/null
+++ b/comm/mailnews/jsaccount/src/JaMsgFolder.cpp
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "JaMsgFolder.h"
+#include "nsComponentManagerUtils.h"
+
+#define MAILDATABASE_CONTRACTID_BASE "@mozilla.org/nsMsgDatabase/msgDB-"
+
+namespace mozilla {
+namespace mailnews {
+
+NS_IMPL_ISUPPORTS_INHERITED(JaBaseCppMsgFolder, nsMsgDBFolder,
+ nsIInterfaceRequestor)
+
+// nsIInterfaceRequestor implementation
+NS_IMETHODIMP
+JaBaseCppMsgFolder::GetInterface(const nsIID& aIID, void** aSink) {
+ return QueryInterface(aIID, aSink);
+}
+
+// Definition of abstract nsMsgDBFolder methods.
+nsresult JaBaseCppMsgFolder::GetDatabase() {
+ nsresult rv = NS_OK;
+ if (!mDatabase) {
+ nsCOMPtr<nsIMsgDBService> msgDBService =
+ do_GetService("@mozilla.org/msgDatabase/msgDBService;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Create the database, keeping it if it is "out of date"
+ rv = msgDBService->OpenFolderDB(this, true, getter_AddRefs(mDatabase));
+ if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING) {
+ rv = msgDBService->CreateNewDB(this, getter_AddRefs(mDatabase));
+ NS_ENSURE_STATE(mDatabase);
+ // not sure about this ... the issue is that if the summary is not valid,
+ // then the db does not get added to the cache in the future, and
+ // reindexes do not show all of the messages.
+ // mDatabase->SetSummaryValid(true);
+ mDatabase->SetSummaryValid(false);
+ CreateDummyFile(this);
+ }
+
+ if (rv != NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE)
+ NS_ENSURE_SUCCESS(rv, rv);
+ else if (mDatabase) {
+ // Not going to warn here, because on initialization we set all
+ // databases as invalid.
+ // NS_WARNING("Mail Summary database is out of date");
+ // Grrr, the only way to get this into the cache is to set the db as
+ // valid,
+ // close, reopen, then set as invalid.
+ mDatabase->SetSummaryValid(true);
+ msgDBService->ForceFolderDBClosed(this);
+ rv = msgDBService->OpenFolderDB(this, true, getter_AddRefs(mDatabase));
+ if (mDatabase) mDatabase->SetSummaryValid(false);
+ }
+
+ if (mDatabase) {
+ //
+ // When I inadvertently deleted the out-of-date database, I hit this code
+ // with the db's m_dbFolderInfo as null from the delete, yet the local
+ // mDatabase reference kept the database alive. So I hit an assert when I
+ // tried to open the database. Be careful if you try to fix the
+ // out-of-date issues!
+ //
+ // UpdateNewMessages();
+ if (mAddListener) mDatabase->AddListener(this);
+ // UpdateSummaryTotals can null mDatabase during initialization, so we
+ // save a local copy
+ nsCOMPtr<nsIMsgDatabase> database(mDatabase);
+ UpdateSummaryTotals(true);
+ mDatabase = database;
+ }
+ }
+
+ return rv;
+}
+
+/*
+ * The utility function GetSummaryFileLocation takes a folder file,
+ * then appends .msf to come up with the name of the database file. So
+ * we need a placeholder file with simply the folder name. This method
+ * creates an appropriate file as a placeholder, or you may use the file if
+ * appropriate.
+ */
+nsresult JaBaseCppMsgFolder::CreateDummyFile(nsIMsgFolder* aMailFolder) {
+ nsresult rv;
+ if (!aMailFolder) return NS_OK;
+ nsCOMPtr<nsIFile> path;
+ // need to make sure folder exists...
+ aMailFolder->GetFilePath(getter_AddRefs(path));
+ if (path) {
+ bool exists;
+ rv = path->Exists(&exists);
+ if (!exists) {
+ rv = path->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ return NS_OK;
+}
+
+// AFAICT this is unused in mailnews code.
+nsresult JaBaseCppMsgFolder::CreateChildFromURI(const nsACString& uri,
+ nsIMsgFolder** folder) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// Delegator object to bypass JS method override.
+
+JaCppMsgFolderDelegator::JaCppMsgFolderDelegator()
+ : mCppBase(new Super(this)), mMethods(nullptr) {}
+
+NS_IMPL_ISUPPORTS_INHERITED(JaCppMsgFolderDelegator, JaBaseCppMsgFolder,
+ msgIOverride)
+
+NS_IMPL_ISUPPORTS(JaCppMsgFolderDelegator::Super, nsIMsgFolder,
+ nsIDBChangeListener, nsIUrlListener,
+ nsIJunkMailClassificationListener,
+ nsIMsgTraitClassificationListener, nsIInterfaceRequestor)
+
+NS_IMETHODIMP
+JaCppMsgFolderDelegator::SetMethodsToDelegate(msgIDelegateList* aDelegateList) {
+ if (!aDelegateList) {
+ NS_WARNING("Null delegate list");
+ return NS_ERROR_NULL_POINTER;
+ }
+ // We static_cast since we want to use the hash object directly.
+ mDelegateList = static_cast<DelegateList*>(aDelegateList);
+ mMethods = &(mDelegateList->mMethods);
+ return NS_OK;
+}
+NS_IMETHODIMP
+JaCppMsgFolderDelegator::GetMethodsToDelegate(
+ msgIDelegateList** aDelegateList) {
+ if (!mDelegateList) mDelegateList = new DelegateList();
+ mMethods = &(mDelegateList->mMethods);
+ NS_ADDREF(*aDelegateList = mDelegateList);
+ return NS_OK;
+}
+
+NS_IMETHODIMP JaCppMsgFolderDelegator::SetJsDelegate(nsISupports* aJsDelegate) {
+ // If these QIs fail, then overrides are not provided for methods in that
+ // interface, which is OK.
+ mJsISupports = aJsDelegate;
+ mJsIMsgFolder = do_QueryInterface(aJsDelegate);
+ mJsIDBChangeListener = do_QueryInterface(aJsDelegate);
+ mJsIUrlListener = do_QueryInterface(aJsDelegate);
+ mJsIJunkMailClassificationListener = do_QueryInterface(aJsDelegate);
+ mJsIMsgTraitClassificationListener = do_QueryInterface(aJsDelegate);
+ mJsIInterfaceRequestor = do_QueryInterface(aJsDelegate);
+ return NS_OK;
+}
+NS_IMETHODIMP JaCppMsgFolderDelegator::GetJsDelegate(
+ nsISupports** aJsDelegate) {
+ NS_ENSURE_ARG_POINTER(aJsDelegate);
+ if (mJsISupports) {
+ NS_ADDREF(*aJsDelegate = mJsISupports);
+ return NS_OK;
+ }
+ return NS_ERROR_NOT_INITIALIZED;
+}
+
+NS_IMETHODIMP JaCppMsgFolderDelegator::GetCppBase(nsISupports** aCppBase) {
+ nsCOMPtr<nsISupports> cppBaseSupports;
+ cppBaseSupports = NS_ISUPPORTS_CAST(nsIMsgFolder*, mCppBase);
+ NS_ENSURE_STATE(cppBaseSupports);
+ cppBaseSupports.forget(aCppBase);
+
+ return NS_OK;
+}
+
+} // namespace mailnews
+} // namespace mozilla
diff --git a/comm/mailnews/jsaccount/src/JaMsgFolder.h b/comm/mailnews/jsaccount/src/JaMsgFolder.h
new file mode 100644
index 0000000000..97cdfce5e6
--- /dev/null
+++ b/comm/mailnews/jsaccount/src/JaMsgFolder.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef _JaMsgFolder_H_
+#define _JaMsgFolder_H_
+
+#include "nsISupports.h"
+#include "DelegateList.h"
+#include "msgIOverride.h"
+#include "nsIMsgFolder.h"
+#include "nsMsgDBFolder.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsTHashMap.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsNetUtil.h"
+#include "nsIDBChangeListener.h"
+#include "nsIUrlListener.h"
+#include "nsIMsgFilterPlugin.h"
+#include "nsIInterfaceRequestor.h"
+
+namespace mozilla {
+namespace mailnews {
+
+/* Header file */
+
+// This class is an XPCOM component, usable in JS, that calls the methods
+// in the C++ base class (bypassing any JS override).
+class JaBaseCppMsgFolder : public nsMsgDBFolder,
+ public nsIInterfaceRequestor
+
+{
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIINTERFACEREQUESTOR
+ JaBaseCppMsgFolder() {}
+
+ // nsMsgDBFolder overrides
+
+ nsresult CreateChildFromURI(const nsACString& uri,
+ nsIMsgFolder** folder) override;
+ nsresult GetDatabase() override;
+
+ // Local Utility Functions
+
+ // Create a placeholder file to represent a folder.
+ nsresult CreateDummyFile(nsIMsgFolder* aMailFolder);
+
+ protected:
+ virtual ~JaBaseCppMsgFolder() {}
+};
+
+class JaCppMsgFolderDelegator : public JaBaseCppMsgFolder, public msgIOverride {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_MSGIOVERRIDE
+
+ NS_FORWARD_NSIMSGFOLDER(DELEGATE_JS(mJsIMsgFolder, mMethods, mCppBase)->)
+ NS_FORWARD_NSIDBCHANGELISTENER(
+ DELEGATE_JS(mJsIDBChangeListener, mMethods,
+ (nsCOMPtr<nsIDBChangeListener>(do_QueryInterface(mCppBase))))
+ ->)
+ NS_FORWARD_NSIURLLISTENER(
+ DELEGATE_JS(mJsIUrlListener, mMethods,
+ (nsCOMPtr<nsIUrlListener>(do_QueryInterface(mCppBase))))
+ ->)
+ NS_FORWARD_NSIJUNKMAILCLASSIFICATIONLISTENER(
+ DELEGATE_JS(mJsIJunkMailClassificationListener, mMethods,
+ (nsCOMPtr<nsIJunkMailClassificationListener>(
+ do_QueryInterface(mCppBase))))
+ ->)
+ NS_FORWARD_NSIMSGTRAITCLASSIFICATIONLISTENER(
+ DELEGATE_JS(mJsIMsgTraitClassificationListener, mMethods,
+ (nsCOMPtr<nsIMsgTraitClassificationListener>(
+ do_QueryInterface(mCppBase))))
+ ->)
+ NS_FORWARD_NSIINTERFACEREQUESTOR(
+ DELEGATE_JS(
+ mJsIInterfaceRequestor, mMethods,
+ (nsCOMPtr<nsIInterfaceRequestor>(do_QueryInterface(mCppBase))))
+ ->)
+
+ JaCppMsgFolderDelegator();
+
+ private:
+ virtual ~JaCppMsgFolderDelegator() {}
+
+ class Super : public nsIMsgFolder,
+ public nsIDBChangeListener,
+ public nsIUrlListener,
+ public nsIJunkMailClassificationListener,
+ public nsIMsgTraitClassificationListener,
+ public nsIInterfaceRequestor {
+ public:
+ // Why fake this? Because this method is fully owned by
+ // JaCppMsgFolderDelegator, and this reference is to the "this" of the
+ // main method. But it is not really the local "this".
+ explicit Super(JaCppMsgFolderDelegator* aFakeThis) {
+ mFakeThis = aFakeThis;
+ }
+ NS_DECL_ISUPPORTS
+ NS_FORWARD_NSIMSGFOLDER(mFakeThis->JaBaseCppMsgFolder::)
+ NS_FORWARD_NSIDBCHANGELISTENER(mFakeThis->JaBaseCppMsgFolder::)
+ NS_FORWARD_NSIURLLISTENER(mFakeThis->JaBaseCppMsgFolder::)
+ NS_FORWARD_NSIJUNKMAILCLASSIFICATIONLISTENER(
+ mFakeThis->JaBaseCppMsgFolder::)
+ NS_FORWARD_NSIMSGTRAITCLASSIFICATIONLISTENER(
+ mFakeThis->JaBaseCppMsgFolder::)
+ NS_FORWARD_NSIINTERFACEREQUESTOR(mFakeThis->JaBaseCppMsgFolder::)
+ private:
+ virtual ~Super() {}
+ JaCppMsgFolderDelegator* mFakeThis;
+ };
+
+ // Interfaces that may be overridden by JS.
+ nsCOMPtr<nsIMsgFolder> mJsIMsgFolder;
+ nsCOMPtr<nsIDBChangeListener> mJsIDBChangeListener;
+ nsCOMPtr<nsIUrlListener> mJsIUrlListener;
+ nsCOMPtr<nsIJunkMailClassificationListener>
+ mJsIJunkMailClassificationListener;
+ nsCOMPtr<nsIMsgTraitClassificationListener>
+ mJsIMsgTraitClassificationListener;
+ nsCOMPtr<nsIInterfaceRequestor> mJsIInterfaceRequestor;
+
+ nsCOMPtr<nsISupports> mJsISupports;
+
+ nsCOMPtr<nsIMsgFolder> mCppBase;
+ RefPtr<DelegateList> mDelegateList;
+ nsTHashMap<nsCStringHashKey, bool>* mMethods;
+};
+
+} // namespace mailnews
+} // namespace mozilla
+
+#endif
diff --git a/comm/mailnews/jsaccount/src/JaUrl.cpp b/comm/mailnews/jsaccount/src/JaUrl.cpp
new file mode 100644
index 0000000000..249c45c9df
--- /dev/null
+++ b/comm/mailnews/jsaccount/src/JaUrl.cpp
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "JaUrl.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIFile.h"
+#include "nsIMessenger.h"
+#include "nsIMsgHdr.h"
+#include "nsMsgUtils.h"
+
+// This file contains an implementation of mailnews URLs in JsAccount.
+
+namespace mozilla {
+namespace mailnews {
+
+NS_IMPL_ISUPPORTS_INHERITED(JaBaseCppUrl, nsMsgMailNewsUrl, nsIMsgMessageUrl,
+ msgIJaUrl, nsIInterfaceRequestor,
+ nsISupportsWeakReference)
+
+// nsIMsgMailNewsUrl overrides
+NS_IMETHODIMP JaBaseCppUrl::GetFolder(nsIMsgFolder** aFolder) {
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_IF_ADDREF(*aFolder = mFolder);
+ return NS_OK;
+}
+
+NS_IMETHODIMP JaBaseCppUrl::SetFolder(nsIMsgFolder* aFolder) {
+ mFolder = aFolder;
+ return NS_OK;
+}
+
+NS_IMETHODIMP JaBaseCppUrl::GetServer(nsIMsgIncomingServer** aIncomingServer) {
+ if (mFolder) {
+ return mFolder->GetServer(aIncomingServer);
+ }
+ return NS_ERROR_NOT_INITIALIZED;
+}
+
+NS_IMETHODIMP JaBaseCppUrl::IsUrlType(uint32_t type, bool* isType) {
+ NS_ENSURE_ARG(isType);
+ *isType = (m_urlType == type);
+ return NS_OK;
+}
+
+// nsIMsgMessageUrl implementation
+NS_IMETHODIMP JaBaseCppUrl::GetUri(nsACString& aUri) {
+ if (!mUri.IsEmpty())
+ aUri = mUri;
+ else
+ return NS_ERROR_NOT_INITIALIZED;
+ return NS_OK;
+}
+NS_IMETHODIMP JaBaseCppUrl::SetUri(const nsACString& aUri) {
+ mUri = aUri;
+ return NS_OK;
+}
+
+NS_IMETHODIMP JaBaseCppUrl::GetMessageFile(nsIFile** aMessageFile) {
+ NS_ENSURE_ARG_POINTER(aMessageFile);
+ NS_IF_ADDREF(*aMessageFile = mMessageFile);
+ return NS_OK;
+}
+NS_IMETHODIMP JaBaseCppUrl::SetMessageFile(nsIFile* aMessageFile) {
+ mMessageFile = aMessageFile;
+ return NS_OK;
+}
+
+NS_IMETHODIMP JaBaseCppUrl::GetAddDummyEnvelope(bool* aAddDummyEnvelope) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+NS_IMETHODIMP JaBaseCppUrl::SetAddDummyEnvelope(bool aAddDummyEnvelope) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP JaBaseCppUrl::GetCanonicalLineEnding(bool* aCanonicalLineEnding) {
+ NS_ENSURE_ARG_POINTER(aCanonicalLineEnding);
+ *aCanonicalLineEnding = mCanonicalLineEnding;
+ return NS_OK;
+}
+NS_IMETHODIMP JaBaseCppUrl::SetCanonicalLineEnding(bool aCanonicalLineEnding) {
+ mCanonicalLineEnding = aCanonicalLineEnding;
+ return NS_OK;
+}
+
+NS_IMETHODIMP JaBaseCppUrl::GetOriginalSpec(nsACString& aOriginalSpec) {
+ if (mOriginalSpec.IsEmpty()) return NS_ERROR_NULL_POINTER;
+ aOriginalSpec = mOriginalSpec;
+ return NS_OK;
+}
+NS_IMETHODIMP JaBaseCppUrl::SetOriginalSpec(const nsACString& aOriginalSpec) {
+ mOriginalSpec = aOriginalSpec;
+ return NS_OK;
+}
+
+NS_IMETHODIMP JaBaseCppUrl::GetNormalizedSpec(nsACString& aPrincipalSpec) {
+ // URLs contain a lot of query parts. We want need a normalized form:
+ // scheme://server/folder?number=123
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsURL;
+ QueryInterface(NS_GET_IID(nsIMsgMailNewsUrl), getter_AddRefs(mailnewsURL));
+
+ nsAutoCString spec;
+ mailnewsURL->GetSpecIgnoringRef(spec);
+
+ nsCString queryPart = MsgExtractQueryPart(spec, "number=");
+
+ // Strip any query part beginning with ? or /;
+ MsgRemoveQueryPart(spec);
+
+ if (!queryPart.IsEmpty()) spec += "?"_ns + queryPart;
+
+ aPrincipalSpec.Assign(spec);
+ return NS_OK;
+}
+
+NS_IMETHODIMP JaBaseCppUrl::GetMessageHeader(nsIMsgDBHdr** aMessageHeader) {
+ // This routine does a lookup using messenger, assuming that the message URI
+ // has been set in mUri.
+ NS_ENSURE_TRUE(!mUri.IsEmpty(), NS_ERROR_NOT_INITIALIZED);
+ nsresult rv;
+ nsCOMPtr<nsIMessenger> messenger(
+ do_CreateInstance("@mozilla.org/messenger;1", &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ rv = messenger->MsgHdrFromURI(mUri, getter_AddRefs(msgHdr));
+ NS_ENSURE_SUCCESS(rv, rv);
+ msgHdr.forget(aMessageHeader);
+ return NS_OK;
+}
+
+// msgIJaUrl implementation
+NS_IMETHODIMP JaBaseCppUrl::SetUrlType(unsigned int type) {
+ m_urlType = type;
+ return NS_OK;
+}
+
+NS_IMETHODIMP JaBaseCppUrl::SetSpec(const nsACString& aSpec) {
+ return SetSpecInternal(aSpec);
+}
+
+// nsIInterfaceRequestor implementation
+NS_IMETHODIMP JaBaseCppUrl::GetInterface(const nsIID& aIID, void** aSink) {
+ return QueryInterface(aIID, aSink);
+}
+
+// Delegator
+NS_IMPL_ISUPPORTS_INHERITED(JaCppUrlDelegator, JaBaseCppUrl, msgIOverride)
+
+// Delegator object to bypass JS method override.
+NS_IMPL_ISUPPORTS(JaCppUrlDelegator::Super, nsIMsgMessageUrl, nsIURI, nsIURL,
+ nsIURIWithSpecialOrigin, nsIMsgMailNewsUrl, msgIJaUrl,
+ nsIInterfaceRequestor, nsISupportsWeakReference)
+
+JaCppUrlDelegator::JaCppUrlDelegator()
+ : mCppBase(new Super(this)), mMethods(nullptr) {}
+
+NS_IMETHODIMP JaCppUrlDelegator::SetMethodsToDelegate(
+ msgIDelegateList* aDelegateList) {
+ if (!aDelegateList) {
+ NS_WARNING("Null delegate list");
+ return NS_ERROR_NULL_POINTER;
+ }
+ // We static_cast since we want to use the hash object directly.
+ mDelegateList = static_cast<DelegateList*>(aDelegateList);
+ mMethods = &(mDelegateList->mMethods);
+ return NS_OK;
+}
+NS_IMETHODIMP JaCppUrlDelegator::GetMethodsToDelegate(
+ msgIDelegateList** aDelegateList) {
+ if (!mDelegateList) mDelegateList = new DelegateList();
+ mMethods = &(mDelegateList->mMethods);
+ NS_ADDREF(*aDelegateList = mDelegateList);
+ return NS_OK;
+}
+
+NS_IMETHODIMP JaCppUrlDelegator::SetJsDelegate(nsISupports* aJsDelegate) {
+ // If these QIs fail, then overrides are not provided for methods in that
+ // interface, which is OK.
+ mJsISupports = aJsDelegate;
+ mJsIMsgMessageUrl = do_QueryInterface(aJsDelegate);
+ mJsIInterfaceRequestor = do_QueryInterface(aJsDelegate);
+ return NS_OK;
+}
+NS_IMETHODIMP JaCppUrlDelegator::GetJsDelegate(nsISupports** aJsDelegate) {
+ NS_ENSURE_ARG_POINTER(aJsDelegate);
+ if (mJsISupports) {
+ NS_ADDREF(*aJsDelegate = mJsISupports);
+ return NS_OK;
+ }
+ return NS_ERROR_NOT_INITIALIZED;
+}
+
+NS_IMETHODIMP JaCppUrlDelegator::GetCppBase(nsISupports** aCppBase) {
+ nsCOMPtr<nsISupports> cppBaseSupports;
+ cppBaseSupports = NS_ISUPPORTS_CAST(nsIMsgMailNewsUrl*, mCppBase);
+ NS_ENSURE_STATE(cppBaseSupports);
+ cppBaseSupports.forget(aCppBase);
+
+ return NS_OK;
+}
+
+} // namespace mailnews
+} // namespace mozilla
diff --git a/comm/mailnews/jsaccount/src/JaUrl.h b/comm/mailnews/jsaccount/src/JaUrl.h
new file mode 100644
index 0000000000..6de4feba60
--- /dev/null
+++ b/comm/mailnews/jsaccount/src/JaUrl.h
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef _JaUrl_H_
+#define _JaUrl_H_
+
+#include "DelegateList.h"
+#include "msgCore.h"
+#include "msgIOverride.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsTHashMap.h"
+#include "nsIFile.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIMsgFolder.h"
+#include "nsISupports.h"
+#include "nsIURI.h"
+#include "nsIURL.h"
+#include "nsMsgMailNewsUrl.h"
+#include "nsWeakReference.h"
+#include "msgIJaUrl.h"
+#include "nsProxyRelease.h"
+
+namespace mozilla {
+namespace mailnews {
+
+/* Header file */
+
+// This class is an XPCOM component, usable in JS, that calls the methods
+// in the C++ base class (bypassing any JS override).
+class JaBaseCppUrl : public nsMsgMailNewsUrl,
+ public nsIMsgMessageUrl,
+ public msgIJaUrl,
+ public nsIInterfaceRequestor,
+ public nsSupportsWeakReference {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIMSGMESSAGEURL
+ NS_DECL_MSGIJAURL
+ NS_DECL_NSIINTERFACEREQUESTOR
+ JaBaseCppUrl() {}
+
+ // nsIMsgMailNewsUrl overrides
+ NS_IMETHOD GetFolder(nsIMsgFolder** aFolder) override;
+ NS_IMETHOD SetFolder(nsIMsgFolder* aFolder) override;
+ NS_IMETHOD IsUrlType(uint32_t type, bool* isType) override;
+ NS_IMETHOD GetServer(nsIMsgIncomingServer** aIncomingServer) override;
+
+ protected:
+ virtual ~JaBaseCppUrl() {}
+
+ // nsIMsgMailUrl variables.
+
+ nsCOMPtr<nsIMsgFolder> mFolder;
+
+ // nsIMsgMessageUrl variables.
+
+ // the uri for the original message, like ews-message://server/folder#123
+ nsCString mUri;
+ nsCOMPtr<nsIFile> mMessageFile;
+ bool mCanonicalLineEnding = false;
+ nsCString mOriginalSpec;
+
+ // msgIJaUrl variables
+ unsigned int m_urlType{0};
+};
+
+class JaCppUrlDelegator : public JaBaseCppUrl, public msgIOverride {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_MSGIOVERRIDE
+
+ NS_FORWARD_NSIMSGMESSAGEURL(
+ DELEGATE_JS(mJsIMsgMessageUrl, mMethods,
+ (nsCOMPtr<nsIMsgMessageUrl>(do_QueryInterface(mCppBase))))
+ ->)
+ NS_FORWARD_NSIINTERFACEREQUESTOR(
+ DELEGATE_JS(
+ mJsIInterfaceRequestor, mMethods,
+ (nsCOMPtr<nsIInterfaceRequestor>(do_QueryInterface(mCppBase))))
+ ->)
+
+ JaCppUrlDelegator();
+
+ class Super : public nsIMsgMailNewsUrl,
+ public nsIURIWithSpecialOrigin,
+ public nsIMsgMessageUrl,
+ public msgIJaUrl,
+ public nsIInterfaceRequestor,
+ public nsISupportsWeakReference {
+ public:
+ explicit Super(JaCppUrlDelegator* aFakeThis) { mFakeThis = aFakeThis; }
+ NS_DECL_ISUPPORTS
+ NS_FORWARD_NSIMSGMAILNEWSURL(mFakeThis->JaBaseCppUrl::)
+ NS_FORWARD_NSIURI(mFakeThis->JaBaseCppUrl::)
+ NS_FORWARD_NSIURL(mFakeThis->JaBaseCppUrl::)
+ NS_FORWARD_NSIURIWITHSPECIALORIGIN(mFakeThis->JaBaseCppUrl::)
+ NS_FORWARD_NSIMSGMESSAGEURL(mFakeThis->JaBaseCppUrl::)
+ NS_FORWARD_MSGIJAURL(mFakeThis->JaBaseCppUrl::)
+ NS_FORWARD_NSIINTERFACEREQUESTOR(mFakeThis->JaBaseCppUrl::)
+ NS_FORWARD_NSISUPPORTSWEAKREFERENCE(mFakeThis->JaBaseCppUrl::)
+ private:
+ virtual ~Super() {}
+ JaCppUrlDelegator* mFakeThis;
+ };
+
+ private:
+ virtual ~JaCppUrlDelegator() {
+ NS_ReleaseOnMainThread("JaCppUrlDelegator::mJsIMsgMessageUrl",
+ mJsIMsgMessageUrl.forget());
+ NS_ReleaseOnMainThread("JaCppUrlDelegator::mJsIInterfaceRequestor",
+ mJsIInterfaceRequestor.forget());
+ NS_ReleaseOnMainThread("JaCppUrlDelegator::mJsISupports",
+ mJsISupports.forget());
+ NS_ReleaseOnMainThread("JaCppUrlDelegator::mDelegateList",
+ mDelegateList.forget());
+ }
+
+ // Interfaces that may be overridden by JS.
+ nsCOMPtr<nsIMsgMessageUrl> mJsIMsgMessageUrl;
+ nsCOMPtr<nsIInterfaceRequestor> mJsIInterfaceRequestor;
+
+ // Owning reference to the JS override.
+ nsCOMPtr<nsISupports> mJsISupports;
+
+ // Class to bypass JS delegates. nsCOMPtr for when we do cycle collection.
+ nsCOMPtr<nsIMsgMailNewsUrl> mCppBase;
+
+ RefPtr<DelegateList> mDelegateList;
+ nsTHashMap<nsCStringHashKey, bool>* mMethods;
+};
+
+} // namespace mailnews
+} // namespace mozilla
+
+#endif
diff --git a/comm/mailnews/jsaccount/src/components.conf b/comm/mailnews/jsaccount/src/components.conf
new file mode 100644
index 0000000000..89b4543316
--- /dev/null
+++ b/comm/mailnews/jsaccount/src/components.conf
@@ -0,0 +1,37 @@
+# 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/.
+
+Classes = [
+ {
+ "cid": "{77b5592c-5018-436d-a466-c4e5443a1669}",
+ "contract_ids": ["@mozilla.org/jacppabdirectorydelegator;1"],
+ "type": "mailnews::JaCppAbDirectoryDelegator",
+ "headers": ["/comm/mailnews/jsaccount/src/JaAbDirectory.h"],
+ },
+ {
+ "cid": "{cfcd1caa-00d9-40d0-831e-673820e04fc6}",
+ "contract_ids": ["@mozilla.org/jacppcomposedelegator;1"],
+ "type": "mailnews::JaCppComposeDelegator",
+ "headers": ["/comm/mailnews/jsaccount/src/JaCompose.h"],
+ },
+ {
+ "cid": "{7aa11dd3-5590-4e01-bd87-91f60272d01a}",
+ "contract_ids": ["@mozilla.org/jacppincomingserverdelegator;1"],
+ "type": "mailnews::JaCppIncomingServerDelegator",
+ "init_method": "Init",
+ "headers": ["/comm/mailnews/jsaccount/src/JaIncomingServer.h"],
+ },
+ {
+ "cid": "{d6bd81fa-b1d4-424a-88ea-bb3ea8381d50}",
+ "contract_ids": ["@mozilla.org/jacppmsgfolderdelegator;1"],
+ "type": "mailnews::JaCppMsgFolderDelegator",
+ "headers": ["/comm/mailnews/jsaccount/src/JaMsgFolder.h"],
+ },
+ {
+ "cid": "{1a0b778c-2fe6-4012-b4f3-e81c0c116409}",
+ "contract_ids": ["@mozilla.org/jacppurldelegator;1"],
+ "type": "mailnews::JaCppUrlDelegator",
+ "headers": ["/comm/mailnews/jsaccount/src/JaUrl.h"],
+ },
+]
diff --git a/comm/mailnews/jsaccount/src/moz.build b/comm/mailnews/jsaccount/src/moz.build
new file mode 100644
index 0000000000..a78cb5c1f7
--- /dev/null
+++ b/comm/mailnews/jsaccount/src/moz.build
@@ -0,0 +1,30 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ "DelegateList.cpp",
+ "JaAbDirectory.cpp",
+ "JaCompose.cpp",
+ "JaIncomingServer.cpp",
+ "JaMsgFolder.cpp",
+ "JaUrl.cpp",
+]
+
+EXPORTS += [
+ "DelegateList.h",
+ "JaAbDirectory.h",
+ "JaCompose.h",
+ "JaIncomingServer.h",
+ "JaMsgFolder.h",
+ "JaUrl.h",
+]
+
+Library("JsAccount")
+FINAL_LIBRARY = "mail"
+
+XPCOM_MANIFESTS += [
+ "components.conf",
+]
diff --git a/comm/mailnews/jsaccount/test/components.conf b/comm/mailnews/jsaccount/test/components.conf
new file mode 100644
index 0000000000..63e26224b3
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/components.conf
@@ -0,0 +1,14 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+ {
+ "cid": "{74b9b9c3-9594-41c4-b9f0-326e5daac2e0}",
+ "contract_ids": ["@mozilla.org/messenger/protocol/info;1?type=testja"],
+ "jsm": "resource:///modules/jsaccount/TestJaMsgProtocolInfoComponent.jsm",
+ "constructor": "TestJaMsgProtocolInfo",
+ },
+]
diff --git a/comm/mailnews/jsaccount/test/idl/moz.build b/comm/mailnews/jsaccount/test/idl/moz.build
new file mode 100644
index 0000000000..9975e6125b
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/idl/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+XPIDL_SOURCES += [
+ "msgIFooUrl.idl",
+]
+
+XPIDL_MODULE = "testJsAccount"
+
+if "comm" in CONFIG["MOZ_BUILD_APP"]:
+ test_harness_base = TEST_HARNESS_FILES.xpcshell.comm
+else:
+ test_harness_base = TEST_HARNESS_FILES.xpcshell
diff --git a/comm/mailnews/jsaccount/test/idl/msgIFooUrl.idl b/comm/mailnews/jsaccount/test/idl/msgIFooUrl.idl
new file mode 100644
index 0000000000..9652a27cc5
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/idl/msgIFooUrl.idl
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This is a sample test interface implemented by the URL object.
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(12CAD9FC-57FC-4AEE-A800-895A289237DD)]
+interface msgIFooUrl : nsISupports
+{
+ /// Foo id for item.
+ attribute AString itemId;
+ /// Does this url refer to an attachment?
+ readonly attribute boolean isAttachment;
+};
diff --git a/comm/mailnews/jsaccount/test/moz.build b/comm/mailnews/jsaccount/test/moz.build
new file mode 100644
index 0000000000..a3af38d313
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+TEST_DIRS += [
+ "idl",
+]
+
+XPCSHELL_TESTS_MANIFESTS += ["unit/xpcshell.ini"]
+
+TESTING_JS_MODULES.mailnews += [
+ "unit/resources/testJaBaseIncomingServer.jsm",
+ "unit/resources/testJaBaseMsgFolder.jsm",
+]
diff --git a/comm/mailnews/jsaccount/test/unit/head_jsaccount.js b/comm/mailnews/jsaccount/test/unit/head_jsaccount.js
new file mode 100644
index 0000000000..a3c37dea1f
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/unit/head_jsaccount.js
@@ -0,0 +1,60 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var CC = Components.Constructor;
+
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+var { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+var { mailTestUtils } = ChromeUtils.import(
+ "resource://testing-common/mailnews/MailTestUtils.jsm"
+);
+var { localAccountUtils } = ChromeUtils.import(
+ "resource://testing-common/mailnews/LocalAccountUtils.jsm"
+);
+
+// Load the test components.
+let contracts = [
+ {
+ contractID: "@mozilla.org/jsaccount/testjafoourl;1",
+ classID: "{73F98539-A59F-4F6F-9A72-D83A08646C23}",
+ source: "resources/testJaFooUrlComponent.js",
+ },
+ {
+ contractID: "@mozilla.org/mail/folder-factory;1?name=testja",
+ classID: "{8508ddeb-3eab-4877-a420-297518f62371}",
+ source: "resources/testJaBaseMsgFolderComponent.js",
+ },
+ {
+ contractID: "@mozilla.org/messenger/server;1?type=testja",
+ classID: "{0eec03cd-da67-4949-ab2d-5fa4bdc68135}",
+ source: "resources/testJaBaseIncomingServerComponent.js",
+ },
+];
+
+let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+for (let { contractID, classID, source } of contracts) {
+ let scope = {};
+ Services.scriptloader.loadSubScript(
+ Services.io.newFileURI(do_get_file(source)).spec,
+ scope
+ );
+ registrar.registerFactory(
+ Components.ID(classID),
+ "",
+ contractID,
+ scope.xpcomFactory
+ );
+}
+
+// Ensure the profile directory is set up.
+do_get_profile();
+
+registerCleanupFunction(function () {
+ load("../../../../mailnews/resources/mailShutdown.js");
+});
diff --git a/comm/mailnews/jsaccount/test/unit/resources/TestJaMsgProtocolInfoComponent.jsm b/comm/mailnews/jsaccount/test/unit/resources/TestJaMsgProtocolInfoComponent.jsm
new file mode 100644
index 0000000000..555f395220
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/unit/resources/TestJaMsgProtocolInfoComponent.jsm
@@ -0,0 +1,75 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This file is the component definition for a demo base implementation of a
+// javascript nsIMsgProtocolInfo implementation.
+
+var EXPORTED_SYMBOLS = ["TestJaMsgProtocolInfo"];
+
+function TestJaMsgProtocolInfo() {
+ dump("testJaMsgProtocolInfo");
+ // nsIFile object to be used for the default local path.
+ this._defaultLocalPath = null;
+}
+
+TestJaMsgProtocolInfo.prototype = {
+ // Flag this item as CPP needs to delegate to JS.
+ _JsPrototypeToDelegate: true,
+
+ get defaultLocalPath() {
+ if (this._defaultLocalPath) {
+ return this._defaultLocalPath;
+ }
+ // Setup a default location, "TestFoo" directory in profile.
+ const NS_APP_USER_PROFILE_50_DIR = "ProfD";
+ let typedir = Services.dirsvc.get(NS_APP_USER_PROFILE_50_DIR, Ci.nsIFile);
+ typedir.append("TestFoo");
+ if (!typedir.exists()) {
+ typedir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0700", 8));
+ }
+ this._defaultLocalPath = typedir;
+ return typedir;
+ },
+ set defaultLocalPath(defaultLocalPath) {
+ this._defaultLocalPath = defaultLocalPath;
+ },
+ // serverIID is used in AccountWizard.js, if missing will just report an error.
+ get serverIID() {
+ return null;
+ },
+ get requiresUsername() {
+ return false;
+ },
+ get preflightPrettyNameWithEmailAddress() {
+ return false;
+ },
+ get canDelete() {
+ return true;
+ },
+ get canLoginAtStartUp() {
+ return false;
+ },
+ get canDuplicate() {
+ return false;
+ },
+ getDefaultServerPort: isSecure => 0,
+ get canGetMessages() {
+ return false;
+ },
+ get canGetIncomingMessages() {
+ return false;
+ },
+ get defaultDoBiff() {
+ return false;
+ },
+ get showComposeMsgLink() {
+ return false;
+ },
+ get foldersCreatedAsync() {
+ return false;
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIMsgProtocolInfo"]),
+};
diff --git a/comm/mailnews/jsaccount/test/unit/resources/readme.html b/comm/mailnews/jsaccount/test/unit/resources/readme.html
new file mode 100644
index 0000000000..e5490e7d41
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/unit/resources/readme.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <title>JsAccount Usage and Architecture</title>
+ </head>
+ <body>
+ <h2>Overview of Testing Objects </h2>
+ This directory contains sample JS components to test the basic JsAccount
+ concepts, which can also serve as templates for new implementations.<br>
+ <h3>Component Naming</h3>
+ Because there are many different components involved with different roles,
+ it will be helpful to keep things straight by using a specific naming
+ convention for objects.
+ For testing, we consider that we are creating a new account type "foo". For
+ the specific case of the JA implementation of an object that implements the
+ nsIMsgMailNewsUrl interface, we'll use the following naming convention.
+ Typically C++ classes and JS constructors are capitalized, class instances
+ are not.<br>
+ <br>
+ Names are constructed with the following subparts:<br>
+ <br>
+ (<strong>Ja)</strong>(<strong>AccountType</strong>)(<strong>Language</strong>)(<strong>ComponentType</strong>)(<strong>Role</strong>)
+ <br>
+ <h4>Common parts of names</h4>
+ <ul>
+ <li><strong>Ja</strong>: All objects or classes begin with Ja</li>
+ <li><strong>AccountType</strong>: the type of account being created (here
+ <strong>Foo</strong>), or <strong>Base</strong> for the generic
+ implementation that is the base class of all types. May be *blank* if
+ the object is used for both base types and specific types.</li>
+ <li>
+ <strong>Language</strong>: Use <strong>Cpp</strong> with objects and
+ classes implemented using C++, leave blank for JS versions.
+ </li>
+ <li>
+ <strong>ComponentType</strong>: the standard MailNews term for objects
+ that implement a particular interface. The legacy .cpp base components
+ are typically named:<br>
+ <strong>(nsMsg)(ComponentType</strong>).cpp<br>
+ for example <strong>nsMsgIncomingServer</strong>.cpp. This may be
+ shortened where appropriate, for example we use <strong>Url</strong>
+ instead of <strong>MailNewsUrl</strong> as the <strong>Ja</strong>
+ prefix implies that this is an implementation of MailNews objects.</li>
+ <li><strong>Role</strong>: the function of the object within the JA
+ architecture.</li>
+ <ul>
+ <li><strong>Constructor</strong>: Creates the object only. </li>
+ <li><strong>Delegator</strong>: Calls the appropriate object, either a
+ JS or C++ variant, that implements a particular XPCOM method.
+ </li>
+ <li> <strong>Properties</strong>: JavaScript object that establishes
+ properties of a JA implementation class. This is used for automatic
+ generation of a list of methods to delegate to the JavaScript classes.</li>
+ <li>(blank): Actual implementation.</li>
+ </ul>
+ </ul>
+ Example: The C++ class that delegates the implementation of
+ nsIMsgMailNewsUrl (abbreviated as Url) to either a C++ or JS method is
+ called <strong>JaCppUrlDelegator</strong>.
+ </body>
+</html>
diff --git a/comm/mailnews/jsaccount/test/unit/resources/testComponents.manifest b/comm/mailnews/jsaccount/test/unit/resources/testComponents.manifest
new file mode 100644
index 0000000000..22bacb7262
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/unit/resources/testComponents.manifest
@@ -0,0 +1,16 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+# Definitions of components used in testing of JsAccount
+
+# nsIMsgMailNewsUrl implementation of a demo base URL
+component {73F98539-A59F-4F6F-9A72-D83A08646C23} testJaFooUrlComponent.js
+contract @mozilla.org/jsaccount/testjafoourl;1 {73F98539-A59F-4F6F-9A72-D83A08646C23}
+
+# nsIMsgFolder implementation
+component {8508ddeb-3eab-4877-a420-297518f62371} testJaBaseMsgFolderComponent.js
+contract @mozilla.org/mail/folder-factory;1?name=testja {8508ddeb-3eab-4877-a420-297518f62371}
+
+# nsIMsgIncomingServer implementation
+component {0eec03cd-da67-4949-ab2d-5fa4bdc68135} testJaBaseIncomingServerComponent.js
+contract @mozilla.org/messenger/server;1?type=testja {0eec03cd-da67-4949-ab2d-5fa4bdc68135}
diff --git a/comm/mailnews/jsaccount/test/unit/resources/testJaBaseIncomingServer.jsm b/comm/mailnews/jsaccount/test/unit/resources/testJaBaseIncomingServer.jsm
new file mode 100644
index 0000000000..602df26c30
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/unit/resources/testJaBaseIncomingServer.jsm
@@ -0,0 +1,74 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ This file creates a JS-based override of the JaIncomingServer implementation. It
+ demos a minimal JS class, and is also used in testing the additional methods
+ added to JaIncomingServer.cpp that are not in nsMsgDBFolder.cpp
+ */
+
+const EXPORTED_SYMBOLS = [
+ "JaBaseIncomingServerProperties",
+ "JaBaseIncomingServer",
+];
+
+// A partial JavaScript implementation of the base server methods.
+
+const JaBaseIncomingServerProperties = {
+ baseContractID: "@mozilla.org/jacppincomingserverdelegator;1",
+ baseInterfaces: [
+ Ci.nsISupports,
+ Ci.nsIMsgIncomingServer,
+ Ci.nsIInterfaceRequestor,
+ Ci.msgIOverride,
+ Ci.nsISupportsWeakReference,
+ ],
+ delegateInterfaces: ["nsIMsgIncomingServer"],
+ contractID: "@mozilla.org/messenger/server;1?type=testja",
+ classID: Components.ID("{0eec03cd-da67-4949-ab2d-5fa4bdc68135}"),
+};
+
+function JaBaseIncomingServer(aDelegator, aBaseInterfaces) {
+ dump("JaBaseIncomingServer\n");
+ // Typical boilerplate to include in all implementations.
+
+ // Object delegating method calls to the appropriate XPCOM object.
+ // Weak because it owns us.
+ this.delegator = Cu.getWeakReference(aDelegator);
+
+ // Base implementation of methods with no overrides.
+ this.cppBase = aDelegator.cppBase;
+
+ // cppBase class sees all interfaces
+ aBaseInterfaces.forEach(iface => this.cppBase instanceof iface);
+}
+
+JaBaseIncomingServer.prototype = {
+ // Typical boilerplate to include in all implementations.
+
+ // Flag this item as CPP needs to delegate to JS.
+ _JsPrototypeToDelegate: true,
+
+ // QI to the (partially implemented only) interfaces.
+ QueryInterface: ChromeUtils.generateQI(
+ JaBaseIncomingServerProperties.delegateInterfaces
+ ),
+
+ // Used to access an instance as JS, bypassing XPCOM.
+ get wrappedJSObject() {
+ return this;
+ },
+
+ // Dynamically-generated list of delegate methods.
+ delegateList: null,
+
+ // nsIMsgIncomingServer overrides.
+ get localStoreType() {
+ return "testja";
+ },
+ get localDatabaseType() {
+ return "mailbox";
+ },
+};
diff --git a/comm/mailnews/jsaccount/test/unit/resources/testJaBaseIncomingServerComponent.js b/comm/mailnews/jsaccount/test/unit/resources/testJaBaseIncomingServerComponent.js
new file mode 100644
index 0000000000..dc2e6682bb
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/unit/resources/testJaBaseIncomingServerComponent.js
@@ -0,0 +1,20 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This file is the component definition for a demo base implementation of a
+// javascript IncomingServer.
+
+const { JSAccountUtils } = ChromeUtils.import(
+ "resource:///modules/jsaccount/JSAccountUtils.jsm"
+);
+var { JaBaseIncomingServerProperties, JaBaseIncomingServer } =
+ ChromeUtils.import(
+ "resource://testing-common/mailnews/testJaBaseIncomingServer.jsm"
+ );
+
+var xpcomFactory = JSAccountUtils.jaFactory(
+ JaBaseIncomingServerProperties,
+ JaBaseIncomingServer
+);
diff --git a/comm/mailnews/jsaccount/test/unit/resources/testJaBaseMsgFolder.jsm b/comm/mailnews/jsaccount/test/unit/resources/testJaBaseMsgFolder.jsm
new file mode 100644
index 0000000000..7f56bbdaef
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/unit/resources/testJaBaseMsgFolder.jsm
@@ -0,0 +1,70 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ This file creates a JS-based override of the JaMsgFolder implementation. It
+ demos a minimal JS class, and is also used in testing the additional methods
+ added to JaMsgFolder.cpp that are not in nsMsgDBFolder.cpp
+ */
+
+const EXPORTED_SYMBOLS = ["JaBaseMsgFolderProperties", "JaBaseMsgFolder"];
+
+// A partial JavaScript implementation of the base server methods.
+
+const JaBaseMsgFolderProperties = {
+ baseContractID: "@mozilla.org/jacppmsgfolderdelegator;1",
+ baseInterfaces: [
+ Ci.nsISupports,
+ Ci.nsIMsgFolder,
+ Ci.nsIDBChangeListener,
+ Ci.nsIUrlListener,
+ Ci.nsIJunkMailClassificationListener,
+ Ci.nsIMsgTraitClassificationListener,
+ Ci.nsIInterfaceRequestor,
+ Ci.msgIOverride,
+ ],
+ delegateInterfaces: ["nsIMsgFolder"],
+ contractID: "@mozilla.org/mail/folder-factory;1?name=testja",
+ classID: Components.ID("{8508ddeb-3eab-4877-a420-297518f62371}"),
+};
+
+function JaBaseMsgFolder(aDelegator, aBaseInterfaces) {
+ // Typical boilerplate to include in all implementations.
+
+ // Object delegating method calls to the appropriate XPCOM object.
+ // Weak because it owns us.
+ this.delegator = Cu.getWeakReference(aDelegator);
+
+ // Base implementation of methods with no overrides.
+ this.cppBase = aDelegator.cppBase;
+
+ // cppBase class sees all interfaces
+ aBaseInterfaces.forEach(iface => this.cppBase instanceof iface);
+}
+
+JaBaseMsgFolder.prototype = {
+ // Typical boilerplate to include in all implementations.
+
+ // Flag this item as CPP needs to delegate to JS.
+ _JsPrototypeToDelegate: true,
+
+ // QI to the (partially implemented only) interfaces.
+ QueryInterface: ChromeUtils.generateQI(
+ JaBaseMsgFolderProperties.delegateInterfaces
+ ),
+
+ // Used to access an instance as JS, bypassing XPCOM.
+ get wrappedJSObject() {
+ return this;
+ },
+
+ // Dynamically-generated list of delegate methods.
+ delegateList: null,
+
+ // nsIMsgFolder overrides.
+ get incomingServerType() {
+ return "testja";
+ },
+};
diff --git a/comm/mailnews/jsaccount/test/unit/resources/testJaBaseMsgFolderComponent.js b/comm/mailnews/jsaccount/test/unit/resources/testJaBaseMsgFolderComponent.js
new file mode 100644
index 0000000000..8f71f71b6c
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/unit/resources/testJaBaseMsgFolderComponent.js
@@ -0,0 +1,19 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This file is the component definition for a demo base implementation of a
+// javascript msgFolder.
+
+const { JSAccountUtils } = ChromeUtils.import(
+ "resource:///modules/jsaccount/JSAccountUtils.jsm"
+);
+var { JaBaseMsgFolderProperties, JaBaseMsgFolder } = ChromeUtils.import(
+ "resource://testing-common/mailnews/testJaBaseMsgFolder.jsm"
+);
+
+var xpcomFactory = JSAccountUtils.jaFactory(
+ JaBaseMsgFolderProperties,
+ JaBaseMsgFolder
+);
diff --git a/comm/mailnews/jsaccount/test/unit/resources/testJaFooUrlComponent.js b/comm/mailnews/jsaccount/test/unit/resources/testJaFooUrlComponent.js
new file mode 100644
index 0000000000..9ed4d4c15b
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/unit/resources/testJaFooUrlComponent.js
@@ -0,0 +1,88 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ One of the goals of JsAccount is to be able to incrementally extend a base
+ implementation, possibly adding a new interface. This code demonstrates
+ a mailnews URL extended for a hypthetical account type "foo".
+*/
+
+const { JSAccountUtils } = ChromeUtils.import(
+ "resource:///modules/jsaccount/JSAccountUtils.jsm"
+);
+const { JaBaseUrl, JaBaseUrlProperties } = ChromeUtils.import(
+ "resource:///modules/jsaccount/JaBaseUrl.jsm"
+);
+
+const ATTACHMENT_QUERY = "part=1.";
+
+var FooUrlProperties = {
+ // Extend the base properties.
+ __proto__: JaBaseUrlProperties,
+
+ contractID: "@mozilla.org/jsaccount/testjafoourl;1",
+ classID: Components.ID("{73F98539-A59F-4F6F-9A72-D83A08646C23}"),
+
+ // Add an additional interface only needed by this custom class.
+ extraInterfaces: [Ci.msgIFooUrl],
+};
+
+// Constructor
+var xpcomFactory = JSAccountUtils.jaFactory(FooUrlProperties, FooUrl);
+
+// Main class.
+function FooUrl(aDelegator, aBaseInterfaces) {
+ // Superclass constructor
+ JaBaseUrl.call(this, aDelegator, aBaseInterfaces);
+
+ // I'm not sure why I have to call this again, as it is called in the
+ // base constructor, but without it this method will not find the
+ // interfaces beyond nsISupports.
+ aBaseInterfaces.forEach(iface => this.cppBase instanceof iface);
+
+ // instance variables
+ this._urlType = -1; // unknown;
+ this._itemId = null;
+ this._hidden = "IAmHidden";
+}
+
+// Extend the base class methods.
+FooUrl.prototype = {
+ // Typical boilerplate to include in all implementations.
+
+ // Extended the JS URL object.
+ __proto__: JaBaseUrl.prototype,
+
+ // Delegate these methods to CPP.
+ _JsPrototypeToDelegate: true,
+
+ // InterfaceRequestor override, needed if extraInterfaces.
+
+ getInterface(iid) {
+ for (let iface of FooUrlProperties.extraInterfaces) {
+ if (iid.equals(iface)) {
+ return this;
+ }
+ }
+ return this.delegator.QueryInterface(iid);
+ },
+
+ // msgIFooUrl implementation
+
+ // Foo id for item.
+ // attribute AString itemId;
+ get itemId() {
+ return this._itemId;
+ },
+ set itemId(aVal) {
+ this._itemId = aVal;
+ },
+
+ // Does this url refer to an attachment?
+ // readonly attribute boolean isAttachment;
+ get isAttachment() {
+ // We look to see if the URL has an attachment query
+ let query = this.QueryInterface(Ci.nsIURL).query;
+ return query && query.includes(ATTACHMENT_QUERY);
+ },
+};
diff --git a/comm/mailnews/jsaccount/test/unit/test_componentsExist.js b/comm/mailnews/jsaccount/test/unit/test_componentsExist.js
new file mode 100644
index 0000000000..726281bccc
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/unit/test_componentsExist.js
@@ -0,0 +1,90 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the components made available by JaAccount can be created with
+// each supported interface.
+
+let tests = [
+ // JaUrl
+ ["@mozilla.org/jacppurldelegator;1", "nsISupports"],
+ ["@mozilla.org/jacppurldelegator;1", "nsIMsgMailNewsUrl"],
+ ["@mozilla.org/jacppurldelegator;1", "nsIMsgMessageUrl"],
+ ["@mozilla.org/jacppurldelegator;1", "nsIURL"],
+ ["@mozilla.org/jacppurldelegator;1", "nsIURI"],
+ ["@mozilla.org/jacppurldelegator;1", "msgIOverride"],
+ ["@mozilla.org/jacppurldelegator;1", "nsIInterfaceRequestor"],
+ // (probably a url bug) ["@mozilla.org/jacppurldelegator;1", "nsISupportsWeakReference"],
+
+ // FooJaUrl
+ ["@mozilla.org/jsaccount/testjafoourl;1", "nsISupports"],
+ ["@mozilla.org/jsaccount/testjafoourl;1", "nsIMsgMailNewsUrl"],
+ ["@mozilla.org/jsaccount/testjafoourl;1", "nsIMsgMessageUrl"],
+ ["@mozilla.org/jsaccount/testjafoourl;1", "nsIURL"],
+ ["@mozilla.org/jsaccount/testjafoourl;1", "nsIURI"],
+ ["@mozilla.org/jsaccount/testjafoourl;1", "msgIOverride"],
+ ["@mozilla.org/jsaccount/testjafoourl;1", "nsIInterfaceRequestor"],
+ // JaAbDirectory
+ ["@mozilla.org/jacppabdirectorydelegator;1", "nsISupports"],
+ ["@mozilla.org/jacppabdirectorydelegator;1", "nsIAbDirectory"],
+ ["@mozilla.org/jacppabdirectorydelegator;1", "msgIOverride"],
+ ["@mozilla.org/jacppabdirectorydelegator;1", "nsIInterfaceRequestor"],
+ ["@mozilla.org/jacppabdirectorydelegator;1", "nsISupportsWeakReference"],
+ // JaCompose
+ ["@mozilla.org/jacppcomposedelegator;1", "nsISupports"],
+ ["@mozilla.org/jacppcomposedelegator;1", "nsIMsgCompose"],
+ ["@mozilla.org/jacppcomposedelegator;1", "nsIMsgSendListener"],
+ ["@mozilla.org/jacppcomposedelegator;1", "msgIOverride"],
+ ["@mozilla.org/jacppcomposedelegator;1", "nsIInterfaceRequestor"],
+ ["@mozilla.org/jacppcomposedelegator;1", "nsISupportsWeakReference"],
+ // JaIncomingServer
+ ["@mozilla.org/jacppincomingserverdelegator;1", "nsISupports"],
+ ["@mozilla.org/jacppincomingserverdelegator;1", "nsIMsgIncomingServer"],
+ ["@mozilla.org/jacppincomingserverdelegator;1", "msgIOverride"],
+ ["@mozilla.org/jacppincomingserverdelegator;1", "nsIInterfaceRequestor"],
+ ["@mozilla.org/jacppincomingserverdelegator;1", "nsISupportsWeakReference"],
+ // JaMsgFolder
+ ["@mozilla.org/jacppmsgfolderdelegator;1", "nsISupports"],
+ ["@mozilla.org/jacppmsgfolderdelegator;1", "nsIMsgFolder"],
+ ["@mozilla.org/jacppmsgfolderdelegator;1", "nsIDBChangeListener"],
+ ["@mozilla.org/jacppmsgfolderdelegator;1", "nsIUrlListener"],
+ [
+ "@mozilla.org/jacppmsgfolderdelegator;1",
+ "nsIJunkMailClassificationListener",
+ ],
+ [
+ "@mozilla.org/jacppmsgfolderdelegator;1",
+ "nsIMsgTraitClassificationListener",
+ ],
+ ["@mozilla.org/jacppmsgfolderdelegator;1", "msgIOverride"],
+ ["@mozilla.org/jacppmsgfolderdelegator;1", "nsISupportsWeakReference"],
+ // TestJaIncomingServer
+ ["@mozilla.org/messenger/server;1?type=testja", "nsISupports"],
+ ["@mozilla.org/messenger/server;1?type=testja", "nsIMsgIncomingServer"],
+ ["@mozilla.org/messenger/server;1?type=testja", "msgIOverride"],
+ ["@mozilla.org/messenger/server;1?type=testja", "nsISupportsWeakReference"],
+ // TestJaMsgProtocolInfo
+ ["@mozilla.org/messenger/protocol/info;1?type=testja", "nsISupports"],
+ ["@mozilla.org/messenger/protocol/info;1?type=testja", "nsIMsgProtocolInfo"],
+];
+
+function run_test() {
+ for (let [contractID, iface] of tests) {
+ dump(
+ "trying to create component " +
+ contractID +
+ " with interface " +
+ iface +
+ "\n"
+ );
+ try {
+ dump(Cc[contractID] + " " + Ci[iface] + "\n");
+ } catch (e) {
+ dump(e + "\n");
+ }
+
+ let comp = Cc[contractID].createInstance(Ci[iface]);
+ Assert.ok(comp instanceof Ci[iface]);
+ }
+}
diff --git a/comm/mailnews/jsaccount/test/unit/test_fooUrl.js b/comm/mailnews/jsaccount/test/unit/test_fooUrl.js
new file mode 100644
index 0000000000..8526ef2093
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/unit/test_fooUrl.js
@@ -0,0 +1,93 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests of override functionality using a demo "foo" type url.
+
+var { JaBaseUrlProperties } = ChromeUtils.import(
+ "resource:///modules/jsaccount/JaBaseUrl.jsm"
+);
+
+var extraInterfaces = [Ci.msgIFooUrl];
+
+function newURL() {
+ return Cc["@mozilla.org/jsaccount/testjafoourl;1"].createInstance(
+ Ci.nsISupports
+ );
+}
+
+var tests = [
+ function testExists() {
+ // test the existence of components and their interfaces.
+ let url = newURL();
+ for (let iface of JaBaseUrlProperties.baseInterfaces) {
+ Assert.ok(url instanceof iface);
+ let urlQI = url.QueryInterface(iface);
+ // Since the URL wasn't properly initialised, that is, it has no spec
+ // the following will crash. The underlying nsMsgMailNewsUrl
+ // has no m_baseURL yet and hence GetSpec() triggered by the
+ // Assert.uk(urlQI) will crash. So use this instead:
+ Assert.ok(urlQI != null);
+ }
+ for (let iface of extraInterfaces) {
+ let fooUrl = url.getInterface(iface);
+ Assert.ok(fooUrl instanceof iface);
+ Assert.ok(fooUrl.QueryInterface(iface) != null);
+ }
+ },
+ function test_msgIOverride() {
+ let url = newURL().QueryInterface(Ci.msgIOverride);
+
+ // test of access to wrapped JS object.
+
+ // Access the ._hidden attribute using the XPCOM interface,
+ // where it is not defined.
+ Assert.equal(typeof url.jsDelegate._hidden, "undefined");
+
+ // Get the JS object, where _hidden IS defined.
+ Assert.equal(url.jsDelegate.wrappedJSObject._hidden, "IAmHidden");
+ },
+
+ // We used to test nsIURI, nsIURL, and nsIMsgMailNewsUrl overrides, but those
+ // can no longer be overridden.
+ function test_nsIMsgMessageUrl() {
+ let url = newURL().QueryInterface(Ci.nsIMsgMessageUrl);
+ Assert.ok("originalSpec" in url);
+ let appDir = Services.dirsvc.get("GreD", Ci.nsIFile);
+ Assert.ok(appDir.path);
+ // test attributes
+ url.messageFile = appDir;
+ Assert.equal(url.messageFile.path, appDir.path);
+ },
+ function test_msgIJaUrl() {
+ let url = newURL().QueryInterface(Ci.msgIJaUrl);
+ url.setUrlType(Ci.nsIMsgMailNewsUrl.eMove);
+ Assert.ok(
+ url
+ .QueryInterface(Ci.nsIMsgMailNewsUrl)
+ .IsUrlType(Ci.nsIMsgMailNewsUrl.eMove)
+ );
+ },
+ function test_msgIFooUrl() {
+ let url = newURL().QueryInterface(Ci.nsIInterfaceRequestor);
+ let fooUrl = url.getInterface(Ci.msgIFooUrl);
+ Assert.ok(fooUrl instanceof Ci.msgIFooUrl);
+
+ fooUrl.itemId = "theItemId";
+ Assert.equal(fooUrl.itemId, "theItemId");
+
+ url.QueryInterface(Ci.msgIJaUrl).setSpec("https://foo.invalid/bar/");
+ Assert.ok(!fooUrl.isAttachment);
+ url
+ .QueryInterface(Ci.msgIJaUrl)
+ .setSpec("https://foo.invalid/bar?part=1.4&dummy=stuff");
+ Assert.ok(fooUrl.isAttachment);
+ },
+];
+
+function run_test() {
+ for (var test of tests) {
+ test();
+ }
+}
diff --git a/comm/mailnews/jsaccount/test/unit/test_jaMsgFolder.js b/comm/mailnews/jsaccount/test/unit/test_jaMsgFolder.js
new file mode 100644
index 0000000000..30a8557b01
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/unit/test_jaMsgFolder.js
@@ -0,0 +1,56 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This tests the additional methods added to JaMsgFolder.cpp that are not
+// in nsMsgDBFolder.cpp Although this code have been done creating the
+// delegator class directly, instead we use a JS component as a demo of
+// JS override classes.
+
+var { JaBaseMsgFolderProperties } = ChromeUtils.import(
+ "resource://testing-common/mailnews/testJaBaseMsgFolder.jsm"
+);
+var { MailUtils } = ChromeUtils.import("resource:///modules/MailUtils.jsm");
+var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+
+function run_test() {
+ let server = MailServices.accounts.createIncomingServer(
+ "foouser",
+ "foohost",
+ "testja"
+ );
+ Assert.ok(server instanceof Ci.msgIOverride);
+
+ // If you create a folder object directly, it will complain about not being registered.
+ // Use folder-lookup-service instead.
+ let testJaMsgFolder = MailUtils.getOrCreateFolder(
+ "testja://foouser@foohost/somefolder"
+ );
+ // let testJaMsgFolder = Cc[JaBaseMsgFolderProperties.contractID]
+ // .createInstance(Ci.msgIOverride);
+ Assert.ok(testJaMsgFolder instanceof Ci.nsIMsgFolder);
+
+ JaBaseMsgFolderProperties.baseInterfaces.forEach(iface => {
+ dump("testing interface " + iface + "(" + Ci[iface] + ")\n");
+ testJaMsgFolder.QueryInterface(Ci[iface]);
+ });
+
+ let db = testJaMsgFolder.msgDatabase;
+ Assert.ok(db instanceof Ci.nsIMsgDatabase);
+
+ // Make sure the DB actually works.
+ let dbFolder = db.folder;
+ Assert.ok(dbFolder instanceof Ci.nsIMsgFolder);
+ Assert.equal(dbFolder.URI, "testja://foouser@foohost/somefolder");
+ let fi = db.dBFolderInfo;
+ Assert.ok(fi instanceof Ci.nsIDBFolderInfo);
+ fi.setCharProperty("testProperty", "foobar");
+ Assert.equal(fi.getCharProperty("testProperty"), "foobar");
+ db.forceClosed();
+ db = null;
+
+ // Confirm that we can access XPCOM properties.
+}
diff --git a/comm/mailnews/jsaccount/test/unit/xpcshell.ini b/comm/mailnews/jsaccount/test/unit/xpcshell.ini
new file mode 100644
index 0000000000..69676b5d6f
--- /dev/null
+++ b/comm/mailnews/jsaccount/test/unit/xpcshell.ini
@@ -0,0 +1,10 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+[DEFAULT]
+head = head_jsaccount.js
+tail =
+support-files = resources/*
+[test_componentsExist.js]
+[test_fooUrl.js]
+[test_jaMsgFolder.js]