Allow JS 1.7 in XBL. Bug 389322, r=brendan, sr=jst, a=brendan
authorbzbarsky@mit.edu
Fri, 15 Feb 2008 21:13:16 -0800
changeset 11775 cdd4eeea471b53110a7c2ce66df67373dd596e1a
parent 11774 efd8900c768f38366ab39cceca00eacd058fbe66
child 11776 33054ccd27a968121bf419662b89b23ca19562ff
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersbrendan, jst, brendan
bugs389322
milestone1.9b4pre
Allow JS 1.7 in XBL. Bug 389322, r=brendan, sr=jst, a=brendan
content/events/src/nsEventListenerManager.cpp
content/html/content/src/nsHTMLScriptElement.cpp
content/xbl/src/nsXBLProtoImpl.cpp
content/xbl/src/nsXBLProtoImplField.cpp
content/xbl/src/nsXBLProtoImplMethod.cpp
content/xbl/src/nsXBLProtoImplProperty.cpp
content/xbl/src/nsXBLPrototypeHandler.cpp
content/xbl/test/Makefile.in
content/xbl/test/test_bug389322.xhtml
content/xul/content/src/nsXULElement.cpp
dom/public/nsIScriptContext.h
dom/src/base/nsJSEnvironment.cpp
dom/src/base/nsJSEnvironment.h
extensions/python/dom/nsdom/context.py
extensions/python/dom/src/nsPyContext.cpp
extensions/python/dom/src/nsPyContext.h
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -807,16 +807,17 @@ nsEventListenerManager::AddScriptEventLi
         PRUint32 argCount;
         const char **argNames;
         nsContentUtils::GetEventArgNames(nameSpace, aName, &argCount,
                                          &argNames);
 
         rv = context->CompileEventHandler(aName, argCount, argNames,
                                           aBody,
                                           url.get(), lineNo,
+                                          SCRIPTVERSION_DEFAULT, // for now?
                                           handler);
         if (rv == NS_ERROR_ILLEGAL_VALUE) {
           NS_WARNING("Probably a syntax error in the event handler!");
           return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
         }
         NS_ENSURE_SUCCESS(rv, rv);
         // And bind it.
         rv = context->BindCompiledEventHandler(aObject, scope,
@@ -1022,16 +1023,17 @@ nsEventListenerManager::CompileEventHand
         const char **argNames;
         nsContentUtils::GetEventArgNames(content->GetNameSpaceID(), aName,
                                          &argCount, &argNames);
 
         result = aContext->CompileEventHandler(aName,
                                                argCount, argNames,
                                                handlerBody,
                                                url.get(), lineNo,
+                                               SCRIPTVERSION_DEFAULT, // for now?
                                                handler);
         NS_ENSURE_SUCCESS(result, result);
         // And bind it.
         result = aContext->BindCompiledEventHandler(aObject, aScope,
                                                     aName, handler);
         NS_ENSURE_SUCCESS(result, result);
       }
 
