Bug 840488 - New domain policy API. r=bz
authorBobby Holley <bobbyholley@gmail.com>
Tue, 12 Nov 2013 16:43:35 -0800
changeset 154813 9f9022aabfe9114c68879fd51660b2899ddf3c7c
parent 154812 4beeac0fbc68a4347687d3069e4d5d2615344e0a
child 154814 cd0e2e0ef136b6bcfefaa695bd7909eb2504f9d3
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersbz
bugs840488
milestone28.0a1
Bug 840488 - New domain policy API. r=bz Note that this patch changes the semantics of javascript.enabled so that changes to the pref do not apply to compartments that have already been created. This is a significant change, but is necessary to support the new domain policy API. After one cycle or so, we'll rip out the old API.
caps/idl/moz.build
caps/idl/nsIDomainPolicy.idl
caps/idl/nsIScriptSecurityManager.idl
caps/include/DomainPolicy.h
caps/include/nsScriptSecurityManager.h
caps/src/DomainPolicy.cpp
caps/src/moz.build
caps/src/nsScriptSecurityManager.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/xpcpublic.h
--- a/caps/idl/moz.build
+++ b/caps/idl/moz.build
@@ -1,14 +1,15 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPIDL_SOURCES += [
+    'nsIDomainPolicy.idl',
     'nsIPrincipal.idl',
     'nsIScriptSecurityManager.idl',
     'nsISecurityCheckedComponent.idl',
 ]
 
 MODULE = 'caps'
 
new file mode 100644
--- /dev/null
+++ b/caps/idl/nsIDomainPolicy.idl
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIURI;
+interface nsIDomainSet;
+
+/*
+ * When a domain policy is instantiated by invoking activateDomainPolicy() on
+ * nsIScriptSecurityManager, these domain sets are consulted when each new
+ * global is created (they have no effect on already-created globals).
+ * If javascript is globally enabled with |javascript.enabled|, the blacklists
+ * are consulted. If globally disabled, the whitelists are consulted. Lookups
+ * on blacklist and whitelist happen with contains(), and lookups on
+ * superBlacklist and superWhitelist happen with containsSuperDomain().
+ *
+ * When deactivate() is invoked, the domain sets are emptied, and the
+ * nsIDomainPolicy ceases to have any effect on the system.
+ */
+[scriptable, builtinclass, uuid(27b10f54-f34b-42b7-8594-4348d3ad7953)]
+interface nsIDomainPolicy : nsISupports
+{
+    readonly attribute nsIDomainSet blacklist;
+    readonly attribute nsIDomainSet superBlacklist;
+    readonly attribute nsIDomainSet whitelist;
+    readonly attribute nsIDomainSet superWhitelist;
+
+    void deactivate();
+};
+
+[scriptable, builtinclass, uuid(946a01ff-6525-4007-a2c2-447ebe1875d3)]
+interface nsIDomainSet : nsISupports
+{
+    /*
+     * Add a domain to the set. No-op if it already exists.
+     */
+    void add(in nsIURI aDomain);
+
+    /*
+     * Remove a domain from the set. No-op if it doesn't exist.
+     */
+    void remove(in nsIURI aDomain);
+
+    /*
+     * Remove all entries from the set.
+     */
+    void clear();
+
+    /*
+     * Returns true if a given domain is in the set.
+     */
+    bool contains(in nsIURI aDomain);
+
+    /*
+     * Returns true if a given domain is a subdomain of one of the entries in
+     * the set.
+     */
+    bool containsSuperDomain(in nsIURI aDomain);
+};
--- a/caps/idl/nsIScriptSecurityManager.idl
+++ b/caps/idl/nsIScriptSecurityManager.idl
@@ -4,18 +4,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsIPrincipal.idl"
 #include "nsIXPCSecurityManager.idl"
 interface nsIURI;
 interface nsIChannel;
 interface nsIDocShell;
+interface nsIDomainPolicy;
 
