Bug 753885 - Part 2: Add JSOPTION_ALLOW_XML, making E4X support optional per-context. r=Waldo.
authorJason Orendorff <jorendorff@mozilla.com>
Wed, 30 May 2012 15:05:59 -0500
changeset 95313 9be14c2b115eb9f985d86987ba06dc9b548ce303
parent 95312 11c7f20bf242f109eec2058a03a84f1b99f4b2e8
child 95314 a780bb0de69526f27a0d48f89ceb6cf77fe3e88a
push id22801
push useremorley@mozilla.com
push dateThu, 31 May 2012 12:15:52 +0000
treeherdermozilla-central@d6ae9ef0eb50 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs753885
milestone15.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 753885 - Part 2: Add JSOPTION_ALLOW_XML, making E4X support optional per-context. r=Waldo.
content/base/src/nsFrameMessageManager.cpp
dom/base/nsJSEnvironment.cpp
dom/workers/RuntimeService.cpp
js/jsd/jsd_high.c
js/src/frontend/Parser.h
js/src/frontend/TokenStream.cpp
js/src/frontend/TokenStream.h
js/src/jit-test/lib/prolog.js
js/src/jit-test/tests/basic/bug753885-1.js
js/src/jsapi-tests/testDebugger.cpp
js/src/jsapi-tests/testVersion.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.h
js/src/shell/js.cpp
js/src/tests/shell.js
js/src/vm/Xdr.h
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/shell/xpcshell.cpp
js/xpconnect/src/XPCComponents.cpp
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -826,17 +826,19 @@ nsFrameScriptExecutor::InitTabChildGloba
 
   JSContext* cx = JS_NewContext(rt, 8192);
   NS_ENSURE_TRUE(cx, false);
 
   mCx = cx;
 
   nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
 
-  JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_PRIVATE_IS_NSISUPPORTS);
+  JS_SetOptions(cx, JS_GetOptions(cx) |
+                    JSOPTION_PRIVATE_IS_NSISUPPORTS |
+                    JSOPTION_ALLOW_XML);
   JS_SetVersion(cx, JSVERSION_LATEST);
   JS_SetErrorReporter(cx, ContentScriptErrorReporter);
 
   xpc_LocalizeContext(cx);
 
   JSAutoRequest ar(cx);
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
   const PRUint32 flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES |
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -949,17 +949,17 @@ nsJSContext::JSOptionChangedCallback(con
     xr->GetInSafeMode(&safeMode);
     if (safeMode) {
       useMethodJIT = false;
       usePCCounts = false;
       useTypeInference = false;
       useMethodJITAlways = true;
       useHardening = false;
     }
-  }    
+  }
 
   if (useMethodJIT)
     newDefaultJSOptions |= JSOPTION_METHODJIT;
   else
     newDefaultJSOptions &= ~JSOPTION_METHODJIT;
 
   if (usePCCounts)
     newDefaultJSOptions |= JSOPTION_PCCOUNT;
