Breaking the grip JS has on the DOM: Difference between revisions

Jump to navigation Jump to search
no edit summary
m (Reverted edit of Minglang, changed back to last version by MarkH)
No edit summary
Line 30: Line 30:
nsIScriptContext will grow new methods relating to:
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.
* 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 "WrapNative" process (or possibly this can be hidden behind JS 'SetProperty' functions?)


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 nsIScriptContext per language.  GetGlobalJSObject and GetContext would be deprecated, with a new method allowing the desired language to be specified. Thus, nsIScriptGlobalObject may have many nsIScriptContexts associated with it (one per supported and initialized language)


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.
A new 'context stack' will be invented:
* nsIScriptContexts to be used directly on the stack, rather than JSContext (hmm - is this necessary?  I guess a "void *" would still work so long as it was associated with the language)
* Contexts for *all* languages are pushed and poped as a single operation - a new context applies to all languages in the environment, not just a single oneThis is so when calls cross multiple language boundaries, they always have a correct context, not just the last one that happened to be pushed for their language.


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.
nsDOMClassInfo will need a fair bit of work, and significant help from Mozilla resources.  It may be able to 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 *".
Need to agree on a single "script language ID".  nsIProgrammingLanguage defines a set of integers, while nsIScriptElement uses a generic "char *". This document assumes an integer.


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:
 
=== new interface nsIScriptLanguage ===


Below are specific implementation notes:
* This is really only a factory for nsIScriptContext objects.  The nsIScriptContext itself becomes the factory for the language specific, void * "global object".


=== nsIScriptContext ===
=== nsIScriptContext ===
Line 48: Line 52:
* 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()
* Existing "void *aScopeObject" (ExecuteScript/EvaluateStringWithValue/CompileScript), replaced with nsIScriptGlobalObject.  The language impl can then get its "void *" via either sgo->GetLanguageContext() or provide a default via its own private means.
This may require a helper function to return a (temporary?) nsIScriptGlobalObject for a given JSObject - eg, plugin code, nsXBLProtoImplField::InstallMember.
[actually, the above may be gratuitious and cause unnecessary problems]


