Breaking the grip JS has on the DOM: Difference between revisions
No edit summary |
(more info about nsIDOMClassInfo and using nsIScriptGlobalObject as "aScopeObject") |
||
Line 34: | Line 34: | ||
The existing nsIScriptGlobalObject interface will move towards a model where there is a global script context per language. GetGlobalJSObject and GetContext would be deprecated, with a new method allowing the desired language to be specified. | The existing nsIScriptGlobalObject interface will move towards a model where there is a global script context per language. GetGlobalJSObject and GetContext would be deprecated, with a new method allowing the desired language to be specified. | ||
The 'context stack' will need to be enhanced to allow each language to have their own implementation for pushing and poping context. Thus, when the DOM requires an item on or off the context stack, each language will be called once to do its own thing. | |||
The | nsDOMClassInfo will be split into a language neutral nsDOMClassInfo, and a JS specific nsIXPCScriptable. The JS specific code in this file will need to be re-written for Python, but this should allow all DOM knowledge to be reused by the Python implementation. | ||
Need to agree on a single "script language ID". nsIProgrammingLanguage defines a set of integers, while nsIScriptElement uses a generic "char *". | |||
Is nsIDOMClassInfo just a poor-man's IDispatch? Should we just invent a new interface designed for truly dynamic object models, and wrap the DOM in that? This will require someone from Mozilla driving it. | |||
Below are specific implementation notes: | Below are specific implementation notes: | ||
Line 45: | Line 47: | ||
* Existing JSObject replaced with void | * Existing JSObject replaced with void | ||
* Existing "void *aScopeObject" (ExecuteScript/EvaluateStringWithValue/CompileScript), replaced with nsIScriptGlobalObject. The language impl can then get the "void *" for their language via either sgo->GetLanguageContext() or (static) nsIScriptGlobalObject::GetDefaultLanguageObject() | |||
* Need a "WrapNative" type function | * Need a "WrapNative" type function | ||
* FinalizeClasses method - JS does the ClearScope/ClearWatchpointsForObject/ClearRegExpStatistics? | * FinalizeClasses method - JS does the ClearScope/ClearWatchpointsForObject/ClearRegExpStatistics? | ||
Line 59: | Line 61: | ||
nsGlobalWindow is the main implemention of nsIScriptGlobalObject | nsGlobalWindow is the main implemention of nsIScriptGlobalObject | ||
* XXXX - Still need to understand GetNativeContext | |||
* nsIScriptGlobalObject probably needs to remain a single object (ie, not per language). It will move towards keeping a list of "IScriptContext *ctx, void *global" items, one for each language. New method: | * nsIScriptGlobalObject probably needs to remain a single object (ie, not per language). It will move towards keeping a list of "IScriptContext *ctx, void *global" items, one for each language. New method: | ||
nsresult GetLanguageContext( [in] language_id language, [out] nsIScriptContext **retLangContext, [out]void **retLangGlobal); | nsresult GetLanguageContext( [in] language_id language, [out] nsIScriptContext **retLangContext, [out]void **retLangGlobal); | ||
mJSObject and mContext will both be dropped in favour of the list. The concept of a single context/global will be deprecated - callers will be encouraged to use the new method. | mJSObject and mContext will both be dropped in favour of the list. The concept of a single context/global will be deprecated - callers will be encouraged to use the new method. | ||
GetContext()/GetGlobalJSObject() remain for b/w compat. They are | GetContext()/GetGlobalJSObject() remain for b/w compat. They are equivalent to "GetLanguageContext('js', ...)" | ||
* New static method GetDefaultLanguageObject(PRInt32 language, void **retLangGlobal). For JS, this will return ::JS_GetGlobalObject(ctxt) | |||
* New method to set and get "properties" - nsGlobalWindow does this for JS. Setting a property should presumably set it in all languages (via the global attached to the context). Getting is tricker - what if the property is in a different language? Or in multiple languages? | * New method to set and get "properties" - nsGlobalWindow does this for JS. Setting a property should presumably set it in all languages (via the global attached to the context). Getting is tricker - what if the property is in a different language? Or in multiple languages? | ||
Line 83: | Line 89: | ||
Existing nsIJSContextStack "@mozilla.org/js/xpc/ContextStack;1" service is replaced with language generic ContextStack. This is almost identical to the JS version, but all functions accept a 'language_id' param, to indicate which language they are interested in. Existing 'js' specific context stack could be re-implemented using the new one. | Existing nsIJSContextStack "@mozilla.org/js/xpc/ContextStack;1" service is replaced with language generic ContextStack. This is almost identical to the JS version, but all functions accept a 'language_id' param, to indicate which language they are interested in. Existing 'js' specific context stack could be re-implemented using the new one. | ||
Probably need a new interface for each language to do their thing. The DOM will simply ask for a new context - something must do the delegation to the languages. | Probably need a new interface for each language to do their thing. The DOM will simply ask for a new context - something must do the delegation to the languages. XXX - but how do we know what languages we need to work with? Can we introduce new languages to the mix even after a context has been pushed? | ||
What about http://lxr.mozilla.org/seamonkey/source/dom/public/nsIScriptContext.h#356 - it | What about http://lxr.mozilla.org/seamonkey/source/dom/public/nsIScriptContext.h#356 - it | ||
Line 92: | Line 98: | ||
=== nsScriptLoader === | === nsScriptLoader === | ||
nsScriptLoader's interface is language neutral. | |||
* nsScriptLoadRequest will have either a nsIScriptContext or language ID as a member. | |||
=== nsScriptLoaderObserver === | |||
Presumably will need to become script language aware. eg, scriptAvailable() method has text of the script passed - not much use without also knowing the language. Possibly replace with nsIScriptElement, which does have the language ID | |||
=== nsDOMClassInfo === | === nsDOMClassInfo === | ||
Lots of DOM namespace magic happens here, but it is very JS specific. We will: | |||
* Create ns(I)DOMClassInfo. It contains all the existing code that works with nsDOMClassInfoData - that includes most of the "tables" defined using macros. | |||
* nsDOMXPC will have all the JS specific code, which is currently implementing nsIXPCScriptable. This is the bulk of the existing nsDOMClassInfo implementation. | |||
* This language neutral interface will allow language "helpers" to be explicitly installed, and will then be able to be fetched by the existing nsIClassInfo::GetLanguageHelper() function. Thus, anyone with an nsIDOMClassInfo will still be able to get the JS helper. | |||
* Decide what to do with the various flags - eg, ALLOW_PROP_MODS_TO_PROTOTYPE. Presumably these will have some meaning to languages other than JS, but they are defined in a JS specific interface (nsIXPCScriptable) | |||
The Python language implementation will then need to write an equivalent to the new nsDOMXPC. This will be the magic where attributes are got/set for objects, etc. This Python implementation will need to lean heavily on nsDOMClassInfo. | |||
=== nsXULPrototypeScript === | === nsXULPrototypeScript === |
Revision as of 12:46, 9 August 2005
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.
Overview
The existing nsIScriptContext interface will remain tied to a specific language. The "void *" params in its interface will remain. This means that a "void *" and a suitable nsIScriptContext must always be treated as a pair (ie, given just a "void *", there is no way to determine an appropriate nsIScriptContext suitable for it - although "assume JS" is likely to remain for existing code that does exactly this)
nsIScriptContext will grow new methods relating to:
- language specific cleanup and "default language context" type code. For example, nsGlobalWindow.cpp has a number of calls to ::JS_ClearScope/::JS_ClearWatchPointsForObject etc - these need to be hidden behind one of the interfaces.
- The "WrapNative" process.
The existing nsIScriptGlobalObject interface will move towards a model where there is a global script context per language. GetGlobalJSObject and GetContext would be deprecated, with a new method allowing the desired language to be specified.
The 'context stack' will need to be enhanced to allow each language to have their own implementation for pushing and poping context. Thus, when the DOM requires an item on or off the context stack, each language will be called once to do its own thing.
nsDOMClassInfo will be split into a language neutral nsDOMClassInfo, and a JS specific nsIXPCScriptable. The JS specific code in this file will need to be re-written for Python, but this should allow all DOM knowledge to be reused by the Python implementation.
Need to agree on a single "script language ID". nsIProgrammingLanguage defines a set of integers, while nsIScriptElement uses a generic "char *".
Is nsIDOMClassInfo just a poor-man's IDispatch? Should we just invent a new interface designed for truly dynamic object models, and wrap the DOM in that? This will require someone from Mozilla driving it.
Below are specific implementation notes:
nsIScriptContext
- Existing JSObject replaced with void
- Existing "void *aScopeObject" (ExecuteScript/EvaluateStringWithValue/CompileScript), replaced with nsIScriptGlobalObject. The language impl can then get the "void *" for their language via either sgo->GetLanguageContext() or (static) nsIScriptGlobalObject::GetDefaultLanguageObject()
- Need a "WrapNative" type function
- FinalizeClasses method - JS does the ClearScope/ClearWatchpointsForObject/ClearRegExpStatistics?
- Some kind of "SetProperty" function - as needed by nsGlobalWindow - "arguments", "navigator" etc. http://lxr.mozilla.org/seamonkey/source/dom/src/base/nsGlobalWindow.cpp#871
nsIScriptGlobalObject / nsGlobalWindow
http://lxr.mozilla.org/seamonkey/source/dom/public/nsIScriptGlobalObject.h
nsGlobalWindow is the main implemention of nsIScriptGlobalObject
- XXXX - Still need to understand GetNativeContext
- nsIScriptGlobalObject probably needs to remain a single object (ie, not per language). It will move towards keeping a list of "IScriptContext *ctx, void *global" items, one for each language. New method:
nsresult GetLanguageContext( [in] language_id language, [out] nsIScriptContext **retLangContext, [out]void **retLangGlobal);
mJSObject and mContext will both be dropped in favour of the list. The concept of a single context/global will be deprecated - callers will be encouraged to use the new method. GetContext()/GetGlobalJSObject() remain for b/w compat. They are equivalent to "GetLanguageContext('js', ...)"
- New static method GetDefaultLanguageObject(PRInt32 language, void **retLangGlobal). For JS, this will return ::JS_GetGlobalObject(ctxt)
- New method to set and get "properties" - nsGlobalWindow does this for JS. Setting a property should presumably set it in all languages (via the global attached to the context). Getting is tricker - what if the property is in a different language? Or in multiple languages?
- Properties set include "arguments" and "navigator". Presumably other code also sets additional properties?
- Properties fetched seem arbitrary.
- "Arguments" will need to be specified in a language neutral way. Maybe array of nsVariants?
- Serializing scripts?
- CompileScript:
nsXulElement. Line 3666: // XXXbe violate nsIScriptContext layering because its version parameter // is mis-typed as const char * sets mJSObject
Context Stack
Existing nsIJSContextStack "@mozilla.org/js/xpc/ContextStack;1" service is replaced with language generic ContextStack. This is almost identical to the JS version, but all functions accept a 'language_id' param, to indicate which language they are interested in. Existing 'js' specific context stack could be re-implemented using the new one.
Probably need a new interface for each language to do their thing. The DOM will simply ask for a new context - something must do the delegation to the languages. XXX - but how do we know what languages we need to work with? Can we introduce new languages to the mix even after a context has been pushed?
What about http://lxr.mozilla.org/seamonkey/source/dom/public/nsIScriptContext.h#356 - it checks flags. Will there be flags for a single stack entry, or flags for each language in a single stack entry, or both?
- JS_GetContextPrivate ???
nsScriptLoader
nsScriptLoader's interface is language neutral.
- nsScriptLoadRequest will have either a nsIScriptContext or language ID as a member.
nsScriptLoaderObserver
Presumably will need to become script language aware. eg, scriptAvailable() method has text of the script passed - not much use without also knowing the language. Possibly replace with nsIScriptElement, which does have the language ID
nsDOMClassInfo
Lots of DOM namespace magic happens here, but it is very JS specific. We will:
- Create ns(I)DOMClassInfo. It contains all the existing code that works with nsDOMClassInfoData - that includes most of the "tables" defined using macros.
- nsDOMXPC will have all the JS specific code, which is currently implementing nsIXPCScriptable. This is the bulk of the existing nsDOMClassInfo implementation.
- This language neutral interface will allow language "helpers" to be explicitly installed, and will then be able to be fetched by the existing nsIClassInfo::GetLanguageHelper() function. Thus, anyone with an nsIDOMClassInfo will still be able to get the JS helper.
- Decide what to do with the various flags - eg, ALLOW_PROP_MODS_TO_PROTOTYPE. Presumably these will have some meaning to languages other than JS, but they are defined in a JS specific interface (nsIXPCScriptable)
The Python language implementation will then need to write an equivalent to the new nsDOMXPC. This will be the magic where attributes are got/set for objects, etc. This Python implementation will need to lean heavily on nsDOMClassInfo.
nsXULPrototypeScript
Change JSObject *mJSObject -> void *mScriptObject, and add new nsIScriptContext reference.
Event listeners
http://lxr.mozilla.org/seamonkey/source/content/events/src/nsEventListenerManager.cpp#1160
Changes required:
- AddScriptEventListener() needs a "language_id" passed in
- 'context' still comes from ScriptGlobal
- Use of "@mozilla.org/js/xpc/ContextStack;1" needs to be abstracted into the nsIScript interfaces.
Exceptions
Exceptions should be chained across languages. This should just mean diligent use of nsIExceptionService?
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
- Still a little gray on the relationship between a JSContext, a "scope" and a "global".
- JSContext a DOM specific concept, so script_language -> C++ -> script_language preserves the state of the globals (correct?)
- What is in a "context" beyond the global object?
- WrapNative and what it really means to this
- What exactly does nsDOMClassInfo do??? How can we factor it?
- 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.
nsDOMParser:
- Gets "native call context" to end up with nsIScriptContext - apparently all just to get the script URI nsDocShell
- http://lxr.mozilla.org/seamonkey/source/extensions/xmlextras/base/src/nsDOMParser.cpp#476
- Will need to know the language_id that should process the data from the stream
- Security related functions
nsXMLHttpRequest.cpp
* GetCurrentContext uses the JS ContextStack.
mozXMLTermUtils.cpp:
* mozXMLTermUtils::ExecuteScript called nsIScriptContext::ExecuteScript - but with NULL "void *" pointers. Needs to have nsIScriptContext passed?
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. Fairly generic compile function: http://lxr.mozilla.org/seamonkey/source/content/xbl/src/nsXBLProtoImpl.cpp#192
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.