-[scriptable, uuid(36619e6e-6ed3-4fd5-9a16-763f3614e5ed)]
+[scriptable, uuid(2911ae60-1b5f-47e6-941e-1bb7b53a167d)]
 interface nsIScriptSecurityManager : nsIXPCSecurityManager
 {
     ///////////////// Security Checks //////////////////
     /**
      * Checks whether the running script is allowed to access aProperty.
      */
     [noscript] void checkPropertyAccess(in JSContextPtr aJSContext,
                                         in JSObjectPtr aJSObject,
@@ -223,13 +224,36 @@ interface nsIScriptSecurityManager : nsI
 
     /**
      * Returns the jar prefix for the app.
      * appId can be NO_APP_ID or a valid app id. appId should not be
      * UNKNOWN_APP_ID.
      * inMozBrowser has to be true if the app is inside a mozbrowser iframe.
      */
     AUTF8String getJarPrefix(in unsigned long appId, in boolean inMozBrowser);
+
+    /**
+     * Per-domain controls to enable and disable script. This system is designed
+     * to be used by at most one consumer, and enforces this with its semantics.
+     *
+     * Initially, domainPolicyActive is false. When activateDomainPolicy() is
+     * invoked, domainPolicyActive becomes true, and subsequent calls to
+     * activateDomainPolicy() will fail until deactivate() is invoked on the
+     * nsIDomainPolicy returned from activateDomainPolicy(). At this point,
+     * domainPolicyActive becomes false again, and a new consumer may acquire
+     * control of the system by invoking activateDomainPolicy().
+     */
+    nsIDomainPolicy activateDomainPolicy();
+    readonly attribute boolean domainPolicyActive;
+
+    /**
+     * Query mechanism for the above policy.
+     *
+     * If domainPolicyEnabled is false, this simply returns the current value
+     * of javascript.enabled. Otherwise, it returns the same value, but taking
+     * the various blacklist/whitelist exceptions into account.
+     */
+    bool policyAllowsScript(in nsIURI aDomain);
 };
 
 %{C++
 #define NS_SCRIPTSECURITYMANAGER_CONTRACTID "@mozilla.org/scriptsecuritymanager;1"
 %}
new file mode 100644
--- /dev/null
+++ b/caps/include/DomainPolicy.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DomainPolicy_h__
+#define DomainPolicy_h__
+
+#include "nsIDomainPolicy.h"
+#include "nsTHashtable.h"
+#include "nsURIHashKey.h"
+
+namespace mozilla {
+
+// The name "DomainPolicy" conflicts with some of the old-style policy machinery
+// in nsScriptSecurityManager.cpp, which needs to #include this file. So we
+// temporarily use a sub-namespace until that machinery goes away in bug 913734.
+namespace hotness {
+
+class DomainPolicy : public nsIDomainPolicy
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIDOMAINPOLICY
+    DomainPolicy();
+    virtual ~DomainPolicy();
+
+private:
+    nsCOMPtr<nsIDomainSet> mBlacklist;
+    nsCOMPtr<nsIDomainSet> mSuperBlacklist;
+    nsCOMPtr<nsIDomainSet> mWhitelist;
+    nsCOMPtr<nsIDomainSet> mSuperWhitelist;
+};
+
+class DomainSet : public nsIDomainSet
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIDOMAINSET
+
+    DomainSet() {}
+    virtual ~DomainSet() {}
+
+protected:
+    nsTHashtable<nsURIHashKey> mHashTable;
+};
+
+} /* namespace hotness */
+} /* namespace mozilla */
+
+#endif /* DomainPolicy_h__ */
--- a/caps/include/nsScriptSecurityManager.h
+++ b/caps/include/nsScriptSecurityManager.h
@@ -357,16 +357,18 @@ public:
      * parent are isolated from each other. All those entities do not share the
      * same data (cookies, IndexedDB, localStorage, etc.) so we shouldn't allow
      * violating that principle.
      */
     static bool
     AppAttributesEqual(nsIPrincipal* aFirst,
                        nsIPrincipal* aSecond);
 
+    void DeactivateDomainPolicy();
+
 private:
 
     // GetScriptSecurityManager is the only call that can make one
     nsScriptSecurityManager();
     virtual ~nsScriptSecurityManager();
 
     bool SubjectIsPrivileged();
 
@@ -482,16 +484,20 @@ private:
     DomainPolicy* mDefaultPolicy;
     nsObjectHashtable* mCapabilities;
 
     nsCOMPtr<nsIPrincipal> mSystemPrincipal;
     bool mPrefInitialized;
     bool mIsJavaScriptEnabled;
     bool mPolicyPrefsChanged;
 
+    // This machinery controls new-style domain policies. The old-style
+    // policy machinery will be removed soon.
+    nsCOMPtr<nsIDomainPolicy> mDomainPolicy;
+
     static bool sStrictFileOriginPolicy;
 
     static nsIIOService    *sIOService;
     static nsIStringBundle *sStrBundle;
     static JSRuntime       *sRuntime;
 };
 
 #define NS_SECURITYNAMESET_CID \