@@ -993,17 +993,18 @@ nsJSContext::JSOptionChangedCallback(con
     newDefaultJSOptions &= ~JSOPTION_WERROR;
 
   bool relimit = Preferences::GetBool(js_relimit_option_str);
   if (relimit)
     newDefaultJSOptions |= JSOPTION_RELIMIT;
   else
     newDefaultJSOptions &= ~JSOPTION_RELIMIT;
 
-  ::JS_SetOptions(context->mContext, newDefaultJSOptions & JSRUNOPTION_MASK);
+  ::JS_SetOptions(context->mContext,
+                  newDefaultJSOptions & (JSRUNOPTION_MASK | JSOPTION_ALLOW_XML));
 
   // Save the new defaults for the next page load (InitContext).
   context->mDefaultJSOptions = newDefaultJSOptions;
 
   JSRuntime *rt = JS_GetRuntime(context->mContext);
   JS_SetJitHardening(rt, useHardening);
 
 #ifdef JS_GC_ZEAL
@@ -1025,17 +1026,17 @@ nsJSContext::nsJSContext(JSRuntime *aRun
   mPrev = &sContextList;
   if (sContextList) {
     sContextList->mPrev = &mNext;
   }
   sContextList = this;
 
   ++sContextCount;
 
-  mDefaultJSOptions = JSOPTION_PRIVATE_IS_NSISUPPORTS;
+  mDefaultJSOptions = JSOPTION_PRIVATE_IS_NSISUPPORTS | JSOPTION_ALLOW_XML;
 
   mContext = ::JS_NewContext(aRuntime, gStackSize);
   if (mContext) {
     ::JS_SetContextPrivate(mContext, static_cast<nsIScriptContext *>(this));
 
     // Preserve any flags the context callback might have set.
     mDefaultJSOptions |= ::JS_GetOptions(mContext);
 
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -209,16 +209,17 @@ PrefCallback(const char* aPrefName, void
       newOptions |= JSOPTION_METHODJIT;
     }
     if (Preferences::GetBool(gPrefsToWatch[PREF_methodjit_always])) {
       newOptions |= JSOPTION_METHODJIT_ALWAYS;
     }
     if (Preferences::GetBool(gPrefsToWatch[PREF_typeinference])) {
       newOptions |= JSOPTION_TYPE_INFERENCE;
     }
+    newOptions |= JSOPTION_ALLOW_XML;
 
     RuntimeService::SetDefaultJSContextOptions(newOptions);
     rts->UpdateAllWorkerJSContextOptions();
   }
 #ifdef JS_GC_ZEAL
   else if (!strcmp(aPrefName, gPrefsToWatch[PREF_gczeal])) {
     PRInt32 gczeal = Preferences::GetInt(gPrefsToWatch[PREF_gczeal]);
     RuntimeService::SetDefaultGCZeal(PRUint8(clamped(gczeal, 0, 3)));
--- a/js/jsd/jsd_high.c
+++ b/js/jsd/jsd_high.c
@@ -101,16 +101,17 @@ static JSDContext*
     if( ! jsd_InitScriptManager(jsdc) )
         goto label_newJSDContext_failure;
 
     jsdc->dumbContext = JS_NewContext(jsdc->jsrt, 256);
     if( ! jsdc->dumbContext )
         goto label_newJSDContext_failure;
 
     JS_BeginRequest(jsdc->dumbContext);
+    JS_SetOptions(jsdc->dumbContext, JS_GetOptions(jsdc->dumbContext) | JSOPTION_ALLOW_XML);
 
     jsdc->glob = JS_NewCompartmentAndGlobalObject(jsdc->dumbContext, &global_class, NULL);
 
     if( ! jsdc->glob )
         goto label_newJSDContext_failure;
 
     call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, jsdc->glob);
     if( ! call )
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -227,17 +227,17 @@ struct Parser : private AutoGCRooter
     ParseNode *destructuringExpr(BindData *data, TokenKind tt);
 
     bool checkForFunctionNode(PropertyName *name, ParseNode *node);
 
     ParseNode *identifierName(bool afterDoubleDot);
 
 #if JS_HAS_XML_SUPPORT
     // True if E4X syntax is allowed in the current syntactic context.
-    bool allowsXML() const { return !tc->sc->inStrictMode(); }
+    bool allowsXML() const { return !tc->sc->inStrictMode() && tokenStream.allowsXML(); }
 
     ParseNode *endBracketedExpr();
 
     ParseNode *propertySelector();
     ParseNode *qualifiedSuffix(ParseNode *pn);
     ParseNode *qualifiedIdentifier();
     ParseNode *attributeIdentifier();
     ParseNode *xmlExpr(JSBool inTag);
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -134,16 +134,17 @@ TokenStream::TokenStream(JSContext *cx, 
     prevLinebaseRoot(cx, &prevLinebase),
     userbuf(base, length),
     userbufRoot(cx, &userbuf),
     filename(fn),
     sourceMap(NULL),
     listenerTSData(),
     tokenbuf(cx),
     version(v),
+    allowXML(VersionHasAllowXML(v)),
     moarXML(VersionHasMoarXML(v)),
     cx(cx),
     originPrincipals(JSScript::normalizeOriginPrincipals(prin, originPrin)),
     strictModeGetter(smg)
 {
     if (originPrincipals)
         JS_HoldPrincipals(originPrincipals);
 
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -462,17 +462,17 @@ class TokenStream
         return type == type1 || type == type2;
     }
     const CharBuffer &getTokenbuf() const { return tokenbuf; }
     const char *getFilename() const { return filename; }
     unsigned getLineno() const { return lineno; }
     /* Note that the version and hasMoarXML can get out of sync via setMoarXML. */
     JSVersion versionNumber() const { return VersionNumber(version); }
     JSVersion versionWithFlags() const { return version; }
-    bool allowsXML() const { return !isStrictMode(); }
+    bool allowsXML() const { return allowXML && !isStrictMode(); }
     bool hasMoarXML() const { return moarXML || VersionShouldParseXML(versionNumber()); }
     void setMoarXML(bool enabled) { moarXML = enabled; }
 
     bool isCurrentTokenEquality() const {
         return TokenKindIsEquality(currentToken().type);
     }
 
     bool isCurrentTokenRelational() const {
@@ -792,16 +792,17 @@ class TokenStream
     const char          *filename;      /* input filename or null */
     jschar              *sourceMap;     /* source map's filename or null */
     void                *listenerTSData;/* listener data for this TokenStream */
     CharBuffer          tokenbuf;       /* current token string buffer */
     int8_t              oneCharTokens[128];  /* table of one-char tokens */
     bool                maybeEOL[256];       /* probabilistic EOL lookup table */
     bool                maybeStrSpecial[256];/* speeds up string scanning */
     JSVersion           version;        /* (i.e. to identify keywords) */
+    bool                allowXML;       /* see JSOPTION_ALLOW_XML */
     bool                moarXML;        /* see JSOPTION_MOAR_XML */
     JSContext           *const cx;
     JSPrincipals        *const originPrincipals;
     StrictModeGetter    *strictModeGetter; /* used to test for strict mode */
 };
 
 struct KeywordInfo {
     const char  *chars;         /* C string with keyword text */
--- a/js/src/jit-test/lib/prolog.js
+++ b/js/src/jit-test/lib/prolog.js
@@ -18,8 +18,11 @@ if (!("schedulegc" in this)) {
 
 if (!("gcslice" in this)) {
   gcslice = function() { }
 }
 
 if (!("selectforgc" in this)) {
   selectforgc = function() { }
 }
+
+if ("options" in this)
+    options("allow_xml");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug753885-1.js
@@ -0,0 +1,16 @@
+// XML syntax is not available unless JSOPTION_ALLOW_XML is enabled.
+
+load(libdir + "asserts.js");
+
+var exprs = ["<a/>", "x..y", "x.@id", "x.(@id === '13')", "x.*", "x.function::length",
+             "function::parseInt", "a::b"];
+
+assertEq(options().split(",").indexOf("allow_xml") >= 0, true);
+
+options("allow_xml");  // turn it off
+for (var e of exprs)
+    assertThrowsInstanceOf(function () { Function("return " + e + ";"); }, SyntaxError);
+
+options("allow_xml");  // turn it back on
+for (var e of exprs)
+    assertEq(typeof Function("return " + e + ";"), "function");
--- a/js/src/jsapi-tests/testDebugger.cpp
+++ b/js/src/jsapi-tests/testDebugger.cpp
@@ -123,17 +123,18 @@ ThrowHook(JSContext *cx, JSScript *, jsb
     jsval _;
     JS_EvaluateScript(cx, global, text, strlen(text), "", 0, &_);
 
     return JSTRAP_CONTINUE;
 }
 
 BEGIN_TEST(testDebugger_throwHook)
 {
-    uint32_t newopts = JS_GetOptions(cx) | JSOPTION_METHODJIT | JSOPTION_METHODJIT_ALWAYS;
+    uint32_t newopts =
+        JS_GetOptions(cx) | JSOPTION_METHODJIT | JSOPTION_METHODJIT_ALWAYS | JSOPTION_ALLOW_XML;
     uint32_t oldopts = JS_SetOptions(cx, newopts);
 
     CHECK(JS_SetThrowHook(rt, ThrowHook, NULL));
     EXEC("function foo() { throw 3 };\n"
          "for (var i = 0; i < 10; ++i) { \n"
          "  var x = <tag></tag>;\n"
          "  try {\n"
          "    foo(); \n"
--- a/js/src/jsapi-tests/testVersion.cpp
+++ b/js/src/jsapi-tests/testVersion.cpp
@@ -31,16 +31,17 @@ JSBool EvalScriptVersion16(JSContext *cx
 
 struct VersionFixture : public JSAPITest
 {
     JSVersion captured;
 
     virtual bool init() {
         if (!JSAPITest::init())
             return false;
+        JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_ALLOW_XML);
         callbackData = this;
         captured = JSVERSION_UNKNOWN;
         return JS_DefineFunction(cx, global, "checkVersionHasMoarXML", CheckVersionHasMoarXML, 0, 0) &&
                JS_DefineFunction(cx, global, "disableMoarXMLOption", DisableMoarXMLOption, 0, 0) &&
                JS_DefineFunction(cx, global, "callSetVersion17", CallSetVersion17, 0, 0) &&
                JS_DefineFunction(cx, global, "checkNewScriptNoXML", CheckNewScriptNoXML, 0, 0) &&
                JS_DefineFunction(cx, global, "overrideVersion15", OverrideVersion15, 0, 0) &&
                JS_DefineFunction(cx, global, "captureVersion", CaptureVersion, 0, 0) &&
@@ -182,17 +183,17 @@ CheckOverride(JSContext *cx, unsigned ar
     return shouldHaveOverride == cx->isVersionOverridden();
 }
 
 /*
  * See bug 611462. We are checking that the MOAR_XML option setting from a
  * JSAPI call is propagated to newly compiled scripts, instead of inheriting
  * the XML setting from a script on the stack.
  */
-BEGIN_FIXTURE_TEST(VersionFixture, testOptionsAreUsedForVersionFlags)
+BEGIN_FIXTURE_TEST(VersionFixture, testVersion_OptionsAreUsedForVersionFlags)
 {
     callbackData = this;
 
     /* Enable XML and compile a script to activate. */
     enableXML();
     static const char toActivateChars[] =
         "checkVersionHasMoarXML();"
         "disableMoarXMLOption();"
@@ -204,64 +205,82 @@ BEGIN_FIXTURE_TEST(VersionFixture, testO
 
     disableMoarXML();
 
     /* Activate the script. */
     jsval dummy;
     CHECK(JS_ExecuteScript(cx, global, toActivate, &dummy));
     return true;
 }
-END_FIXTURE_TEST(VersionFixture, testOptionsAreUsedForVersionFlags)
+END_FIXTURE_TEST(VersionFixture, testVersion_OptionsAreUsedForVersionFlags)
 
 /*
  * When re-entering the virtual machine through a *Version API the version
  * is no longer forced -- it continues with its natural push/pop oriented
  * version progression.  This is maintained by the |AutoVersionAPI| class in
  * jsapi.cpp.
  */
-BEGIN_FIXTURE_TEST(VersionFixture, testEntryLosesOverride)
+BEGIN_FIXTURE_TEST(VersionFixture, testVersion_EntryLosesOverride)
 {
     EXEC("overrideVersion15(); evalScriptVersion16('checkOverride(false); captureVersion()');");
     CHECK_EQUAL(captured, JSVERSION_1_6);
 
     /*
      * Override gets propagated to default version as non-override when you leave the VM's execute
      * call.
      */
     CHECK_EQUAL(JS_GetVersion(cx), JSVERSION_1_5);
     CHECK(!cx->isVersionOverridden());
     return true;
 }
-END_FIXTURE_TEST(VersionFixture, testEntryLosesOverride)
+END_FIXTURE_TEST(VersionFixture, testVersion_EntryLosesOverride)
 
 /*
  * EvalScriptVersion does not propagate overrides to its caller, it
  * restores things exactly as they were before the call. This is as opposed to
  * the normal (no Version suffix) API which propagates overrides
  * to the caller.
  */
-BEGIN_FIXTURE_TEST(VersionFixture, testReturnLosesOverride)
+BEGIN_FIXTURE_TEST(VersionFixture, testVersion_ReturnLosesOverride)
 {
     CHECK_EQUAL(JS_GetVersion(cx), JSVERSION_ECMA_5);
     EXEC(
         "checkOverride(false);"
         "evalScriptVersion16('overrideVersion15();');"
         "checkOverride(false);"
         "captureVersion();"
     );
     CHECK_EQUAL(captured, JSVERSION_ECMA_5);
     return true;
 }
-END_FIXTURE_TEST(VersionFixture, testReturnLosesOverride)
+END_FIXTURE_TEST(VersionFixture, testVersion_ReturnLosesOverride)
 
-BEGIN_FIXTURE_TEST(VersionFixture, testEvalPropagatesOverride)
+BEGIN_FIXTURE_TEST(VersionFixture, testVersion_EvalPropagatesOverride)
 {
     CHECK_EQUAL(JS_GetVersion(cx), JSVERSION_ECMA_5);
     EXEC(
         "checkOverride(false);"
         "eval('overrideVersion15();');"
         "checkOverride(true);"
         "captureVersion();"
     );
     CHECK_EQUAL(captured, JSVERSION_1_5);
     return true;
 }
-END_FIXTURE_TEST(VersionFixture, testEvalPropagatesOverride)
+END_FIXTURE_TEST(VersionFixture, testVersion_EvalPropagatesOverride)
+
+BEGIN_TEST(testVersion_AllowXML)
+{
+    JSBool ok;
+
+    static const char code[] = "var m = <x/>;";
+    ok = JS_EvaluateScriptForPrincipalsVersion(cx, global, NULL, code, strlen(code),
+                                               __FILE__, __LINE__, NULL, JSVERSION_ECMA_5);
+    CHECK_EQUAL(ok, JS_FALSE);
+    JS_ClearPendingException(cx);
+
+    JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_ALLOW_XML);
+    ok = JS_EvaluateScriptForPrincipalsVersion(cx, global, NULL, code, strlen(code),
+                                               __FILE__, __LINE__, NULL, JSVERSION_ECMA_5);
+    CHECK_EQUAL(ok, JS_TRUE);
+    return true;
+}
+END_TEST(testVersion_AllowXML)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -83,17 +83,17 @@
 #include "methodjit/Logging.h"
 #endif
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 /*
- * This class is a version-establising barrier at the head of a VM entry or
+ * This class is a version-establishing barrier at the head of a VM entry or
  * re-entry. It ensures that:
  *
  * - |newVersion| is the starting (default) version used for the context.
  * - The starting version state is not an override.
  * - Overrides in the VM session are not propagated to the caller.
  */
 class AutoVersionAPI
 {
@@ -102,25 +102,30 @@ class AutoVersionAPI
     bool        oldHasVersionOverride;
     JSVersion   oldVersionOverride;
 #ifdef DEBUG
     unsigned       oldCompileOptions;
 #endif
     JSVersion   newVersion;
 
   public:
-    explicit AutoVersionAPI(JSContext *cx, JSVersion newVersion)
+    AutoVersionAPI(JSContext *cx, JSVersion newVersion)
       : cx(cx),
         oldDefaultVersion(cx->getDefaultVersion()),
         oldHasVersionOverride(cx->isVersionOverridden()),
         oldVersionOverride(oldHasVersionOverride ? cx->findVersion() : JSVERSION_UNKNOWN)
 #ifdef DEBUG
         , oldCompileOptions(cx->getCompileOptions())
 #endif
     {
+#if JS_HAS_XML_SUPPORT
+        // For backward compatibility, AutoVersionAPI clobbers the
+        // JSOPTION_MOAR_XML bit in cx, but not the JSOPTION_ALLOW_XML bit.
+        newVersion = JSVersion(newVersion | (oldDefaultVersion & VersionFlags::ALLOW_XML));
+#endif
         this->newVersion = newVersion;
         cx->clearVersionOverride();
         cx->setDefaultVersion(newVersion);
     }
 
     ~AutoVersionAPI() {
         cx->setDefaultVersion(oldDefaultVersion);
         if (oldHasVersionOverride)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2725,22 +2725,24 @@ JS_StringToVersion(const char *string);
                                                    promises to execute compiled
                                                    script once only; enables
                                                    compile-time scope chain
                                                    resolution of consts. */
 #define JSOPTION_ATLINE         JS_BIT(5)       /* //@line number ["filename"]
                                                    option supported for the
                                                    XUL preprocessor and kindred
                                                    beasts. */
-#define JSOPTION_MOAR_XML       JS_BIT(6)       /* EMCAScript for XML support:
+#define JSOPTION_ALLOW_XML      JS_BIT(6)       /* enable E4X syntax (deprecated) */
+#define JSOPTION_MOAR_XML       JS_BIT(7)       /* enable E4X even in versions
+                                                   that don't normally get it;
                                                    parse <!-- --> as a token,
                                                    not backward compatible with
                                                    the comment-hiding hack used
                                                    in HTML script tags. */
-#define JSOPTION_DONT_REPORT_UNCAUGHT \
+#define JSOPTION_DONT_REPORT_UNCAUGHT                                   \
                                 JS_BIT(8)       /* When returning from the
                                                    outermost API call, prevent
                                                    uncaught exceptions from
                                                    being converted to error
                                                    reports */
 
 #define JSOPTION_RELIMIT        JS_BIT(9)       /* Throw exception on any
                                                    regular expression which
@@ -2772,17 +2774,17 @@ JS_StringToVersion(const char *string);
 
 #define JSOPTION_TYPE_INFERENCE JS_BIT(18)      /* Perform type inference. */
 #define JSOPTION_STRICT_MODE    JS_BIT(19)      /* Provides a way to force
                                                    strict mode for all code
                                                    without requiring
                                                    "use strict" annotations. */
 
 /* Options which reflect compile-time properties of scripts. */
-#define JSCOMPILEOPTION_MASK    (JSOPTION_MOAR_XML)
+#define JSCOMPILEOPTION_MASK    (JSOPTION_ALLOW_XML | JSOPTION_MOAR_XML)
 
 #define JSRUNOPTION_MASK        (JS_BITMASK(20) & ~JSCOMPILEOPTION_MASK)
 #define JSALLOPTION_MASK        (JSCOMPILEOPTION_MASK | JSRUNOPTION_MASK)
 
 extern JS_PUBLIC_API(uint32_t)
 JS_GetOptions(JSContext *cx);
 
 extern JS_PUBLIC_API(uint32_t)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -901,16 +901,22 @@ struct JSArgumentFormatMap {
 };
 #endif
 
 namespace js {
 
 struct AutoResolving;
 
 static inline bool
+OptionsHasAllowXML(uint32_t options)
+{
+    return !!(options & JSOPTION_ALLOW_XML);
+}
+
+static inline bool
 OptionsHasMoarXML(uint32_t options)
 {
     return !!(options & JSOPTION_MOAR_XML);
 }
 
 static inline bool
 OptionsSameVersionFlags(uint32_t self, uint32_t other)
 {
@@ -923,27 +929,34 @@ OptionsSameVersionFlags(uint32_t self, u
  * can inherit their caller's compile-time properties and b) scripts can be
  * appropriately compared in the eval cache across global option changes. An
  * example of the latter is enabling the top-level-anonymous-function-is-error
  * option: subsequent evals of the same, previously-valid script text may have
  * become invalid.
  */
 namespace VersionFlags {
 static const unsigned MASK      = 0x0FFF; /* see JSVersion in jspubtd.h */
-static const unsigned MOAR_XML  = 0x1000; /* flag induced by JSOPTION_MOAR_XML */
+static const unsigned ALLOW_XML = 0x1000; /* flag induced by JSOPTION_ALLOW_XML */
+static const unsigned MOAR_XML  = 0x2000; /* flag induced by JSOPTION_MOAR_XML */
 static const unsigned FULL_MASK = 0x3FFF;
 } /* namespace VersionFlags */
 
 static inline JSVersion
 VersionNumber(JSVersion version)
 {
     return JSVersion(uint32_t(version) & VersionFlags::MASK);
 }
 
 static inline bool
+VersionHasAllowXML(JSVersion version)
+{
+    return !!(version & VersionFlags::ALLOW_XML);
+}
+
+static inline bool
 VersionHasMoarXML(JSVersion version)
 {
     return !!(version & VersionFlags::MOAR_XML);
 }
 
 /* @warning This is a distinct condition from having the XML flag set. */
 static inline bool
 VersionShouldParseXML(JSVersion version)
@@ -967,25 +980,32 @@ static inline bool
 VersionHasFlags(JSVersion version)
 {
     return !!VersionExtractFlags(version);
 }
 
 static inline unsigned
 VersionFlagsToOptions(JSVersion version)
 {
-    unsigned copts = VersionHasMoarXML(version) ? JSOPTION_MOAR_XML : 0;
+    unsigned copts = (VersionHasAllowXML(version) ? JSOPTION_ALLOW_XML : 0) |
+                     (VersionHasMoarXML(version) ? JSOPTION_MOAR_XML : 0);
     JS_ASSERT((copts & JSCOMPILEOPTION_MASK) == copts);
     return copts;
 }
 
 static inline JSVersion
 OptionFlagsToVersion(unsigned options, JSVersion version)
 {
-    return VersionSetMoarXML(version, OptionsHasMoarXML(options));
+    uint32_t v = version;
+    v &= ~(VersionFlags::ALLOW_XML | VersionFlags::MOAR_XML);
+    if (OptionsHasAllowXML(options))
+        v |= VersionFlags::ALLOW_XML;
+    if (OptionsHasMoarXML(options))
+        v |= VersionFlags::MOAR_XML;
+    return JSVersion(v);
 }
 
 static inline bool
 VersionIsKnown(JSVersion version)
 {
     return VersionNumber(version) != JSVERSION_UNKNOWN;
 }
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -579,16 +579,17 @@ static const struct JSOption {
 } js_options[] = {
     {"atline",          JSOPTION_ATLINE},
     {"methodjit",       JSOPTION_METHODJIT},
     {"methodjit_always",JSOPTION_METHODJIT_ALWAYS},
     {"relimit",         JSOPTION_RELIMIT},
     {"strict",          JSOPTION_STRICT},
     {"typeinfer",       JSOPTION_TYPE_INFERENCE},
     {"werror",          JSOPTION_WERROR},
+    {"allow_xml",       JSOPTION_ALLOW_XML},
     {"moar_xml",        JSOPTION_MOAR_XML},
     {"strict_mode",     JSOPTION_STRICT_MODE},
 };
 
 static uint32_t
 MapContextOptionNameToFlag(JSContext* cx, const char* name)
 {
     for (size_t i = 0; i < ArrayLength(js_options); ++i) {
--- a/js/src/tests/shell.js
+++ b/js/src/tests/shell.js
@@ -674,16 +674,17 @@ function optionsReset() {
   }
 
 }
 
 if (typeof options == 'function')
 {
   optionsInit();
   optionsClear();
+  options("allow_xml");
 }
 
 function getTestCaseResult(expected, actual)
 {
   if (typeof expected != typeof actual)
     return false;
   if (typeof expected != 'number')
     // Note that many tests depend on the use of '==' here, not '==='.
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -20,17 +20,17 @@ namespace js {
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number is XDR'd near the front of xdr bytecode and
  * aborts deserialization if there is a mismatch between the current
  * and saved versions. If deserialization fails, the data should be
  * invalidated if possible.
  */
-static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 116);
+static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 117);
 
 class XDRBuffer {
   public:
     XDRBuffer(JSContext *cx)
       : context(cx), base(NULL), cursor(NULL), limit(NULL) { }
 
     JSContext *cx() const {
         return context;
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -398,17 +398,17 @@ mozJSComponentLoader::ReallyInit()
         return rv;
 
     // Create our compilation context.
     mContext = JS_NewContext(mRuntime, 256);
     if (!mContext)
         return NS_ERROR_OUT_OF_MEMORY;
 
     uint32_t options = JS_GetOptions(mContext);
-    JS_SetOptions(mContext, options | JSOPTION_MOAR_XML);
+    JS_SetOptions(mContext, options | JSOPTION_ALLOW_XML | JSOPTION_MOAR_XML);
 
     // Always use the latest js version
     JS_SetVersion(mContext, JSVERSION_LATEST);
 
     nsCOMPtr<nsIScriptSecurityManager> secman =
         do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
     if (!secman)
         return NS_ERROR_FAILURE;
--- a/js/xpconnect/shell/xpcshell.cpp
+++ b/js/xpconnect/shell/xpcshell.cpp
@@ -678,16 +678,17 @@ GetChildGlobalObject(JSContext* cx,
 static const struct JSOption {
     const char  *name;
     uint32_t    flag;
 } js_options[] = {
     {"atline",          JSOPTION_ATLINE},
     {"relimit",         JSOPTION_RELIMIT},
     {"strict",          JSOPTION_STRICT},
     {"werror",          JSOPTION_WERROR},
+    {"allow_xml",       JSOPTION_ALLOW_XML},
     {"moar_xml",        JSOPTION_MOAR_XML},
     {"strict_mode",     JSOPTION_STRICT_MODE},
 };
 
 static uint32_t
 MapContextOptionNameToFlag(JSContext* cx, const char* name)
 {
     for (size_t i = 0; i < ArrayLength(js_options); ++i) {
@@ -1806,16 +1807,17 @@ main(int argc, char **argv, char **envp)
         gOldJSContextCallback = JS_SetContextCallback(rt, ContextCallback);
 
         cx = JS_NewContext(rt, 8192);
         if (!cx) {
             printf("JS_NewContext failed!\n");
             return 1;
         }
 
+        JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_ALLOW_XML);
         xpc_LocalizeContext(cx);
 
         nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
         if (!xpc) {
             printf("failed to get nsXPConnect service!\n");
             return 1;
         }
 
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -3491,17 +3491,18 @@ ContextHolder::ContextHolder(JSContext *
     : mJSContext(JS_NewContext(JS_GetRuntime(aOuterCx), 1024)),
       mOrigCx(aOuterCx)
 {
     if (mJSContext) {
         JSAutoRequest ar(mJSContext);
         JS_SetOptions(mJSContext,
                       JS_GetOptions(mJSContext) |
                       JSOPTION_DONT_REPORT_UNCAUGHT |
-                      JSOPTION_PRIVATE_IS_NSISUPPORTS);
+                      JSOPTION_PRIVATE_IS_NSISUPPORTS |
+                      JSOPTION_ALLOW_XML);
         JS_SetGlobalObject(mJSContext, aSandbox);
         JS_SetContextPrivate(mJSContext, this);
         JS_SetOperationCallback(mJSContext, ContextHolderOperationCallback);
     }
 }
 
 ContextHolder::~ContextHolder()
 {