Breaking the grip JS has on the DOM
We want to change the grip JS has on the DOM and on XUL. We will do this in 2 steps:
- Change the framework to be language neutral - https://bugzilla.mozilla.org/show_bug.cgi?id=255942
- Create a Python implementation.
Ideally, the first step could be done without consideration for the second, in the assumption at the implementation should be truly language neutral. However, this first real implementation is likely to impact the design decisions made, so there will be some iteration involved
Original specification - stage 1
This is the high-level task list as specified at the start of the project:
- Extend nsScriptLoader using the XPCOM category manager to handle arbitrary <script type="...">
- MIME types, loading extension component mapped by MIME type through a category
- Abstract and multiplex nsIScriptContext, generalizing it (changing its IID of course) away from the JS API, making a nsIJavaScriptContext for JS and an nsIPythonContext for Python, fixing various places that assume nsIScriptContext "does JS"
- Fix default script language selection and event handlers, which are script-language-typed by the selected default, so that you can write Python event handlers.
High-level design decisions
Decisions made/ratified by Brendan etc:
- No sharing of language namespaces. Only the "global DOM namespace" (ie, the nsIScriptGlobalObject) will be shared.
Identified task list
These are the tasks identified during the analysis and implementation phases of the project.
nsScriptLoader
- nsScriptLoader's interface is language neutral. Theoretically only the implementation need change.
nsIScriptContext
- nsIScriptContext is not language neutral. In some cases, what would otherwise be a "JSObject *" has been replaced with a "void *" - however, callers and implementation still make JS assumptions. Most consumers of this interface are suspect.
The primary usage of nsIScriptContext falls into the following categories:
- Getting the global object for a context, and/or getting a default context - but not actually doing anything with them other than pass them on.
- Futzing with the global context and/or global object - setting globals or event handlers, etc??
- Explicit execution of script code.
We need to either:
- Clone and adapt this interface for non JS languages. Every consumer would need to be changed heavily for each new language.
- Invent nsIScriptObject and related interfaces/structures. This would allow most call sites can move towards a fully language-independent model.
nsDOMClassInfo
Some of this will need to be generalized for use by multiple languages. Specifically, the "tables" of interfaces and access to the extended type information would be unmaintainable across multiple languages.
nsIScriptGlobalObject and nsIScriptGlobalObjectOwner
There remains a "JSObject *global" here. Hopefully this is used primarily to provide either a default "context", or to set global variables.
Exceptions
Exceptions should be chained across languages. This should just mean diligent use of nsIExceptionService?
New Interfaces
nsIScriptLanguageContext - replaces "JSContext *". hrm - why not simply nsIScriptContext
nsIScriptObject - replaces "JSObject *" and some "void *"?
existing nsIScriptGlobalObject - inherits from nsIScriptObject?
nsIScriptLanguage - new "manager" of globals etc?
Identified list of things we will *not* do
- We will make no attempt to have abstract the security interfaces - Python has no concept of "untrusted code". This means Python will be restricted to running from trusted XUL.
- Principals (obviously we must not change existing semantics, but Python will ignore them.)
- Allowing a language version to be specified.
Stuff Mark still needs to grok
- Whole JS "globals" concept - need to understand JS namespaces better. It seems a "js global" is analogous to a Python dict which will be used as the globals namespace.
- What exactly does nsDOMClassInfo do???
- nsEventListenerManager::AddScriptEventListener is interesting and a good place to look for factoring opportunities. It does quite a bit JS specific - but it really is *not* JS specific at all.
http://lxr.mozilla.org/seamonkey/source/content/events/src/nsEventListenerManager.cpp#1160
- nsBindingManager - very JS specific and not using nsIScript interfaces
- Need to understand the desired 'undefined' semantics for the return value.
Python's builtin None is closer to a JS null, so may not be suitable for 'undefined'. However, a simple 'return' will return None.
- Timeouts look tricky.
- Events look tricky
Random Notes
Some notes about existing callers of certain interfaces.
nsIScriptContext
nsDOMParser:
* Gets "native call context" to end up with nsIScriptContext - apparently all just to get the script URI
nsDocShell - security related functions
nsXMLHttpRequest.cpp
* GetCurrentContext uses the JS ContextStack.
mozXMLTermUtils.cpp:
* mozXMLTermUtils::ExecuteScript called nsIScriptContext::ExecuteScript - but with NULL "void *" pointers.
nsScriptSecurityManager.cpp/nsSecurityManagerFactory.cpp:
- Almost all functions convert a JSContext to an nsIScriptContext. Most just use to GetGlobalObject - GetPrincipalAndFrame has JS assumptions
- nsSecurityNameSet::InitializeNameSet - Lots of JS specific code.
docshell/base/nsDocShell.cpp:
1 use: nsDocShell::CheckLoadingPermissions() - use of JF ContextStack to fetch nsIScriptContext - just to GetGlobalObject
embedding/components/windowwatcher/src/nsWWJSUtils.cpp
Converts JSContext to nsIScriptContext. Mainly for GetGlobalObject, but nsIScriptGlobalObject *nsWWJSUtils::GetStaticScriptGlobal has JS deps.
embedding/components/windowwatcher/src/nsWindowWatcher.cpp:
JSObject * nsWindowWatcher::GetWindowScriptObject not used???
nsWindowWatcher::AttachArguments AddSupportsTojsvals 2038 rv = xpc->WrapNative(cx, ::JS_GetGlobalObject(cx), data, 2039 *iid, getter_AddRefs(wrapper));
content/base/src/nsContentUtils.cpp:
Convert nsIScriptContext -> JS Context
content/base/src/nsDocument.cpp:
Convert nsIScriptContext -> JS Context -> CanRunScripts (can CanRunScripts take an nsIScriptContext?)
nsRange:
nsRange::CreateContextualFragment - seems to push/pop a JS Context?
content/base/src/nsScriptLoader.cpp:
nsScriptLoader::ProcessScriptElement Does the actual loading of the stream. EvaluateScript calls nsIScriptContext->EvaluateScript - with most args
content/events/src/nsEventListenerManager.cpp:
Quite JS specific
content/xbl/src/ nsXBLBinding
::JSSetPrototype for a new context?
nsXBLDocumentInfo:
implementation of nsIScriptGlobalObject. JSObject *mJSObject; // XXX JS language rabies bigotry badness mJSObject = ::JS_NewObject - abstraction?
nsXBLProtoImpl:
Lots of JS Specific code.
content/xul: nsXULElement
event handlers remain JS specific serialization too
nsXULDocument:
Calls ExecuteScript with mScriptGlobalObject->GetGlobalJSObject() as an arg case nsXULPrototypeNode::eType_Script: { else if (scriptproto->mJSObject) { // An inline script rv = ExecuteScript(scriptproto->mJSObject); if (NS_FAILED(rv)) return rv; }
XUL Script cache
dom/src/base/nsDOMClassInfo.cpp
Lots of JS - dynamic DOM not exposed by XPCOM class info?
dom/src/base/nsGlobalWindow.cpp
JS specific "scope" code nsIScriptContext *currentCX = nsJSUtils::GetDynamicScriptContext(cx); argv handling Creating a window - calls WindowWatcher::OpenWindowJS
dom/src/events/nsJSEventListener.cpp:
layout/generic/nsObjectFrame.cpp
xpfe/appshell/src/nsAppShellService.cpp
plugins/activex/embedding/crypto: ignoring for now.
nsIScriptGlobalObject
http://lxr.mozilla.org/seamonkey/source/dom/public/nsIScriptGlobalObject.h
- GetGlobalJSObject -> nsIScriptLanguageObject *GetGlobalObject(const char *language);??
- JSObject references (GetGlobalJSObject/OnFinalize)
- SetNewArguments - nsVariant[]?
- CompileScript:
nsXulElement. Line 3666: // XXXbe violate nsIScriptContext layering because its version parameter // is mis-typed as const char * sets mJSObject