new file mode 100644
--- /dev/null
+++ b/caps/src/DomainPolicy.cpp
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=4 et sw=4 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DomainPolicy.h"
+#include "nsScriptSecurityManager.h"
+
+namespace mozilla {
+namespace hotness {
+
+NS_IMPL_ISUPPORTS1(DomainPolicy, nsIDomainPolicy)
+
+DomainPolicy::DomainPolicy() : mBlacklist(new DomainSet())
+                             , mSuperBlacklist(new DomainSet())
+                             , mWhitelist(new DomainSet())
+                             , mSuperWhitelist(new DomainSet())
+{}
+
+DomainPolicy::~DomainPolicy()
+{
+    // The SSM holds a strong ref to the DomainPolicy until Deactivate() is
+    // invoked, so we should never hit the destructor until that happens.
+    MOZ_ASSERT(!mBlacklist && !mSuperBlacklist &&
+               !mWhitelist && !mSuperWhitelist);
+}
+
+
+NS_IMETHODIMP
+DomainPolicy::GetBlacklist(nsIDomainSet** aSet)
+{
+    nsCOMPtr<nsIDomainSet> set = mBlacklist;
+    set.forget(aSet);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+DomainPolicy::GetSuperBlacklist(nsIDomainSet** aSet)
+{
+    nsCOMPtr<nsIDomainSet> set = mSuperBlacklist;
+    set.forget(aSet);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+DomainPolicy::GetWhitelist(nsIDomainSet** aSet)
+{
+    nsCOMPtr<nsIDomainSet> set = mWhitelist;
+    set.forget(aSet);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+DomainPolicy::GetSuperWhitelist(nsIDomainSet** aSet)
+{
+    nsCOMPtr<nsIDomainSet> set = mSuperWhitelist;
+    set.forget(aSet);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+DomainPolicy::Deactivate()
+{
+    // Clear the hashtables first to free up memory, since script might
+    // hold the doomed sets alive indefinitely.
+    mBlacklist->Clear();
+    mSuperBlacklist->Clear();
+    mWhitelist->Clear();
+    mSuperWhitelist->Clear();
+
+    // Null them out.
+    mBlacklist = nullptr;
+    mSuperBlacklist = nullptr;
+    mWhitelist = nullptr;
+    mSuperWhitelist = nullptr;
+
+    // Inform the SSM.
+    nsScriptSecurityManager::GetScriptSecurityManager()->DeactivateDomainPolicy();
+    return NS_OK;
+}
+
+static already_AddRefed<nsIURI>
+GetCanonicalClone(nsIURI* aURI)
+{
+    nsCOMPtr<nsIURI> clone;
+    nsresult rv = aURI->Clone(getter_AddRefs(clone));
+    NS_ENSURE_SUCCESS(rv, nullptr);
+    rv = clone->SetUserPass(EmptyCString());
+    NS_ENSURE_SUCCESS(rv, nullptr);
+    rv = clone->SetPath(EmptyCString());
+    NS_ENSURE_SUCCESS(rv, nullptr);
+    return clone.forget();
+}
+
+NS_IMPL_ISUPPORTS1(DomainSet, nsIDomainSet)
+
+NS_IMETHODIMP
+DomainSet::Add(nsIURI* aDomain)
+{
+    nsCOMPtr<nsIURI> clone = GetCanonicalClone(aDomain);
+    NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE);
+    mHashTable.PutEntry(clone);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+DomainSet::Remove(nsIURI* aDomain)
+{
+    nsCOMPtr<nsIURI> clone = GetCanonicalClone(aDomain);
+    NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE);
+    mHashTable.RemoveEntry(clone);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+DomainSet::Clear()
+{
+    mHashTable.Clear();
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+DomainSet::Contains(nsIURI* aDomain, bool* aContains)
+{
+    *aContains = false;
+    nsCOMPtr<nsIURI> clone = GetCanonicalClone(aDomain);
+    NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE);
+    *aContains = mHashTable.Contains(clone);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+DomainSet::ContainsSuperDomain(nsIURI* aDomain, bool* aContains)
+{
+    *aContains = false;
+    nsCOMPtr<nsIURI> clone = GetCanonicalClone(aDomain);
+    NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE);
+    nsAutoCString domain;
+    nsresult rv = clone->GetHost(domain);
+    NS_ENSURE_SUCCESS(rv, rv);
+    while (true) {
+        // Check the current domain.
+        if (mHashTable.Contains(clone)) {
+            *aContains = true;
+            return NS_OK;
+        }
+
+        // Chop off everything before the first dot, or break if there are no
+        // dots left.
+        int32_t index = domain.Find(".");
+        if (index == kNotFound)
+            break;
+        domain.Assign(Substring(domain, index + 1));
+        rv = clone->SetHost(domain);
+        NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    // No match.
+    return NS_OK;
+
+}
+
+} /* namespace hotness */
+} /* namespace mozilla */
--- a/caps/src/moz.build
+++ b/caps/src/moz.build
@@ -2,16 +2,17 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'caps'
 
 SOURCES += [
+    'DomainPolicy.cpp',
     'nsJSPrincipals.cpp',
     'nsNullPrincipal.cpp',
     'nsNullPrincipalURI.cpp',
     'nsPrincipal.cpp',
     'nsScriptSecurityManager.cpp',
     'nsSecurityManagerFactory.cpp',
     'nsSystemPrincipal.cpp',
 ]
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -16,16 +16,17 @@
 #include "nsIScriptContext.h"
 #include "nsIURL.h"
 #include "nsINestedURI.h"
 #include "nspr.h"
 #include "nsJSPrincipals.h"
 #include "nsSystemPrincipal.h"
 #include "nsPrincipal.h"
 #include "nsNullPrincipal.h"
+#include "DomainPolicy.h"
 #include "nsXPIDLString.h"
 #include "nsCRT.h"
 #include "nsCRTGlue.h"
 #include "nsError.h"
 #include "nsDOMCID.h"
 #include "nsIXPConnect.h"
 #include "nsIXPCSecurityManager.h"
 #include "nsTextFormatter.h"
@@ -1610,21 +1611,16 @@ nsScriptSecurityManager::ScriptAllowed(J
         return false;
     }
 
     // If the compartment is immune to script policy, we're done.
     if (scriptability.IsImmuneToScriptPolicy()) {
         return true;
     }
 
-    // If JS is disabled system-wide, we disallow.
-    if (!mIsJavaScriptEnabled) {
-        return false;
-    }
-
     // Check for a per-site policy.
     static const char jsPrefGroupName[] = "javascript";
     ClassInfoData nameData(nullptr, jsPrefGroupName);
     SecurityLevel secLevel;
     nsresult rv = LookupPolicy(doGetObjectPrincipal(global), nameData,
                                EnabledID(),
                                nsIXPCSecurityManager::ACCESS_GET_PROPERTY,
                                nullptr, &secLevel);
@@ -2151,16 +2147,19 @@ static StaticRefPtr<nsScriptSecurityMana
 
 nsScriptSecurityManager::~nsScriptSecurityManager(void)
 {
     Preferences::RemoveObservers(this, kObservedPrefs);
     delete mOriginToPolicyMap;
     if(mDefaultPolicy)
         mDefaultPolicy->Drop();
     delete mCapabilities;
+    if (mDomainPolicy)
+        mDomainPolicy->Deactivate();
+    MOZ_ASSERT(!mDomainPolicy);
 }
 
 void
 nsScriptSecurityManager::Shutdown()
 {
     if (sRuntime) {
         JS_SetSecurityCallbacks(sRuntime, nullptr);
         JS_SetTrustedPrincipals(sRuntime, nullptr);
@@ -2576,8 +2575,79 @@ nsScriptSecurityManager::GetJarPrefix(ui
                                       bool aInMozBrowser,
                                       nsACString& aJarPrefix)
 {
   MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
 
   mozilla::GetJarPrefix(aAppId, aInMozBrowser, aJarPrefix);
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsScriptSecurityManager::GetDomainPolicyActive(bool *aRv)
+{
+    *aRv = !!mDomainPolicy;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::ActivateDomainPolicy(nsIDomainPolicy** aRv)
+{
+    // We only allow one domain policy at a time. The holder of the previous
+    // policy must explicitly deactivate it first.
+    if (mDomainPolicy) {
+        return NS_ERROR_SERVICE_NOT_AVAILABLE;
+    }
+
+    mDomainPolicy = new mozilla::hotness::DomainPolicy();
+    nsCOMPtr<nsIDomainPolicy> ptr = mDomainPolicy;
+    ptr.forget(aRv);
+    return NS_OK;
+}
+
+// Intentionally non-scriptable. Script must have a reference to the
+// nsIDomainPolicy to deactivate it.
+void
+nsScriptSecurityManager::DeactivateDomainPolicy()
+{
+    mDomainPolicy = nullptr;
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::PolicyAllowsScript(nsIURI* aURI, bool *aRv)
+{
+    nsresult rv;
+
+    // Compute our rule. If we don't have any domain policy set up that might
+    // provide exceptions to this rule, we're done.
+    *aRv = mIsJavaScriptEnabled;
+    if (!mDomainPolicy) {
+        return NS_OK;
+    }
+
+    // We have a domain policy. Grab the appropriate set of exceptions to the
+    // rule (either the blacklist or the whitelist, depending on whether script
+    // is enabled or disabled by default).
+    nsCOMPtr<nsIDomainSet> exceptions;
+    nsCOMPtr<nsIDomainSet> superExceptions;
+    if (*aRv) {
+        mDomainPolicy->GetBlacklist(getter_AddRefs(exceptions));
+        mDomainPolicy->GetSuperBlacklist(getter_AddRefs(superExceptions));
+    } else {
+        mDomainPolicy->GetWhitelist(getter_AddRefs(exceptions));
+        mDomainPolicy->GetSuperWhitelist(getter_AddRefs(superExceptions));
+    }
+
+    bool contains;
+    rv = exceptions->Contains(aURI, &contains);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (contains) {
+        *aRv = !*aRv;
+        return NS_OK;
+    }
+    rv = superExceptions->ContainsSuperDomain(aURI, &contains);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (contains) {
+        *aRv = !*aRv;
+    }
+
+    return NS_OK;
+}
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Per JSRuntime object */
 
 #include "mozilla/MemoryReporting.h"
 
 #include "xpcprivate.h"
 #include "xpcpublic.h"
+#include "XPCWrapper.h"
 #include "XPCJSMemoryReporter.h"
 #include "WrapperFactory.h"
 #include "dom_quickstubs.h"
 
 #include "nsIMemoryReporter.h"
 #include "nsIObserverService.h"
 #include "nsIDebug2.h"
 #include "amIAddonManager.h"
@@ -443,25 +444,44 @@ PrincipalImmuneToScriptPolicy(nsIPrincip
         }
     }
 
     return false;
 }
 
 Scriptability::Scriptability(JSCompartment *c) : mScriptBlocks(0)
                                                , mDocShellAllowsScript(true)
+                                               , mScriptBlockedByPolicy(false)
 {
     nsIPrincipal *prin = nsJSPrincipals::get(JS_GetCompartmentPrincipals(c));
     mImmuneToScriptPolicy = PrincipalImmuneToScriptPolicy(prin);
+
+    // If we're not immune, we should have a real principal with a codebase URI.
+    // Check the URI against the new-style domain policy.
+    if (!mImmuneToScriptPolicy) {
+        nsIScriptSecurityManager* ssm = XPCWrapper::GetSecurityManager();
+        nsCOMPtr<nsIURI> codebase;
+        nsresult rv = prin->GetURI(getter_AddRefs(codebase));
+        bool policyAllows;
+        if (NS_SUCCEEDED(rv) && codebase &&
+            NS_SUCCEEDED(ssm->PolicyAllowsScript(codebase, &policyAllows)))
+        {
+            mScriptBlockedByPolicy = !policyAllows;
+        } else {
+            // Something went wrong - be safe and block script.
+            mScriptBlockedByPolicy = true;
+        }
+    }
 }
 
 bool
 Scriptability::Allowed()
 {
-    return mDocShellAllowsScript && mScriptBlocks == 0;
+    return mDocShellAllowsScript && !mScriptBlockedByPolicy &&
+           mScriptBlocks == 0;
 }
 
 bool
 Scriptability::IsImmuneToScriptPolicy()
 {
     return mImmuneToScriptPolicy;
 }
 
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -56,16 +56,20 @@ private:
 
     // Whether the docshell allows javascript in this scope. If this scope
     // doesn't have a docshell, this value is always true.
     bool mDocShellAllowsScript;
 
     // Whether this scope is immune to user-defined or addon-defined script
     // policy.
     bool mImmuneToScriptPolicy;
+
+    // Whether the new-style domain policy when this compartment was created
+    // forbids script execution.
+    bool mScriptBlockedByPolicy;
 };
 
 JSObject *
 TransplantObject(JSContext *cx, JS::HandleObject origobj, JS::HandleObject target);
 
 // Return a raw XBL scope object corresponding to contentScope, which must
 // be an object whose global is a DOM window.
 //