* Need a "WrapNative" type function
* Need a "WrapNative" type function (or maybe not - this may end up being able to be hidden behind Get/SetProperty functions?


* FinalizeClasses method - JS does the ClearScope/ClearWatchpointsForObject/ClearRegExpStatistics?
* 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
* Some kind of "SetProperty" function - as needed by nsGlobalWindow - "arguments", "navigator" etc.  http://lxr.mozilla.org/seamonkey/source/dom/src/base/nsGlobalWindow.cpp#871
* SetTerminationFunction needs thinking through - this does *not* seem to be a per-language thing.  Maybe could be on the nsIDOMContextStackItem proposed below?  Only few callers though.


=== nsIScriptGlobalObject / nsGlobalWindow ===
=== nsIScriptGlobalObject / nsGlobalWindow ===
Line 60: Line 68:
http://lxr.mozilla.org/seamonkey/source/dom/public/nsIScriptGlobalObject.h
http://lxr.mozilla.org/seamonkey/source/dom/public/nsIScriptGlobalObject.h


nsGlobalWindow is the main implemention of nsIScriptGlobalObject
nsGlobalWindow is the main implemention of nsIScriptGlobalObject, but XUL has one too.


* XXXX - Still need to understand GetNativeContext
* Still need to understand callers of GetNativeContext - they will not be language neutral


* 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.
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', ...)"
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 ensure the nsIScriptGlobal is initialized for a specific language.  This may be called at any time - whenever someone needs to run a script in a language.


* 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 75: Line 83:
** Properties fetched seem arbitrary.
** Properties fetched seem arbitrary.


* "Arguments" will need to be specified in a language neutral wayMaybe array of nsVariants?
* SetNewArguments will need to be specified as an nsISupports.  This trickles down into a number of things, including the concept of "extra args" nsGlobalWindow hasMay need to supply *2* nsISupports.
 
* New method to push all current contexts onto the context stack.


* Serializing scripts?
* Serializing scripts?
Line 84: Line 94:
   // is mis-typed as const char *  
   // is mis-typed as const char *  
   sets mJSObject
   sets mJSObject
=== nsDOMClassInfo ===
Note: Work on nsDOMClassInfo could be done by a Mozilla resource in parallel with this other work.  We will not need to use this enhanced class info until we already have Python code being executed and that code trying to work with a "dumb" DOM object.
One radical alternative: Invent a new "nsIDynamicClassInfo" or similar interface that allows the additional class info to be expressed.  Modify the JS XPConnect code to also use that new interface is supported regardless of implementor.  Python's xpcom code would do likewise.  nsIDOMClassInfo is responsible only for implementing this new interface.  It is possible this could also subsume your existing IDispatch support.
Notes assuming a less radical evolution of the existing code:
Lots of DOM namespace magic happens here, but it is very JS specific.  We will try to split the implementation:
* 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.
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.
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 following "public" functions are problematic:
* nsWindowSH::InvalidateGlobalScopePolluter referenced by nsGlobalWindow.
* GC
* oops - lost the other one!


=== Context Stack ===
=== 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 inExisting '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 operations work for the entire set of languages in use, not just the language about to be executed.
 
Probably need a new interface for each language to do their thing.  The DOM will simply ask for a new context, and the nsIScriptGlobal will push the context for each language it is initialized with.
 
Thus, we will have one ContextStack, with each holding an array of nsIScriptContexts - one for each language.  You can not push a context for only a single language on the stack.
 
If a language is initialized even after items are already on the context stack, only new items pushed will include a context item for that languageOnce the stack pops back past where a language has no entries, the language will not be able to run (but this should be impossible - there can be no stack entries for that language higher in the context stack, and attempting to start a new script in that language will simply re-push a context entry which will again include the language)
 
As mentioned above re nsIScriptContext, we may need to handle SetTerminationFunction functionality on this stack.
 
Tricky: We need to interoperate with code that is not multi-language aware, and may not be for some time.  Eg:
http://lxr.mozilla.org/seamonkey/source/extensions/pref/autoconfig/src/nsJSConfigTriggers.cpp#216
http://lxr.mozilla.org/seamonkey/source/extensions/webservices/security/src/nsWebScriptsAccess.cpp#767 - pushes a NULL context?
This probably will mean that the existing JS Context Stack will be a wrapper around the new cross-language 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?
Generic "GetCallerDocShell" or similar will avoid some JS specifics - eg, http://lxr.mozilla.org/seamonkey/source/docshell/base/nsDocShell.cpp#5898


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
checks flags.  Will there be flags for a single stack entry, or flags for each language in a single stack entry, or both?
checks flags.  Will there be flags for a single stack entry, or flags for each language in a single stack entry, or both?
==== New interface - nsIDOMContextStackItem ====
An item on the context stack - stores one nsIScriptContext for every language initialized for this item.
nsIDOMContextStackItem : nsISupports
{
  nsIScriptContext getLanguageContext(in PRInt32 langId);
  void setLanguageContext(in PRInt32 langId, in nsIScriptContext cx);
};
==== New interface - nsIDOMContextStack ====
interface nsIDOMContextStack : nsISupports
{
  readonly attribute PRInt32    count;
  nsIDOMContextStackItem      peek();
  nsIDOMContextStackItem      pop();
  void                                  push(in nsIDOMContextStackItem cx);
  /* what is a "safe context" anyway??? */
  nsIScriptContext getLanguageSafeContext(in PRInt32 langId);
 
  /* A helper for code that wants the most recent nsIDocShell on
  the context stack - any/all languages on the stack can provide it */
  nsIDocShell GetCallerDocShell()
};
==== Notes on callers of existing JS Context Stack ====
nsJSConfigTriggers.cpp: pushed and pops its own private JSContext.  Not nsIScript* aware at all.
nsWebScriptsAccess.cpp: pushes and pops an explicitly null context
nsXMLHttpRequest.cpp: appears to just push the context from its nsIScriptContext *mScriptContext, and fetch an nsIScriptContext from the context stack - can use the language independent function.  In NotifyEventListeners/ChangeState/OnChannelRedirect
nsScriptSecurityManager: Very JS specific already and will probably remain so.
nsDocShell: Use new GetCallerDocShell as described above.
LegacyPlugin.cpp: Does some private magic with context stacks.  Filename implies we can ignore this.
ns4xPlugin.cpp: some private js context work.
nsWindowWatcher: Generally looks at the top of the stack to try and locate a dynamic or static script global.  Some JS specifics though.
js/src/*: Lots of JS specific stuff as you expect
ProxyClassLoader.cpp: Peeks at the stack to get a context, to get a property.  Could use new property fetch functions.
nsProfile.cpp: Calls GetSafeJSContext just to force a GC.
nsContentUtils: See nsContentUtils discussion below.
nsRange.cpp::CreateContextualFragment - seems to push/pop a JS Context, and compares principals and contexts.
// If there's no JS or system JS running, push the current document's context on the JS context stack
What does that mean???
content/html - a few uses ignored.
nsLocation: Looks for existing context.  Some DynamicContext.


::JS_GetContextPrivate ???
::JS_GetContextPrivate ???
=== nsIScriptSecurityManager ===
All JSContext* can probably be replaced with nsIScriptContext.  Use of simple JObject* may need to be replaced with nsIScriptContext* and matching void*.


=== nsScriptLoader ===
=== nsScriptLoader ===
Line 106: Line 216:
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
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 ===
=== nsXULPrototypeScript ===
 
* Change JSObject *mJSObject -> void *mScriptObject.


Lots of DOM namespace magic happens here, but it is very JS specific. We will:
* Add language_id reference - caller must provide their own nsIScriptContext.


* Create ns(I)DOMClassInfoIt contains all the existing code that works with nsDOMClassInfoData - that includes most of the "tables" defined using macros.
* Deserializing etc needs to support multi-languagesMay require serialize/deserialize functions on the context?


* nsDOMXPC will have all the JS specific code, which is currently implementing nsIXPCScriptable.  This is the bulk of the existing nsDOMClassInfo implementation.
=== nsContentUtils ===


* 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.
Uses native JSContext for ReparentContentWrapper


* 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)
GetDocShellFromCaller/GetDocumentFromCaller - uses JS context stack to get the nsIScriptGlobal


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, etcThis Python implementation will need to lean heavily on nsDOMClassInfo.
nsCxPusher helper class: Peeks at JSContextStack to determine if a script is running (the simple fact an item is on the stack is all it checks)Pushes and pops a context.


nsContentUtils::NotifyXPCIfExceptionPending(JSContext* aCx) - 2 callers:
* content/xbl/src/nsXBLProtoImplMethod.cpp -  nsXBLProtoImplAnonymousMethod::Execute() - very JS specific
* dom/src/base/nsJSEnvironment.cpp


=== nsXULPrototypeScript ===
=== nsAppShellService ===


Change JSObject *mJSObject -> void *mScriptObject, and add new nsIScriptContext reference.
SetXPConnectSafeContext - seems to create and set the "safe context" for the context stack.


=== Event listeners ===
=== Event listeners ===
Line 132: Line 247:
* 'context' still comes from ScriptGlobal
* 'context' still comes from ScriptGlobal
* Use of "@mozilla.org/js/xpc/ContextStack;1" needs to be abstracted into the nsIScript interfaces.
* Use of "@mozilla.org/js/xpc/ContextStack;1" needs to be abstracted into the nsIScript interfaces.
=== nsDOMScriptObjectFactory ===
nsDOMScriptObjectFactory:::NewScriptContext may be unnecessary - just allow the ScriptGlobalObject to auto-create its own somehow.  There are multiple implementations, so may not be reasonable.


=== Exceptions ===
=== Exceptions ===
Line 151: Line 270:
* WrapNative and what it really means to this
* WrapNative and what it really means to this


* What exactly does nsDOMClassInfo do???  How can we factor it?
* Best way to tackle nsDOMClassInfo???


* nsBindingManager - very JS specific and not using nsIScript interfaces
* nsBindingManager - very JS specific and not using nsIScript interfaces
Line 163: Line 282:
== Random Notes ==
== Random Notes ==


Some notes about existing callers of certain interfaces.
Some notes about existing callers of certain nsIScriptContext.


nsDOMParser:
nsDOMParser:
Line 203: Line 322:
   
   
content/base/src/nsDocument.cpp:
content/base/src/nsDocument.cpp:
     Convert nsIScriptContext -> JS Context -> CanRunScripts
     Convert nsIScriptContext -> JS Context -> CanExecuteScripts
     (can CanRunScripts take an nsIScriptContext?)
     (can CanExecuteScripts take an nsIScriptContext?)
 
nsRange:
    nsRange::CreateContextualFragment - seems to push/pop a JS Context?


content/base/src/nsScriptLoader.cpp:
content/base/src/nsScriptLoader.cpp:
32

edits

Navigation menu