Bug 430716 - part 1, Use mailnews url override as initial conceptual demo of JsAccount, r=jorgk

This commit is contained in:
R Kent James 2016-04-19 16:47:56 -07:00
Родитель 823048784c
Коммит 9fd1161ce4
27 изменённых файлов: 1409 добавлений и 0 удалений

Просмотреть файл

@ -162,6 +162,7 @@
; interfaces.manifest doesn't get packaged because it is dynamically
; re-created at packaging time when linking the xpts that will actually
; go into the package, so the test related interfaces aren't included.
@RESPATH@/components/msgjsaccount.xpt
@RESPATH@/components/mime.xpt
@RESPATH@/components/mimeJSComponents.js
@RESPATH@/components/msgMime.manifest

Просмотреть файл

@ -190,6 +190,12 @@
#include "nsSmtpServer.h"
#include "nsMsgCompUtils.h"
////////////////////////////////////////////////////////////////////////////////
// jsAccount includes
////////////////////////////////////////////////////////////////////////////////
#include "msgJsAccountCID.h"
#include "JaUrl.h"
////////////////////////////////////////////////////////////////////////////////
// imap includes
////////////////////////////////////////////////////////////////////////////////
@ -585,6 +591,13 @@ NS_DEFINE_NAMED_CID(NS_MSGQUOTELISTENER_CID);
NS_DEFINE_NAMED_CID(NS_URLFETCHER_CID);
NS_DEFINE_NAMED_CID(NS_MSGCOMPUTILS_CID);
////////////////////////////////////////////////////////////////////////////////
// jsAccount factories
////////////////////////////////////////////////////////////////////////////////
NS_GENERIC_FACTORY_CONSTRUCTOR(JaCppUrlDelegator)
NS_DEFINE_NAMED_CID(JACPPURLDELEGATOR_CID);
////////////////////////////////////////////////////////////////////////////////
// imap factories
////////////////////////////////////////////////////////////////////////////////
@ -968,6 +981,8 @@ const mozilla::Module::CIDEntry kMailNewsCIDs[] = {
{ &kNS_MSGQUOTELISTENER_CID, false, NULL, nsMsgQuoteListenerConstructor},
{ &kNS_URLFETCHER_CID, false, NULL, nsURLFetcherConstructor},
{ &kNS_MSGCOMPUTILS_CID, false, NULL, nsMsgCompUtilsConstructor},
// JsAccount Entries
{ &kJACPPURLDELEGATOR_CID, false, nullptr, JaCppUrlDelegatorConstructor },
// Imap Entries
{ &kNS_IMAPURL_CID, false, NULL, nsImapUrlConstructor },
{ &kNS_IMAPPROTOCOL_CID, false, nullptr, nsImapProtocolConstructor },
@ -1184,6 +1199,8 @@ const mozilla::Module::ContractIDEntry kMailNewsContracts[] = {
{ NS_MSGQUOTELISTENER_CONTRACTID, &kNS_MSGQUOTELISTENER_CID },
{ NS_URLFETCHER_CONTRACTID, &kNS_URLFETCHER_CID },
{ NS_MSGCOMPUTILS_CONTRACTID, &kNS_MSGCOMPUTILS_CID },
// JsAccount Entries
{ JACPPURLDELEGATOR_CONTRACTID, &kJACPPURLDELEGATOR_CID },
// Imap Entries
{ NS_IMAPINCOMINGSERVER_CONTRACTID, &kNS_IMAPINCOMINGSERVER_CID },
{ NS_RDF_RESOURCE_FACTORY_CONTRACTID_PREFIX "imap", &kNS_IMAPRESOURCE_CID },
@ -1304,6 +1321,7 @@ static const mozilla::Module::CategoryEntry kMailNewsCategories[] = {
// Bayesian Filter Entries
// Compose Entries
{ "command-line-handler", "m-compose", NS_MSGCOMPOSESTARTUPHANDLER_CONTRACTID },
// JsAccount Entries
// Imap Entries
// Local Entries
// msgdb Entries

Просмотреть файл

@ -0,0 +1,282 @@
/* -*- 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;
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;
}

Просмотреть файл

@ -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.
};

Просмотреть файл

@ -0,0 +1,17 @@
# -*- 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_DIRS += ['test']

Просмотреть файл

@ -0,0 +1,17 @@
# -*- 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',
'msgIOverride.idl',
]
EXPORTS += [
'msgJsAccountCID.h',
]
XPIDL_MODULE = 'msgjsaccount'

Просмотреть файл

@ -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 string aMethod);
};

Просмотреть файл

@ -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;
};

Просмотреть файл

@ -0,0 +1,16 @@
/* -*- 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/. */
// Contains the definitions of contract IDs for modules in JsAccounts.
#ifndef _msgJsAccountCID_H_
#define _msgJsAccountCID_H_
#define JACPPURLDELEGATOR_CID \
{ 0x1a0b778c, 0x2fe6, 0x4012, { 0xb4, 0xf3, 0xe8, 0x1c, 0xc, 0x11, 0x64, 0x9 } }
#define JACPPURLDELEGATOR_CONTRACTID "@mozilla.org/jacppurldelegator;1"
#endif

Просмотреть файл

@ -0,0 +1,56 @@
<!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,
NS_MSGINCOMINGSERVER_CONTRACTID_PREFIX
"@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>

Просмотреть файл

@ -0,0 +1,33 @@
/* -*- 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/. */
#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 char *aMethodName)
{
// __FUNCTION__ is the undecorated function name in gcc, but decorated in
// Windows. __func__ will resolve this when supported in VS 2015.
nsCString prettyFunction;
#if defined (_MSC_VER)
prettyFunction.Append(mPrefix);
#endif
prettyFunction.Append(nsDependentCString(aMethodName));
mMethods.Put(prettyFunction, true);
return NS_OK;
}
} // namespace mailnews
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,52 @@
/* -*- 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 "nsDataHashtable.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(const char *aWindowsPrefix) :
mPrefix(aWindowsPrefix)
{ }
nsDataHashtable<nsCStringHashKey, bool> mMethods;
protected:
virtual ~DelegateList() { }
nsCString mPrefix; // Windows decorated method prefix.
};
} // namespace mailnews
} // namespace mozilla
/*
* This macro is used in forwarding functions.
* _interface: the interface being forwarded.
* _jsdelegate: the name of the JS pointer that implements a particular
* interface.
*
* You must follow the naming convention:
* 1) use mCppBase as the name of the C++ base class instance.
* 2) use mMethod as the name of the DelegateList object.
**/
#define DELEGATE_JS(_interface, _jsdelegate) (\
_jsdelegate && mMethods && \
mMethods->Contains(nsLiteralCString(__FUNCTION__)) ? \
_jsdelegate : nsCOMPtr<_interface>(do_QueryInterface(mCppBase)))
#endif

Просмотреть файл

@ -0,0 +1,198 @@
/* -*- 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/. */
#include "JaUrl.h"
#include "nsComponentManagerUtils.h"
#include "nsIFile.h"
#include "nsIMessenger.h"
#include "nsIMsgHdr.h"
#include "nsISupportsUtils.h"
#include "nsMsgBaseCID.h"
// This file contains an implementation of mailnews URLs in JsAccount.
namespace mozilla {
namespace mailnews {
NS_IMPL_ISUPPORTS_INHERITED(JaBaseCppUrl, nsMsgMailNewsUrl,
nsIMsgMessageUrl,
nsIInterfaceRequestor)
// 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;
}
// nsIMsgMessageUrl implementation
NS_IMETHODIMP JaBaseCppUrl::GetUri(char **aUri)
{
if (!mUri.IsEmpty())
*aUri = ToNewCString(mUri);
else
return NS_ERROR_NOT_INITIALIZED;
return NS_OK;
}
NS_IMETHODIMP JaBaseCppUrl::SetUri(const char *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(char **aOriginalSpec)
{
if (!aOriginalSpec || mOriginalSpec.IsEmpty())
return NS_ERROR_NULL_POINTER;
*aOriginalSpec = ToNewCString(mOriginalSpec);
return NS_OK;
}
NS_IMETHODIMP JaBaseCppUrl::SetOriginalSpec(const char *aOriginalSpec)
{
mOriginalSpec = aOriginalSpec;
return NS_OK;
}
NS_IMETHODIMP JaBaseCppUrl::GetMessageHeader(nsIMsgDBHdr **aMessageHeader)
{
// This routine does a lookup using messenger, assumming 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(NS_MESSENGER_CONTRACTID, &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;
}
NS_IMETHODIMP JaBaseCppUrl::SetMessageHeader(nsIMsgDBHdr *aMsgHdr)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
// 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,
nsIMsgMailNewsUrl,
nsIMsgMessageUrl,
nsIURI,
nsIURL,
nsIInterfaceRequestor)
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("mozilla::mailnews::JaCppUrlDelegator::");
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;
mJsIMsgMailNewsUrl = do_QueryInterface(aJsDelegate);
mJsIURI = do_QueryInterface(aJsDelegate);
mJsIURL = do_QueryInterface(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

Просмотреть файл

@ -0,0 +1,117 @@
/* -*- 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 "nsAutoPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDataHashtable.h"
#include "nsIFile.h"
#include "nsIInterfaceRequestor.h"
#include "nsIMsgFolder.h"
#include "nsISupports.h"
#include "nsMsgMailNewsUrl.h"
#include "nsWeakReference.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 nsIInterfaceRequestor
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIMSGMESSAGEURL
NS_DECL_NSIINTERFACEREQUESTOR
JaBaseCppUrl() { }
// nsIMsgMailNewsUrl overrides
NS_IMETHOD GetFolder(nsIMsgFolder **aFolder);
NS_IMETHOD SetFolder(nsIMsgFolder *aFolder);
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;
nsCString mOriginalSpec;
};
class JaCppUrlDelegator : public JaBaseCppUrl,
public msgIOverride
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_MSGIOVERRIDE
NS_FORWARD_NSIMSGMAILNEWSURL(DELEGATE_JS(nsIMsgMailNewsUrl, mJsIMsgMailNewsUrl)->)
NS_FORWARD_NSIURI(DELEGATE_JS(nsIURI, mJsIURI)->)
NS_FORWARD_NSIURL(DELEGATE_JS(nsIURL, mJsIURL)->)
NS_FORWARD_NSIMSGMESSAGEURL(DELEGATE_JS(nsIMsgMessageUrl, mJsIMsgMessageUrl)->)
NS_FORWARD_NSIINTERFACEREQUESTOR(DELEGATE_JS(nsIInterfaceRequestor, mJsIInterfaceRequestor)->)
JaCppUrlDelegator();
class Super : public nsIMsgMailNewsUrl,
public nsIMsgMessageUrl,
public nsIInterfaceRequestor
{
public:
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_NSIMSGMESSAGEURL(mFakeThis->JaBaseCppUrl::)
NS_FORWARD_NSIINTERFACEREQUESTOR(mFakeThis->JaBaseCppUrl::)
private:
virtual ~Super() {}
JaCppUrlDelegator *mFakeThis;
};
private:
virtual ~JaCppUrlDelegator() {
}
// Interfaces that may be overridden by JS.
nsCOMPtr<nsIMsgMailNewsUrl> mJsIMsgMailNewsUrl;
nsCOMPtr<nsIURI> mJsIURI;
nsCOMPtr<nsIURL> mJsIURL;
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;
nsDataHashtable<nsCStringHashKey, bool> *mMethods;
};
} // namespace mailnews
} // namespace mozilla
#endif

Просмотреть файл

@ -0,0 +1,18 @@
# -*- 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',
'JaUrl.cpp',
]
EXPORTS += [
'DelegateList.h',
'JaUrl.h',
]
Library('JsAccount')
FINAL_LIBRARY = 'mail'

Просмотреть файл

@ -0,0 +1,14 @@
# -*- 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'
TEST_HARNESS_FILES.xpcshell.mailnews.jsaccount.test.unit.resources += [
'!/dist/bin/components/testJsAccount.xpt',
]

Просмотреть файл

@ -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: */
/* 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;
/// urlType (copy, move, display) from nsIMsgMailNewsUrl
void setUrlType(in unsigned long aType);
};

Просмотреть файл

@ -0,0 +1,11 @@
# -*- 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']

Просмотреть файл

@ -0,0 +1,21 @@
/* -*- 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.classes;
var Ci = Components.interfaces;
var Cr = Components.results;
var CC = Components.Constructor;
var Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/mailServices.js");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://testing-common/mailnews/mailTestUtils.js");
Cu.import("resource://testing-common/mailnews/localAccountUtils.js");
// Load the test components.
do_load_manifest("resources/testComponents.manifest")
// Ensure the profile directory is set up.
do_get_profile();

Просмотреть файл

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type">
<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>

Просмотреть файл

@ -0,0 +1,3 @@
interfaces testJsAccount.xpt
component {73F98539-A59F-4F6F-9A72-D83A08646C23} testJaFooUrlComponent.js
contract @mozilla.org/jsaccount/testjafoourl;1 {73F98539-A59F-4F6F-9A72-D83A08646C23}

Просмотреть файл

@ -0,0 +1,119 @@
/* 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".
**/
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cr = Components.results;
var Cu = Components.utils;
var CE = Components.Exception;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/jsaccount/JSAccountUtils.jsm");
Cu.import("resource://gre/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
function FooUrlConstructor() {
}
// Constructor prototype (not instance prototype).
FooUrlConstructor.prototype = {
classID: FooUrlProperties.classID,
_xpcom_factory: 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: function(iid) {
for (let iface of FooUrlProperties.extraInterfaces) {
if (iid.equals(iface))
return this;
}
return this.delegator.QueryInterface(iid);
},
// nsIURI overrides
// Override clone() to always make the pathname capitalized. This is useful
// to test that a method called from C++, but overridden in JS, gets the JS
// method, since nsMsgMailNewsUrl::CloneIgnoringRef calls Clone().
clone: function()
{
let uriClone = this.cppBase.clone();
uriClone.path = uriClone.path.toUpperCase();
return uriClone;
},
// nsIMsgMailNewsUrl overrides
// Override to allow setting from a JS variable.
IsUrlType(aType) { return aType == this._urlType;},
// 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.indexOf(ATTACHMENT_QUERY) != -1);
},
/// urlType (copy, move, display) from nsIMsgMailNewsUrl
//void setUrlType(in unsigned long aType);
setUrlType: function(aType) { this._urlType = aType;},
}
var NSGetFactory = XPCOMUtils.generateNSGetFactory([FooUrlConstructor]);

Просмотреть файл

@ -0,0 +1,6 @@
/* -*- 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/ */
load("../../../resources/mailShutdown.js");

Просмотреть файл

@ -0,0 +1,43 @@
/* -*- 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"],
// 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"],
];
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]);
}
}

Просмотреть файл

@ -0,0 +1,134 @@
/* -*- 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.
Cu.import("resource://gre/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);
Assert.ok(urlQI);
}
for (let iface of extraInterfaces) {
let fooUrl = url.getInterface(iface);
Assert.ok(fooUrl instanceof iface);
Assert.ok(fooUrl.QueryInterface(iface));
}
},
function test_msgIOverride() {
let url = newURL().QueryInterface(Ci.msgIOverride);
// test of access to wrapped JS object
Assert.equal(typeof url.jsDelegate._hidden, "undefined");
Assert.equal(url.jsDelegate.wrappedJSObject._hidden, "IAmHidden");
},
function test_nsIURI() {
let url = newURL().QueryInterface(Ci.nsIURI);
// test attributes
Assert.ok("spec" in url);
url.spec = "https://test.invalid/folder?isFoo=true&someBar=theBar";
Assert.equal(url.host, "test.invalid");
// test non-attributes
// url.resolve is overridden in nsMsgMailNewsUrl to only work if starts with "#"
Assert.equal("https://test.invalid/folder?isFoo=true&someBar=theBar#modules",
url.resolve("#modules"));
// Test JS override of method called virtually in C++.
// nsMsgMailNewsUrl::CloneIgnoringRef calls Clone(). We overrode the JS to
// capitalize the path.
url.spec = "https://test.invalid/folder#modules";
Assert.equal(url.clone().spec, "https://test.invalid/FOLDER#MODULES");
Assert.equal(url.cloneIgnoringRef().spec, "https://test.invalid/FOLDER");
// Demo of differences between the various versions of the object. The
// standard XPCOM constructor returns the JS implementation, as was tested
// above. Try the same tests using the JS delegate, which should give the
// same (overridden) results (overridden for clone, C++ class
// for cloneIgnoringRef.
let jsDelegate = url.QueryInterface(Ci.msgIOverride).jsDelegate.wrappedJSObject;
Assert.equal(jsDelegate.clone().spec, "https://test.invalid/FOLDER#MODULES");
Assert.equal(jsDelegate.cloneIgnoringRef().spec, "https://test.invalid/FOLDER");
// Not sure why you would want to do this, but you call also call the C++
// object that does delegation, and get the same result. This is actually
// what we expect C++ callers to see.
let cppDelegator = jsDelegate.delegator.QueryInterface(Ci.nsIURI);
Assert.equal(cppDelegator.clone().spec, "https://test.invalid/FOLDER#MODULES");
Assert.equal(cppDelegator.cloneIgnoringRef().spec, "https://test.invalid/FOLDER");
// The cppBase object will not have the overrides.
let cppBase = url.QueryInterface(Ci.msgIOverride).cppBase.QueryInterface(Ci.nsIURI);
Assert.equal(cppBase.clone().spec, "https://test.invalid/folder#modules");
// But then it gets tricky. We can call cloneIgnoringRef in the C++ base
// but it will use the virtual clone which is overridden.
Assert.equal(cppBase.cloneIgnoringRef().spec, "https://test.invalid/FOLDER");
},
function test_nsIURL() {
let url = newURL().QueryInterface(Ci.nsIURL);
Assert.ok("filePath" in url);
url.spec = "https://test.invalid/folder?isFoo=true&someBar=theBar";
Assert.equal(url.query, "isFoo=true&someBar=theBar");
// Note that I tried here to test getCommonSpec, but that does not work
// because nsStandardURL.cpp makes an assumption that the URL is directly
// an nsStandardURL.cpp.
},
function test_nsIMsgMailNewsUrl() {
let url = newURL().QueryInterface(Ci.nsIMsgMailNewsUrl);
Assert.ok("msgWindow" in url);
url.maxProgress = 23;
Assert.equal(url.maxProgress, 23);
},
function test_nsIMsgMessageUrl() {
let url = newURL().QueryInterface(Ci.nsIMsgMessageUrl);
Assert.ok("originalSpec" in url);
let appDir = Services.dirsvc.get("GreD", Components.interfaces.nsIFile);
Assert.ok(appDir.path);
// test attributes
url.messageFile = appDir;
Assert.equal(url.messageFile.path, appDir.path);
},
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.nsIURI).spec = "https://foo.invalid/bar/";
Assert.ok(!fooUrl.isAttachment);
url.QueryInterface(Ci.nsIURI).spec = "https://foo.invalid/bar?part=1.4&dummy=stuff";
Assert.ok(fooUrl.isAttachment);
let msgMailNewsUrl = url.QueryInterface(Ci.nsIMsgMailNewsUrl);
Assert.ok(!msgMailNewsUrl.IsUrlType(Ci.nsIMsgMailNewsUrl.eMove));
fooUrl.setUrlType(Ci.nsIMsgMailNewsUrl.eMove);
Assert.ok(msgMailNewsUrl.IsUrlType(Ci.nsIMsgMailNewsUrl.eMove));
},
];
function run_test()
{
for (var test of tests)
test();
}

Просмотреть файл

@ -0,0 +1,9 @@
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
[DEFAULT]
head = head_jsaccount.js
tail = tail_jsaccount.js
support-files = resources/*
[test_componentsExist.js]
[test_fooUrl.js]

Просмотреть файл

@ -17,6 +17,7 @@ DIRS += [
'import/text/src',
'import/vcard/src',
'intl',
'jsaccount',
'local/public',
'local/src',
'mime',