--- a/content/html/content/src/nsHTMLScriptElement.cpp
+++ b/content/html/content/src/nsHTMLScriptElement.cpp
@@ -278,16 +278,17 @@ nsHTMLScriptEventHandler::Invoke(nsISupp
 
   rv = scriptContext->CompileFunction(scriptObject,
                                       funcName,   // method name
                                       argc,       // no of arguments
                                       args,       // argument names
                                       scriptBody, // script text
                                       nsnull,     // XXX: URL
                                       lineNumber, // line no (for errors)
+                                      JSVERSION_DEFAULT, // Default for now?
                                       PR_FALSE,   // shared ?
                                       &funcObject);
   // Free the argument names array if it was heap allocated...
   if (args != stackArgs) {
     delete [] args;
   }
 
   // Fail if there was an error compiling the script.
--- a/content/xbl/src/nsXBLProtoImpl.cpp
+++ b/content/xbl/src/nsXBLProtoImpl.cpp
@@ -78,22 +78,28 @@ nsXBLProtoImpl::InstallImplementation(ns
   void * targetClassObject = nsnull;
   nsresult rv = InitTargetObjects(aBinding, context, aBoundElement,
                                   getter_AddRefs(holder), &targetClassObject);
   NS_ENSURE_SUCCESS(rv, rv); // kick out if we were unable to properly intialize our target objects
 
   JSObject * targetScriptObject;
   holder->GetJSObject(&targetScriptObject);
 
+  JSContext *cx = (JSContext *)context->GetNativeContext();
+  // Set version up front so we don't thrash it
+  JSVersion oldVersion = ::JS_SetVersion(cx, JSVERSION_LATEST);
+  
   // Walk our member list and install each one in turn.
   for (nsXBLProtoImplMember* curr = mMembers;
        curr;
        curr = curr->GetNext())
     curr->InstallMember(context, aBoundElement, targetScriptObject,
                         targetClassObject, mClassName);
+
+  ::JS_SetVersion(cx, oldVersion);
   return NS_OK;
 }
 
 nsresult 
 nsXBLProtoImpl::InitTargetObjects(nsXBLPrototypeBinding* aBinding,
                                   nsIScriptContext* aContext, 
                                   nsIContent* aBoundElement, 
                                   nsIXPConnectJSObjectHolder** aScriptObjectHolder, 
@@ -166,41 +172,48 @@ nsXBLProtoImpl::CompilePrototypeMembers(
   // context.
   nsCOMPtr<nsIScriptGlobalObjectOwner> globalOwner(
       do_QueryInterface(aBinding->XBLDocumentInfo()));
   nsIScriptGlobalObject* globalObject = globalOwner->GetScriptGlobalObject();
   NS_ENSURE_TRUE(globalObject, NS_ERROR_UNEXPECTED);
 
   nsIScriptContext *context = globalObject->GetContext();
   NS_ENSURE_TRUE(context, NS_ERROR_OUT_OF_MEMORY);
+
+  JSContext *cx = (JSContext *)context->GetNativeContext();
   JSObject *global = globalObject->GetGlobalJSObject();
+  
 
   void* classObject;
-  nsresult rv = aBinding->InitClass(mClassName,
-                                    (JSContext *)context->GetNativeContext(),
-                                    global, global,
+  nsresult rv = aBinding->InitClass(mClassName, cx, global, global,
                                     &classObject);
   if (NS_FAILED(rv))
     return rv;
 
   mClassObject = (JSObject*) classObject;
   if (!mClassObject)
     return NS_ERROR_FAILURE;
 
+  // Set version up front so we don't thrash it
+  JSVersion oldVersion = ::JS_SetVersion(cx, JSVERSION_LATEST);
+
   // Now that we have a class object installed, we walk our member list and compile each of our
   // properties and methods in turn.
   for (nsXBLProtoImplMember* curr = mMembers;
        curr;
        curr = curr->GetNext()) {
     nsresult rv = curr->CompileMember(context, mClassName, mClassObject);
     if (NS_FAILED(rv)) {
       DestroyMembers();
+      ::JS_SetVersion(cx, oldVersion);
       return rv;
     }
   }
+
+  ::JS_SetVersion(cx, oldVersion);
   return NS_OK;
 }
 
 void
 nsXBLProtoImpl::Trace(TraceCallback aCallback, void *aClosure) const
 {
   // If we don't have a class object then we either didn't compile members
   // or we only have fields, in both cases there are no cycles through our
@@ -233,29 +246,33 @@ nsXBLProtoImpl::FindField(const nsString
   }
 
   return nsnull;
 }
 
 PRBool
 nsXBLProtoImpl::ResolveAllFields(JSContext *cx, JSObject *obj) const
 {
+  // Set version up front so we don't thrash it
+  JSVersion oldVersion = ::JS_SetVersion(cx, JSVERSION_LATEST);  
   for (nsXBLProtoImplField* f = mFields; f; f = f->GetNext()) {
     // Using OBJ_LOOKUP_PROPERTY is a pain, since what we have is a
     // PRUnichar* for the property name.  Let's just use the public API and
     // all.
     nsDependentString name(f->GetName());
     jsval dummy;
     if (!::JS_LookupUCProperty(cx, obj,
                                reinterpret_cast<const jschar*>(name.get()),
                                name.Length(), &dummy)) {
+      ::JS_SetVersion(cx, oldVersion);
       return PR_FALSE;
     }
   }
 
+  ::JS_SetVersion(cx, oldVersion);
   return PR_TRUE;
 }
 
 void
 nsXBLProtoImpl::UndefineFields(JSContext *cx, JSObject *obj) const
 {
   JSAutoRequest ar(cx);
   for (nsXBLProtoImplField* f = mFields; f; f = f->GetNext()) {
--- a/content/xbl/src/nsXBLProtoImplField.cpp
+++ b/content/xbl/src/nsXBLProtoImplField.cpp
@@ -123,17 +123,17 @@ nsXBLProtoImplField::InstallField(nsIScr
   // XXX Could we produce a better principal here?  Should be able
   // to, really!
   PRBool undefined;
   nsCOMPtr<nsIScriptContext> context = aContext;
   rv = context->EvaluateStringWithValue(nsDependentString(mFieldText,
                                                           mFieldTextLength), 
                                         aBoundNode,
                                         nsnull, uriSpec.get(),
-                                        mLineNumber, nsnull,
+                                        mLineNumber, JSVERSION_LATEST,
                                         (void*) &result, &undefined);
   if (NS_FAILED(rv))
     return rv;
 
   if (undefined) {
     result = JSVAL_VOID;
   }
 
--- a/content/xbl/src/nsXBLProtoImplMethod.cpp
+++ b/content/xbl/src/nsXBLProtoImplMethod.cpp
@@ -235,16 +235,17 @@ nsXBLProtoImplMethod::CompileMember(nsIS
   JSObject* methodObject = nsnull;
   nsresult rv = aContext->CompileFunction(aClassObject,
                                           cname,
                                           paramCount,
                                           (const char**)args,
                                           body, 
                                           functionUri.get(),
                                           uncompiledMethod->mBodyText.GetLineNumber(),
+                                          JSVERSION_LATEST,
                                           PR_TRUE,
                                           (void **) &methodObject);
 
   // Destroy our uncompiled method and delete our arg list.
   delete uncompiledMethod;
   delete [] args;
   if (NS_FAILED(rv)) {
     SetUncompiledMethod(nsnull);
--- a/content/xbl/src/nsXBLProtoImplProperty.cpp
+++ b/content/xbl/src/nsXBLProtoImplProperty.cpp
@@ -237,16 +237,17 @@ nsXBLProtoImplProperty::CompileMember(ns
       rv = aContext->CompileFunction(aClassObject,
                                      NS_LITERAL_CSTRING("get_") +
                                      NS_ConvertUTF16toUTF8(mName),
                                      0,
                                      nsnull,
                                      getter, 
                                      functionUri.get(),
                                      mGetterText->GetLineNumber(),
+                                     JSVERSION_LATEST,
                                      PR_TRUE,
                                      (void **) &getterObject);
 
       // Make sure we free mGetterText here before setting mJSGetterObject, since
       // that'll overwrite mGetterText
       delete mGetterText;
       deletedGetter = PR_TRUE;
       mJSGetterObject = getterObject;
@@ -286,16 +287,17 @@ nsXBLProtoImplProperty::CompileMember(ns
       rv = aContext->CompileFunction(aClassObject,
                                      NS_LITERAL_CSTRING("set_") +
                                      NS_ConvertUTF16toUTF8(mName),
                                      1,
                                      gPropertyArgs,
                                      setter, 
                                      functionUri.get(),
                                      mSetterText->GetLineNumber(),
+                                     JSVERSION_LATEST,
                                      PR_TRUE,
                                      (void **) &setterObject);
 
       // Make sure we free mSetterText here before setting mJSGetterObject, since
       // that'll overwrite mSetterText
       delete mSetterText;
       deletedSetter = PR_TRUE;
       mJSSetterObject = setterObject;
--- a/content/xbl/src/nsXBLPrototypeHandler.cpp
+++ b/content/xbl/src/nsXBLPrototypeHandler.cpp
@@ -369,18 +369,20 @@ nsXBLPrototypeHandler::EnsureEventHandle
   nsCAutoString bindingURI;
   mPrototypeBinding->DocURI()->GetSpec(bindingURI);
 
   PRUint32 argCount;
   const char **argNames;
   nsContentUtils::GetEventArgNames(kNameSpaceID_XBL, aName, &argCount,
                                    &argNames);
   nsresult rv = aBoundContext->CompileEventHandler(aName, argCount, argNames,
-                                                   handlerText, bindingURI.get(), 
-                                                   mLineNumber, aHandler);
+                                                   handlerText,
+                                                   bindingURI.get(), 
+                                                   mLineNumber,
+                                                   JSVERSION_LATEST, aHandler);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (pWindow) {
     pWindow->CacheXBLPrototypeHandler(this, aHandler);
   }
 
   return NS_OK;
 }
--- a/content/xbl/test/Makefile.in
+++ b/content/xbl/test/Makefile.in
@@ -49,16 +49,17 @@ include $(topsrcdir)/config/rules.mk
 _TEST_FILES =	\
 		test_bug296375.xul \
 		test_bug310107.html \
 		test_bug366770.html \
 		test_bug371724.xhtml \
 		test_bug372769.xhtml \
 		test_bug378866.xhtml \
 		test_bug397934.xhtml \
+		test_bug389322.xhtml \
 		test_bug398135.xhtml \
 		test_bug398492.xul \
 		test_bug400705.xhtml \
 		test_bug401907.xhtml \
 		test_bug403162.xhtml \
 		bug310107-resource.xhtml \
 		$(NULL)
 
new file mode 100644
--- /dev/null
+++ b/content/xbl/test/test_bug389322.xhtml
@@ -0,0 +1,126 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=389322
+-->
+<head>
+  <title>Test for Bug 389322</title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script>var ctorRan = false;</script>
+  <bindings xmlns="http://www.mozilla.org/xbl">
+    <binding id="test">
+      <implementation>
+        <field name="field"><![CDATA[
+          (function () {
+             try {
+               let (x=1) (x);
+               var success = true;
+             }
+             catch (e) { success = false; }
+             report("XBL fields", success)
+             return ""
+           }())
+        ]]></field>
+        <property name="property">
+          <getter><![CDATA[
+             try {
+               let (x=1) (x);
+               var success = true;
+             }
+             catch (e) { success = false; }
+             report("XBL property getters", success)
+             return 1
+           ]]></getter>
+          <setter><![CDATA[
+            try {
+              let (x=1) (x);
+              var success = true
+            }
+            catch (e) { success = false }
+            report("XBL property setters", success)
+            return val
+          ]]></setter>
+        </property>
+        <method name="method">
+          <body><![CDATA[
+            try {
+              let (x=1) (x);
+              var success = true;
+            }
+            catch (e) { success = false; }
+            report("XBL methods", success)
+          ]]></body>
+        </method>
+        <constructor><![CDATA[
+          this.property += 1
+          var x = this.field;
+          this.method()
+          try {
+            let (x=1) (x);
+            var success = true
+          }
+          catch (e) { success = false }
+          report("XBL constructors", success)
+        
+          var ev = document.createEvent("Events")
+          ev.initEvent("custom", false, false)
+          this.dispatchEvent(ev)
+          ctorRan = true;
+        ]]></constructor>
+      </implementation>
+      <handlers>
+        <handler action='
+          try {
+            let (x=1) (x);
+            var success = true
+          }
+          catch (e) { success = false }
+          report("XBL event handlers", success);
+        ' event="custom"/>
+      </handlers>
+    </binding>
+  </bindings>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=389322">Mozilla Bug 389322</a>
+<p id="display" style="-moz-binding: url(#test)"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+/** Test for Bug 389322 **/
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+  is(ctorRan, true, "Constructor should have run");
+});
+addLoadEvent(SimpleTest.finish);
+
+function report(testName, success) {
+  is(success, true, "JS 1.7 should work in " + testName);
+}
+]]>
+</script>
+<script type="text/javascript; version=1.7"><![CDATA[
+  try {
+    let (x=1) (x);
+    var success = true;
+  }
+  catch (e) { success = false; }
+  report("HTML script tags with explicit version", success)
+]]></script>
+<script type="text/javascript"><![CDATA[
+  try {
+    let (x=1) (x);
+    var success = false;
+  }
+  catch (e) { success = true; }
+  is(success, true, "JS 1.7 should not work in versionless HTML script tags");
+]]></script>
+</pre>
+</body>
+</html>
+
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -733,17 +733,19 @@ nsScriptEventHandlerOwnerTearoff::Compil
     }
 
     // Compile the event handler
     PRUint32 argCount;
     const char **argNames;
     nsContentUtils::GetEventArgNames(kNameSpaceID_XUL, aName, &argCount,
                                      &argNames);
     rv = context->CompileEventHandler(aName, argCount, argNames,
-                                      aBody, aURL, aLineNo, aHandler);
+                                      aBody, aURL, aLineNo,
+                                      SCRIPTVERSION_DEFAULT,  // for now?
+                                      aHandler);
     if (NS_FAILED(rv)) return rv;
 
     // XXX: Shouldn't this use context and not aContext?
     // XXXmarkh - is GetNativeGlobal() the correct scope?
     rv = aContext->BindCompiledEventHandler(aTarget, aContext->GetNativeGlobal(),
                                             aName, aHandler);
     if (NS_FAILED(rv)) return rv;
 
--- a/dom/public/nsIScriptContext.h
+++ b/dom/public/nsIScriptContext.h
@@ -52,19 +52,23 @@ class nsIArray;
 class nsIVariant;
 class nsIObjectInputStream;
 class nsIObjectOutputStream;
 class nsScriptObjectHolder;
 
 typedef void (*nsScriptTerminationFunc)(nsISupports* aRef);
 
 #define NS_ISCRIPTCONTEXT_IID \
-{ /* {52B46C37-A078-4952-AED7-035D83C810C0} */ \
-  0x52b46c37, 0xa078, 0x4952, \
-  {0xae, 0xd7, 0x3, 0x5d, 0x83, 0xc8, 0x10, 0xc0 } }
+{ /* {09316a0e-8d05-4d26-9efd-8f907a7c79d2} */ \
+  0x09316a0e, 0x8d05, 0x4d26, \
+ { 0x9e, 0xfd, 0x8f, 0x90, 0x7a, 0x7c, 0x79, 0xd2 } }
+
+/* This MUST match JSVERSION_DEFAULT.  This version stuff if we don't
+   know what language we have is a little silly... */
+#define SCRIPTVERSION_DEFAULT JSVERSION_DEFAULT
 
 /**
  * It is used by the application to initialize a runtime and run scripts.
  * A script runtime would implement this interface.
  * <P><I>It does have a bit too much java script information now, that
  * should be removed in a short time. Ideally this interface will be
  * language neutral</I>
  */
@@ -171,26 +175,29 @@ public:
    *
    * @param aName an nsIAtom pointer naming the function; it must be lowercase
    *        and ASCII, and should not be longer than 63 chars.  This bound on
    *        length is enforced only by assertions, so caveat caller!
    * @param aEventName the name that the event object should be bound to
    * @param aBody the event handler function's body
    * @param aURL the URL or filename for error messages
    * @param aLineNo the starting line number of the script for error messages
+   * @param aVersion the script language version to use when executing
    * @param aHandler the out parameter in which a void pointer to the compiled
    *        function object is stored on success
    *
    * @return NS_OK if the function body was valid and got compiled
    */
   virtual nsresult CompileEventHandler(nsIAtom* aName,
                                        PRUint32 aArgCount,
                                        const char** aArgNames,
                                        const nsAString& aBody,
-                                       const char* aURL, PRUint32 aLineNo,
+                                       const char* aURL,
+                                       PRUint32 aLineNo,
+                                       PRUint32 aVersion,
                                        nsScriptObjectHolder &aHandler) = 0;
 
   /**
    * Call the function object with given args and return its boolean result,
    * or true if the result isn't boolean.
    *
    * @param aTarget the event target
    * @param aScript an object telling the scope in which to call the compiled
@@ -250,16 +257,17 @@ public:
    **/
   virtual nsresult CompileFunction(void* aTarget,
                                    const nsACString& aName,
                                    PRUint32 aArgCount,
                                    const char** aArgArray,
                                    const nsAString& aBody,
                                    const char* aURL,
                                    PRUint32 aLineNo,
+                                   PRUint32 aVersion,
                                    PRBool aShared,
                                    void **aFunctionObject) = 0;
 
   /**
    * Set the default scripting language version for this context, which must
    * be a context specific to a particular scripting language.
    *
    **/
--- a/dom/src/base/nsJSEnvironment.cpp
+++ b/dom/src/base/nsJSEnvironment.cpp
@@ -294,16 +294,60 @@ NS_IMPL_ISUPPORTS1(nsCCMemoryPressureObs
 NS_IMETHODIMP
 nsCCMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic,
                                     const PRUnichar* aData)
 {
   nsJSContext::CC();
   return NS_OK;
 }
 
+class nsJSVersionSetter {
+public:
+  nsJSVersionSetter(JSContext *aContext, PRUint32 aVersion);
+  ~nsJSVersionSetter();
+
+private:
+  JSContext* mContext;
+  uint32 mOldOptions;
+  JSVersion mOldVersion;
+  JSBool mOptionsChanged;
+};
+
+nsJSVersionSetter::nsJSVersionSetter(JSContext *aContext, PRUint32 aVersion)
+  : mContext(aContext)
+{
+  // JSVERSION_HAS_XML may be set in our version mask - however, we can't
+  // simply pass this directly to JS_SetOptions as it masks out that bit -
+  // the only way to make this happen is via JS_SetOptions.
+  JSBool hasxml = (aVersion & JSVERSION_HAS_XML) != 0;
+  mOldOptions = ::JS_GetOptions(mContext);
+  mOptionsChanged = ((hasxml) ^ !!(mOldOptions & JSOPTION_XML));
+
+  if (mOptionsChanged) {
+    ::JS_SetOptions(mContext,
+                    hasxml
+                    ? mOldOptions | JSOPTION_XML
+                    : mOldOptions & ~JSOPTION_XML);
+  }
+
+  // Change the version - this is cheap when the versions match, so no need
+  // to optimize here...
+  JSVersion newVer = (JSVersion)(aVersion & JSVERSION_MASK);
+  mOldVersion = ::JS_SetVersion(mContext, newVer);
+}
+
+nsJSVersionSetter::~nsJSVersionSetter()
+{
+  ::JS_SetVersion(mContext, mOldVersion);
+
+  if (mOptionsChanged) {
+      ::JS_SetOptions(mContext, mOldOptions);
+  }
+}
+
 
 /****************************************************************
  ************************** AutoFree ****************************
  ****************************************************************/
 
 class AutoFree {
 public:
   AutoFree(void *aPtr) : mPtr(aPtr) {
@@ -1255,55 +1299,32 @@ nsJSContext::EvaluateStringWithValue(con
     return NS_ERROR_FAILURE;
   }
 
   jsval val;
 
   nsJSContext::TerminationFuncHolder holder(this);
 
   // SecurityManager said "ok", but don't compile if aVersion is unknown.
-  // Do compile with the default version (and avoid thrashing the context's
-  // version) if aVersion is the default.
-  // As the caller is responsible for parsing the version strings, we just
+  // Since the caller is responsible for parsing the version strings, we just
   // check it isn't JSVERSION_UNKNOWN.
   if (ok && ((JSVersion)aVersion) != JSVERSION_UNKNOWN) {
-    // JSVERSION_HAS_XML may be set in our version mask - however, we can't
-    // simply pass this directly to JS_SetOptions as it masks out that bit -
-    // the only way to make this happen is via JS_SetOptions.
-    JSBool hasxml = (aVersion & JSVERSION_HAS_XML) != 0;
-    uint32 jsoptions = ::JS_GetOptions(mContext);
-    JSBool optionsChanged = ((hasxml) ^ !!(jsoptions & JSOPTION_XML));
-
-    if (optionsChanged) {
-      ::JS_SetOptions(mContext,
-                      hasxml
-                      ? jsoptions | JSOPTION_XML
-                      : jsoptions & ~JSOPTION_XML);
-    }
-    // Change the version - this is cheap when the versions match, so no need
-    // to optimize here...
-    JSVersion newVer = (JSVersion)(aVersion & JSVERSION_MASK);
-    JSVersion oldVer = ::JS_SetVersion(mContext, newVer);
+
     JSAutoRequest ar(mContext);
+    nsJSVersionSetter setVersion(mContext, aVersion);
 
     ok = ::JS_EvaluateUCScriptForPrincipals(mContext,
                                             (JSObject *)aScopeObject,
                                             jsprin,
                                             (jschar*)PromiseFlatString(aScript).get(),
                                             aScript.Length(),
                                             aURL,
                                             aLineNo,
                                             &val);
 
-    ::JS_SetVersion(mContext, oldVer);
-
-    if (optionsChanged) {
-      ::JS_SetOptions(mContext, jsoptions);
-    }
-
     if (!ok) {
         // Tell XPConnect about any pending exceptions. This is needed
         // to avoid dropping JS exceptions in case we got here through
         // nested calls through XPConnect.
 
         nsContentUtils::NotifyXPCIfExceptionPending(mContext);
     }
   }
@@ -1458,55 +1479,31 @@ nsJSContext::EvaluateString(const nsAStr
   //
   // TODO: use JS_Begin/EndRequest to keep the GC from racing with JS
   // execution on any thread.
   jsval val;
 
   nsJSContext::TerminationFuncHolder holder(this);
 
   // SecurityManager said "ok", but don't compile if aVersion is unknown.
-  // Do compile with the default version (and avoid thrashing the context's
-  // version) if aVersion is the default.
-  // As the caller is responsible for parsing the version strings, we just
+  // Since the caller is responsible for parsing the version strings, we just
   // check it isn't JSVERSION_UNKNOWN.
   if (ok && ((JSVersion)aVersion) != JSVERSION_UNKNOWN) {
     JSAutoRequest ar(mContext);
-    // JSVERSION_HAS_XML may be set in our version mask - however, we can't
-    // simply pass this directly to JS_SetOptions as it masks out that bit -
-    // the only way to make this happen is via JS_SetOptions.
-    JSBool hasxml = (aVersion & JSVERSION_HAS_XML) != 0;
-    uint32 jsoptions = ::JS_GetOptions(mContext);
-    JSBool optionsChanged = ((hasxml) ^ !!(jsoptions & JSOPTION_XML));
-
-    if (optionsChanged) {
-      ::JS_SetOptions(mContext,
-                      hasxml
-                      ? jsoptions | JSOPTION_XML
-                      : jsoptions & ~JSOPTION_XML);
-    }
-    // Change the version - this is cheap when the versions match, so no need
-    // to optimize here...
-    JSVersion newVer = (JSVersion)(aVersion & JSVERSION_MASK);
-    JSVersion oldVer = ::JS_SetVersion(mContext, newVer);
+    nsJSVersionSetter setVersion(mContext, aVersion);
 
     ok = ::JS_EvaluateUCScriptForPrincipals(mContext,
                                               (JSObject *)aScopeObject,
                                               jsprin,
                                               (jschar*)PromiseFlatString(aScript).get(),
                                               aScript.Length(),
                                               aURL,
                                               aLineNo,
                                               &val);
 
-    ::JS_SetVersion(mContext, oldVer);
-
-    if (optionsChanged) {
-      ::JS_SetOptions(mContext, jsoptions);
-    }
-
     if (!ok) {
         // Tell XPConnect about any pending exceptions. This is needed
         // to avoid dropping JS exceptions in case we got here through
         // nested calls through XPConnect.
 
         nsContentUtils::NotifyXPCIfExceptionPending(mContext);
     }
   }
@@ -1567,39 +1564,21 @@ nsJSContext::CompileScript(const PRUnich
   if (NS_FAILED(rv)) {
     JSPRINCIPALS_DROP(mContext, jsprin);
     return NS_ERROR_FAILURE;
   }
 
   aScriptObject.drop(); // ensure old object not used on failure...
 
   // SecurityManager said "ok", but don't compile if aVersion is unknown.
-  // Do compile with the default version (and avoid thrashing the context's
-  // version) if aVersion is the default.
-  // As the caller is responsible for parsing the version strings, we just
+  // Since the caller is responsible for parsing the version strings, we just
   // check it isn't JSVERSION_UNKNOWN.
   if (ok && ((JSVersion)aVersion) != JSVERSION_UNKNOWN) {
-    // JSVERSION_HAS_XML may be set in our version mask - however, we can't
-    // simply pass this directly to JS_SetOptions as it masks out that bit -
-    // the only way to make this happen is via JS_SetOptions.
     JSAutoRequest ar(mContext);
-    JSBool hasxml = (aVersion & JSVERSION_HAS_XML) != 0;
-    uint32 jsoptions = ::JS_GetOptions(mContext);
-    JSBool optionsChanged = ((hasxml) ^ !!(jsoptions & JSOPTION_XML));
-
-    if (optionsChanged) {
-      ::JS_SetOptions(mContext,
-                      hasxml
-                      ? jsoptions | JSOPTION_XML
-                      : jsoptions & ~JSOPTION_XML);
-    }
-    // Change the version - this is cheap when the versions match, so no need
-    // to optimize here...
-    JSVersion newVer = (JSVersion)(aVersion & JSVERSION_MASK);
-    JSVersion oldVer = ::JS_SetVersion(mContext, newVer);
+    nsJSVersionSetter setVersion(mContext, aVersion);
 
     JSScript* script =
         ::JS_CompileUCScriptForPrincipals(mContext,
                                           (JSObject *)aScopeObject,
                                           jsprin,
                                           (jschar*) aText,
                                           aTextLength,
                                           aURL,
@@ -1609,23 +1588,18 @@ nsJSContext::CompileScript(const PRUnich
       if (scriptObject) {
         NS_ASSERTION(aScriptObject.getScriptTypeID()==JAVASCRIPT,
                      "Expecting JS script object holder");
         rv = aScriptObject.set(scriptObject);
       } else {
         ::JS_DestroyScript(mContext, script);
         script = nsnull;
       }
-    } else
+    } else {
       rv = NS_ERROR_OUT_OF_MEMORY;
-
-    ::JS_SetVersion(mContext, oldVer);
-
-    if (optionsChanged) {
-      ::JS_SetOptions(mContext, jsoptions);
     }
   }
 
   // Whew!  Finally done.
   JSPRINCIPALS_DROP(mContext, jsprin);
   return rv;
 }
 
@@ -1755,33 +1729,42 @@ nsJSContext::JSObjectFromInterface(nsISu
 
 
 nsresult
 nsJSContext::CompileEventHandler(nsIAtom *aName,
                                  PRUint32 aArgCount,
                                  const char** aArgNames,
                                  const nsAString& aBody,
                                  const char *aURL, PRUint32 aLineNo,
+                                 PRUint32 aVersion,
                                  nsScriptObjectHolder &aHandler)
 {
   NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
 
   if (!sSecurityManager) {
     NS_ERROR("Huh, we need a script security manager to compile "
              "an event handler!");
 
     return NS_ERROR_UNEXPECTED;
   }
 
+  // Don't compile if aVersion is unknown.  Since the caller is responsible for
+  // parsing the version strings, we just check it isn't JSVERSION_UNKNOWN.
+  if ((JSVersion)aVersion == JSVERSION_UNKNOWN) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
   const char *charName = AtomToEventHandlerName(aName);
 
   // Event handlers are always shared, and must be bound before use.
   // Therefore we never bother compiling with principals.
   // (that probably means we should avoid JS_CompileUCFunctionForPrincipals!)
   JSAutoRequest ar(mContext);
+  nsJSVersionSetter setVersion(mContext, aVersion);
+
   JSFunction* fun =
       ::JS_CompileUCFunctionForPrincipals(mContext,
                                           nsnull, nsnull,
                                           charName, aArgCount, aArgNames,
                                           (jschar*)PromiseFlatString(aBody).get(),
                                           aBody.Length(),
                                           aURL, aLineNo);
 
@@ -1800,21 +1783,28 @@ nsJSContext::CompileEventHandler(nsIAtom
 nsresult
 nsJSContext::CompileFunction(void* aTarget,
                              const nsACString& aName,
                              PRUint32 aArgCount,
                              const char** aArgArray,
                              const nsAString& aBody,
                              const char* aURL,
                              PRUint32 aLineNo,
+                             PRUint32 aVersion,
                              PRBool aShared,
                              void** aFunctionObject)
 {
   NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
 
+  // Don't compile if aVersion is unknown.  Since the caller is responsible for
+  // parsing the version strings, we just check it isn't JSVERSION_UNKNOWN.
+  if ((JSVersion)aVersion == JSVERSION_UNKNOWN) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
   JSPrincipals *jsprin = nsnull;
 
   nsIScriptGlobalObject *global = GetGlobalObject();
   if (global) {
     // XXXbe why the two-step QI? speed up via a new GetGlobalObjectData func?
     nsCOMPtr<nsIScriptObjectPrincipal> globalData = do_QueryInterface(global);
     if (globalData) {
       nsIPrincipal *prin = globalData->GetPrincipal();
@@ -1822,16 +1812,17 @@ nsJSContext::CompileFunction(void* aTarg
         return NS_ERROR_FAILURE;
       prin->GetJSPrincipals(mContext, &jsprin);
     }
   }
 
   JSObject *target = (JSObject*)aTarget;
 
   JSAutoRequest ar(mContext);
+  nsJSVersionSetter setVersion(mContext, aVersion);
 
   JSFunction* fun =
       ::JS_CompileUCFunctionForPrincipals(mContext,
                                           aShared ? nsnull : target, jsprin,
                                           PromiseFlatCString(aName).get(),
                                           aArgCount, aArgArray,
                                           (jschar*)PromiseFlatString(aBody).get(),
                                           aBody.Length(),
--- a/dom/src/base/nsJSEnvironment.h
+++ b/dom/src/base/nsJSEnvironment.h
@@ -94,16 +94,17 @@ public:
                                  nsAString* aRetValue,
                                  PRBool* aIsUndefined);
 
   virtual nsresult CompileEventHandler(nsIAtom *aName,
                                        PRUint32 aArgCount,
                                        const char** aArgNames,
                                        const nsAString& aBody,
                                        const char *aURL, PRUint32 aLineNo,
+                                       PRUint32 aVersion,
                                        nsScriptObjectHolder &aHandler);
   virtual nsresult CallEventHandler(nsISupports* aTarget, void *aScope,
                                     void* aHandler,
                                     nsIArray *argv, nsIVariant **rv);
   virtual nsresult BindCompiledEventHandler(nsISupports *aTarget,
                                             void *aScope,
                                             nsIAtom *aName,
                                             void *aHandler);
@@ -112,16 +113,17 @@ public:
                                         nsScriptObjectHolder &aHandler);
   virtual nsresult CompileFunction(void* aTarget,
                                    const nsACString& aName,
                                    PRUint32 aArgCount,
                                    const char** aArgArray,
                                    const nsAString& aBody,
                                    const char* aURL,
                                    PRUint32 aLineNo,
+                                   PRUint32 aVersion,
                                    PRBool aShared,
                                    void** aFunctionObject);
 
   virtual void SetDefaultLanguageVersion(PRUint32 aVersion);
   virtual nsIScriptGlobalObject *GetGlobalObject();
   virtual void *GetNativeContext();
   virtual void *GetNativeGlobal();
   virtual nsresult CreateNativeGlobalForInner(
--- a/extensions/python/dom/nsdom/context.py
+++ b/extensions/python/dom/nsdom/context.py
@@ -440,17 +440,17 @@ class ScriptContext:
         assert type(scriptObject) == types.CodeType, \
                "Script object should be a code object (got %r)" % (scriptObject,)
         exec scriptObject in scopeObject
 
     def CompileScript(self, text, scopeObject, principal, url, lineno, version):
         # The line number passed is the first; offset is -1
         return domcompile.compile(text, url, lineno=lineno-1)
 
-    def CompileEventHandler(self, name, argNames, body, url, lineno):
+    def CompileEventHandler(self, name, argNames, body, url, lineno, version):
         if __debug__:
             logger.debug("%s.CompileEventHandler %s %s:%s ('%s')",
                          self, name, url, lineno, body[:100])
         co = domcompile.compile_function(body, url, name, argNames,
                                          lineno=lineno-1)
         g = {}
         exec co in g
         handler = g[name]
--- a/extensions/python/dom/src/nsPyContext.cpp
+++ b/extensions/python/dom/src/nsPyContext.cpp
@@ -470,16 +470,17 @@ nsPythonContext::CompileScript(const PRU
 }
 
 nsresult
 nsPythonContext::CompileEventHandler(nsIAtom *aName,
                                  PRUint32 aArgCount,
                                  const char** aArgNames,
                                  const nsAString& aBody,
                                  const char *aURL, PRUint32 aLineNo,
+                                 PRUint32 aVersion;
                                  nsScriptObjectHolder &aHandler)
 {
   NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
   NS_TIMELINE_MARK_FUNCTION("nsPythonContext::CompileEventHandler");
 
   NS_ASSERTION(mDelegate, "Script context has no delegate");
   NS_ENSURE_TRUE(mDelegate, NS_ERROR_UNEXPECTED);
 
@@ -493,17 +494,17 @@ nsPythonContext::CompileEventHandler(nsI
   for (PRUint32 i=0;i<aArgCount;i++) {
     PyList_SET_ITEM(argNames, i, PyString_FromString(aArgNames[i]));
   }
   PyObject *ret = PyObject_CallMethod(mDelegate, "CompileEventHandler",
                                       "sNNsi",
                                       AtomToEventHandlerName(aName),
                                       argNames,
                                       PyObject_FromNSString(aBody),
-                                      aURL, aLineNo);
+                                      aURL, aLineNo, aVersion);
   if (!ret)
     return HandlePythonError();
 
   NS_ASSERTION(aHandler.getScriptTypeID()==nsIProgrammingLanguage::PYTHON,
                "Expecting Python script object holder");
   aHandler.set(ret);
   Py_DECREF(ret);
   return NS_OK;
@@ -538,16 +539,17 @@ nsPythonContext::BindCompiledEventHandle
 nsresult
 nsPythonContext::CompileFunction(void* aTarget,
                              const nsACString& aName,
                              PRUint32 aArgCount,
                              const char** aArgArray,
                              const nsAString& aBody,
                              const char* aURL,
                              PRUint32 aLineNo,
+                             PRUint32 aVersion,
                              PRBool aShared,
                              void** aFunctionObject)
 {
   NS_TIMELINE_MARK_FUNCTION("nsPythonContext::CompileFunction");
   NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
 
   NS_ERROR("CompileFunction not implemented");
   return NS_ERROR_NOT_IMPLEMENTED;
--- a/extensions/python/dom/src/nsPyContext.h
+++ b/extensions/python/dom/src/nsPyContext.h
@@ -142,16 +142,17 @@ public:
                                  nsAString* aRetValue,
                                  PRBool* aIsUndefined);
   virtual nsresult CompileEventHandler(nsIAtom *aName,
                                        PRUint32 aArgCount,
                                        const char** aArgNames,
                                        const nsAString& aBody,
                                        const char *aURL,
                                        PRUint32 aLineNo,
+                                       PRUint32 aVersion,
                                        nsScriptObjectHolder &aHandler);
   virtual nsresult CallEventHandler(nsISupports* aTarget, void *aScope,
                                     void* aHandler,
                                     nsIArray *argv, nsIVariant **rv);
   virtual nsresult BindCompiledEventHandler(nsISupports*aTarget, void *aScope,
                                             nsIAtom *aName,
                                             void *aHandler);
   virtual nsresult GetBoundEventHandler(nsISupports* aTarget, void *aScope,
@@ -159,16 +160,17 @@ public:
                                         nsScriptObjectHolder &aHandler);
   virtual nsresult CompileFunction(void* aTarget,
                                    const nsACString& aName,
                                    PRUint32 aArgCount,
                                    const char** aArgArray,
                                    const nsAString& aBody,
                                    const char* aURL,
                                    PRUint32 aLineNo,
+                                   PRUint32 aVersion,
                                    PRBool aShared,
                                    void** aFunctionObject);
 
   virtual void SetDefaultLanguageVersion(PRUint32 aVersion);
   virtual nsIScriptGlobalObject *GetGlobalObject();
   virtual void *GetNativeContext();
   virtual void *GetNativeGlobal();
   virtual nsresult CreateNativeGlobalForInner(