Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1

This commit is contained in:
Matt A. Tobin 2019-11-03 00:17:46 -04:00 • committed by Roy Tam
commit e400f4130a
1564 changed files with 510348 additions and 0 deletions

View file

@ -0,0 +1,285 @@
/* -*- 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 = {};
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
// Logger definitions.
const LOGGER_NAME = "JsAccount";
const PREF_BRANCH_LOG = "mailnews.jsaccount.log.";
const PREF_LOG_LEVEL = PREF_BRANCH_LOG + "level";
const PREF_LOG_DUMP = PREF_BRANCH_LOG + "dump";
// Set default logging levels.
const LOG_LEVEL_DEFAULT = "Info"
const LOG_DUMP_DEFAULT = true;
// Logging usage: set mailnews.jsaccount.log.level to the word "Debug" to
// increase logging level.
var log = configureLogging();
/**
*
* 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 contructor 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 = XPCOMUtils.generateQI([Ci.nsIFactory]);
factory.lockFactory = function() {};
factory.createInstance = function(outer, iid)
{
if (outer != null)
throw Cr.NS_ERROR_NO_AGGREGATION;
// 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(delegator.jsDelegate.wrappedJSObject);
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 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.config("Skipping " + method + "\n");
continue;
}
log.config("processing " + method + "\n");
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;
}
// Configure the logger based on the preferences.
function configureLogging()
{
let log = Log.repository.getLogger(LOGGER_NAME);
// Log messages need to go to the browser console.
let consoleAppender = new Log.ConsoleAppender(new Log.BasicFormatter());
log.addAppender(consoleAppender);
// Make sure the logger keeps up with the logging level preference.
log.level = Log.Level[Preferences.get(PREF_LOG_LEVEL, LOG_LEVEL_DEFAULT)];
// If enabled in the preferences, add a dump appender.
let logDumping = Preferences.get(PREF_LOG_DUMP, LOG_DUMP_DEFAULT);
if (logDumping) {
let dumpAppender = new Log.DumpAppender(new Log.BasicFormatter());
log.addAppender(dumpAppender);
}
return log;
}

View file

@ -0,0 +1,81 @@
/* -*- 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"];
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cr = Components.results;
var Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/jsaccount/JSAccountUtils.jsm");
// A partial JavaScript implementation of the base server methods.
const JaBaseUrlProperties = {
// The CPP object that delgates 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,
],
// 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}"),
};
function JaBaseUrl(aDelegator, aBaseInterfaces) {
// Typical boilerplate to include in all implementations.
// 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);
}
JaBaseUrl.prototype = {
// Typical boilerplate to include in all implementations.
__proto__: JSAccountUtils.makeCppDelegator(JaBaseUrlProperties),
// Flag this item as CPP needs to delegate to JS.
_JsPrototypeToDelegate: true,
// QI to the interfaces.
QueryInterface: XPCOMUtils.generateQI(JaBaseUrlProperties.baseInterfaces),
// 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.
};