summaryrefslogtreecommitdiffstats
path: root/caps
diff options
context:
space:
mode:
Diffstat (limited to 'caps')
-rw-r--r--caps/BasePrincipal.cpp777
-rw-r--r--caps/BasePrincipal.h342
-rw-r--r--caps/DomainPolicy.cpp260
-rw-r--r--caps/DomainPolicy.h72
-rw-r--r--caps/moz.build63
-rw-r--r--caps/nsIAddonPolicyService.idl78
-rw-r--r--caps/nsIDomainPolicy.idl80
-rw-r--r--caps/nsIPrincipal.idl367
-rw-r--r--caps/nsIScriptSecurityManager.idl305
-rw-r--r--caps/nsJSPrincipals.cpp299
-rw-r--r--caps/nsJSPrincipals.h66
-rw-r--r--caps/nsNullPrincipal.cpp193
-rw-r--r--caps/nsNullPrincipal.h78
-rw-r--r--caps/nsNullPrincipalURI.cpp362
-rw-r--r--caps/nsNullPrincipalURI.h59
-rw-r--r--caps/nsPrincipal.cpp854
-rw-r--r--caps/nsPrincipal.h110
-rw-r--r--caps/nsScriptSecurityManager.cpp1759
-rw-r--r--caps/nsScriptSecurityManager.h162
-rw-r--r--caps/nsSystemPrincipal.cpp141
-rw-r--r--caps/nsSystemPrincipal.h60
-rw-r--r--caps/tests/gtest/TestOriginAttributes.cpp38
-rw-r--r--caps/tests/gtest/moz.build13
-rw-r--r--caps/tests/mochitest/browser.ini1
-rw-r--r--caps/tests/mochitest/browser_checkloaduri.js292
-rw-r--r--caps/tests/mochitest/chrome.ini9
-rw-r--r--caps/tests/mochitest/file_data.txt1
-rw-r--r--caps/tests/mochitest/file_disableScript.html11
-rw-r--r--caps/tests/mochitest/mochitest.ini13
-rw-r--r--caps/tests/mochitest/resource_test_file.html2
-rw-r--r--caps/tests/mochitest/test_addonMayLoad.html97
-rw-r--r--caps/tests/mochitest/test_app_principal_equality.html88
-rw-r--r--caps/tests/mochitest/test_bug246699.html65
-rw-r--r--caps/tests/mochitest/test_bug292789.html105
-rw-r--r--caps/tests/mochitest/test_bug423375.html44
-rw-r--r--caps/tests/mochitest/test_bug470804.html41
-rw-r--r--caps/tests/mochitest/test_bug995943.xul115
-rw-r--r--caps/tests/mochitest/test_disableScript.xul339
-rw-r--r--caps/tests/mochitest/test_disallowInheritPrincipal.html60
-rw-r--r--caps/tests/mochitest/test_extensionURL.html166
-rw-r--r--caps/tests/unit/test_origin.js307
-rw-r--r--caps/tests/unit/xpcshell.ini5
42 files changed, 8299 insertions, 0 deletions
diff --git a/caps/BasePrincipal.cpp b/caps/BasePrincipal.cpp
new file mode 100644
index 000000000..b768d5c1f
--- /dev/null
+++ b/caps/BasePrincipal.cpp
@@ -0,0 +1,777 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et 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 "mozilla/BasePrincipal.h"
+
+#include "nsDocShell.h"
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+#include "nsIAddonPolicyService.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsIEffectiveTLDService.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+
+#include "nsPrincipal.h"
+#include "nsNetUtil.h"
+#include "nsIURIWithPrincipal.h"
+#include "nsNullPrincipal.h"
+#include "nsScriptSecurityManager.h"
+#include "nsServiceManagerUtils.h"
+
+#include "mozilla/dom/ChromeUtils.h"
+#include "mozilla/dom/CSPDictionariesBinding.h"
+#include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "mozilla/dom/URLSearchParams.h"
+
+namespace mozilla {
+
+using dom::URLParams;
+
+void
+PrincipalOriginAttributes::InheritFromDocShellToDoc(const DocShellOriginAttributes& aAttrs,
+ const nsIURI* aURI)
+{
+ mAppId = aAttrs.mAppId;
+ mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
+
+ // addonId is computed from the principal URI and never propagated
+ mUserContextId = aAttrs.mUserContextId;
+
+ mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
+ mFirstPartyDomain = aAttrs.mFirstPartyDomain;
+}
+
+void
+PrincipalOriginAttributes::InheritFromNecko(const NeckoOriginAttributes& aAttrs)
+{
+ mAppId = aAttrs.mAppId;
+ mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
+
+ // addonId is computed from the principal URI and never propagated
+ mUserContextId = aAttrs.mUserContextId;
+
+ mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
+ mFirstPartyDomain = aAttrs.mFirstPartyDomain;
+}
+
+void
+PrincipalOriginAttributes::StripUserContextIdAndFirstPartyDomain()
+{
+ mUserContextId = nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID;
+ mFirstPartyDomain.Truncate();
+}
+
+void
+DocShellOriginAttributes::InheritFromDocToChildDocShell(const PrincipalOriginAttributes& aAttrs)
+{
+ mAppId = aAttrs.mAppId;
+ mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
+
+ // addonId is computed from the principal URI and never propagated
+ mUserContextId = aAttrs.mUserContextId;
+
+ mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
+ mFirstPartyDomain = aAttrs.mFirstPartyDomain;
+}
+
+void
+NeckoOriginAttributes::InheritFromDocToNecko(const PrincipalOriginAttributes& aAttrs)
+{
+ mAppId = aAttrs.mAppId;
+ mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
+
+ // addonId is computed from the principal URI and never propagated
+ mUserContextId = aAttrs.mUserContextId;
+
+ mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
+ mFirstPartyDomain = aAttrs.mFirstPartyDomain;
+}
+
+void
+NeckoOriginAttributes::InheritFromDocShellToNecko(const DocShellOriginAttributes& aAttrs,
+ const bool aIsTopLevelDocument,
+ nsIURI* aURI)
+{
+ mAppId = aAttrs.mAppId;
+ mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
+
+ // addonId is computed from the principal URI and never propagated
+ mUserContextId = aAttrs.mUserContextId;
+
+ mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
+
+ bool isFirstPartyEnabled = IsFirstPartyEnabled();
+
+ // When the pref is on, we also compute the firstPartyDomain attribute
+ // if this is for top-level document.
+ if (isFirstPartyEnabled && aIsTopLevelDocument) {
+ nsCOMPtr<nsIEffectiveTLDService> tldService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
+ MOZ_ASSERT(tldService);
+ if (!tldService) {
+ return;
+ }
+
+ nsAutoCString baseDomain;
+ tldService->GetBaseDomain(aURI, 0, baseDomain);
+ mFirstPartyDomain = NS_ConvertUTF8toUTF16(baseDomain);
+ } else {
+ mFirstPartyDomain = aAttrs.mFirstPartyDomain;
+ }
+}
+
+void
+OriginAttributes::CreateSuffix(nsACString& aStr) const
+{
+ UniquePtr<URLParams> params(new URLParams());
+ nsAutoString value;
+
+ //
+ // Important: While serializing any string-valued attributes, perform a
+ // release-mode assertion to make sure that they don't contain characters that
+ // will break the quota manager when it uses the serialization for file
+ // naming (see addonId below).
+ //
+
+ if (mAppId != nsIScriptSecurityManager::NO_APP_ID) {
+ value.AppendInt(mAppId);
+ params->Set(NS_LITERAL_STRING("appId"), value);
+ }
+
+ if (mInIsolatedMozBrowser) {
+ params->Set(NS_LITERAL_STRING("inBrowser"), NS_LITERAL_STRING("1"));
+ }
+
+ if (!mAddonId.IsEmpty()) {
+ if (mAddonId.FindCharInSet(dom::quota::QuotaManager::kReplaceChars) != kNotFound) {
+#ifdef MOZ_CRASHREPORTER
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Crash_AddonId"),
+ NS_ConvertUTF16toUTF8(mAddonId));
+#endif
+ MOZ_CRASH();
+ }
+ params->Set(NS_LITERAL_STRING("addonId"), mAddonId);
+ }
+
+ if (mUserContextId != nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID) {
+ value.Truncate();
+ value.AppendInt(mUserContextId);
+ params->Set(NS_LITERAL_STRING("userContextId"), value);
+ }
+
+
+ if (mPrivateBrowsingId) {
+ value.Truncate();
+ value.AppendInt(mPrivateBrowsingId);
+ params->Set(NS_LITERAL_STRING("privateBrowsingId"), value);
+ }
+
+ if (!mFirstPartyDomain.IsEmpty()) {
+ MOZ_RELEASE_ASSERT(mFirstPartyDomain.FindCharInSet(dom::quota::QuotaManager::kReplaceChars) == kNotFound);
+ params->Set(NS_LITERAL_STRING("firstPartyDomain"), mFirstPartyDomain);
+ }
+
+ aStr.Truncate();
+
+ params->Serialize(value);
+ if (!value.IsEmpty()) {
+ aStr.AppendLiteral("^");
+ aStr.Append(NS_ConvertUTF16toUTF8(value));
+ }
+
+// In debug builds, check the whole string for illegal characters too (just in case).
+#ifdef DEBUG
+ nsAutoCString str;
+ str.Assign(aStr);
+ MOZ_ASSERT(str.FindCharInSet(dom::quota::QuotaManager::kReplaceChars) == kNotFound);
+#endif
+}
+
+void
+OriginAttributes::CreateAnonymizedSuffix(nsACString& aStr) const
+{
+ OriginAttributes attrs = *this;
+
+ if (!attrs.mFirstPartyDomain.IsEmpty()) {
+ attrs.mFirstPartyDomain.AssignLiteral("_anonymizedFirstPartyDomain_");
+ }
+
+ attrs.CreateSuffix(aStr);
+}
+
+namespace {
+
+class MOZ_STACK_CLASS PopulateFromSuffixIterator final
+ : public URLParams::ForEachIterator
+{
+public:
+ explicit PopulateFromSuffixIterator(OriginAttributes* aOriginAttributes)
+ : mOriginAttributes(aOriginAttributes)
+ {
+ MOZ_ASSERT(aOriginAttributes);
+ // If mPrivateBrowsingId is passed in as >0 and is not present in the suffix,
+ // then it will remain >0 when it should be 0 according to the suffix. Set to 0 before
+ // iterating to fix this.
+ mOriginAttributes->mPrivateBrowsingId = 0;
+ }
+
+ bool URLParamsIterator(const nsString& aName,
+ const nsString& aValue) override
+ {
+ if (aName.EqualsLiteral("appId")) {
+ nsresult rv;
+ int64_t val = aValue.ToInteger64(&rv);
+ NS_ENSURE_SUCCESS(rv, false);
+ NS_ENSURE_TRUE(val <= UINT32_MAX, false);
+ mOriginAttributes->mAppId = static_cast<uint32_t>(val);
+
+ return true;
+ }
+
+ if (aName.EqualsLiteral("inBrowser")) {
+ if (!aValue.EqualsLiteral("1")) {
+ return false;
+ }
+
+ mOriginAttributes->mInIsolatedMozBrowser = true;
+ return true;
+ }
+
+ if (aName.EqualsLiteral("addonId")) {
+ MOZ_RELEASE_ASSERT(mOriginAttributes->mAddonId.IsEmpty());
+ mOriginAttributes->mAddonId.Assign(aValue);
+ return true;
+ }
+
+ if (aName.EqualsLiteral("userContextId")) {
+ nsresult rv;
+ int64_t val = aValue.ToInteger64(&rv);
+ NS_ENSURE_SUCCESS(rv, false);
+ NS_ENSURE_TRUE(val <= UINT32_MAX, false);
+ mOriginAttributes->mUserContextId = static_cast<uint32_t>(val);
+
+ return true;
+ }
+
+ if (aName.EqualsLiteral("privateBrowsingId")) {
+ nsresult rv;
+ int64_t val = aValue.ToInteger64(&rv);
+ NS_ENSURE_SUCCESS(rv, false);
+ NS_ENSURE_TRUE(val >= 0 && val <= UINT32_MAX, false);
+ mOriginAttributes->mPrivateBrowsingId = static_cast<uint32_t>(val);
+
+ return true;
+ }
+
+ if (aName.EqualsLiteral("firstPartyDomain")) {
+ MOZ_RELEASE_ASSERT(mOriginAttributes->mFirstPartyDomain.IsEmpty());
+ mOriginAttributes->mFirstPartyDomain.Assign(aValue);
+ return true;
+ }
+
+ // No other attributes are supported.
+ return false;
+ }
+
+private:
+ OriginAttributes* mOriginAttributes;
+};
+
+} // namespace
+
+bool
+OriginAttributes::PopulateFromSuffix(const nsACString& aStr)
+{
+ if (aStr.IsEmpty()) {
+ return true;
+ }
+
+ if (aStr[0] != '^') {
+ return false;
+ }
+
+ UniquePtr<URLParams> params(new URLParams());
+ params->ParseInput(Substring(aStr, 1, aStr.Length() - 1));
+
+ PopulateFromSuffixIterator iterator(this);
+ return params->ForEach(iterator);
+}
+
+bool
+OriginAttributes::PopulateFromOrigin(const nsACString& aOrigin,
+ nsACString& aOriginNoSuffix)
+{
+ // RFindChar is only available on nsCString.
+ nsCString origin(aOrigin);
+ int32_t pos = origin.RFindChar('^');
+
+ if (pos == kNotFound) {
+ aOriginNoSuffix = origin;
+ return true;
+ }
+
+ aOriginNoSuffix = Substring(origin, 0, pos);
+ return PopulateFromSuffix(Substring(origin, pos));
+}
+
+void
+OriginAttributes::SyncAttributesWithPrivateBrowsing(bool aInPrivateBrowsing)
+{
+ mPrivateBrowsingId = aInPrivateBrowsing ? 1 : 0;
+}
+
+void
+OriginAttributes::SetFromGenericAttributes(const GenericOriginAttributes& aAttrs)
+{
+ mAppId = aAttrs.mAppId;
+ mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
+ mAddonId = aAttrs.mAddonId;
+ mUserContextId = aAttrs.mUserContextId;
+ mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
+ mFirstPartyDomain = aAttrs.mFirstPartyDomain;
+}
+
+/* static */
+bool
+OriginAttributes::IsFirstPartyEnabled()
+{
+ // Cache the privacy.firstparty.isolate pref.
+ static bool sFirstPartyIsolation = false;
+ static bool sCachedFirstPartyPref = false;
+ if (!sCachedFirstPartyPref) {
+ sCachedFirstPartyPref = true;
+ Preferences::AddBoolVarCache(&sFirstPartyIsolation, "privacy.firstparty.isolate");
+ }
+
+ return sFirstPartyIsolation;
+}
+
+BasePrincipal::BasePrincipal()
+{}
+
+BasePrincipal::~BasePrincipal()
+{}
+
+NS_IMETHODIMP
+BasePrincipal::GetOrigin(nsACString& aOrigin)
+{
+ nsresult rv = GetOriginInternal(aOrigin);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString suffix;
+ mOriginAttributes.CreateSuffix(suffix);
+ aOrigin.Append(suffix);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetOriginNoSuffix(nsACString& aOrigin)
+{
+ return GetOriginInternal(aOrigin);
+}
+
+bool
+BasePrincipal::Subsumes(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration)
+{
+ MOZ_ASSERT(aOther);
+
+ // Expanded principals handle origin attributes for each of their
+ // sub-principals individually, null principals do only simple checks for
+ // pointer equality, and system principals are immune to origin attributes
+ // checks, so only do this check for codebase principals.
+ if (Kind() == eCodebasePrincipal &&
+ OriginAttributesRef() != Cast(aOther)->OriginAttributesRef()) {
+ return false;
+ }
+
+ return SubsumesInternal(aOther, aConsideration);
+}
+
+NS_IMETHODIMP
+BasePrincipal::Equals(nsIPrincipal *aOther, bool *aResult)
+{
+ NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
+ *aResult = Subsumes(aOther, DontConsiderDocumentDomain) &&
+ Cast(aOther)->Subsumes(this, DontConsiderDocumentDomain);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::EqualsConsideringDomain(nsIPrincipal *aOther, bool *aResult)
+{
+ NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
+ *aResult = Subsumes(aOther, ConsiderDocumentDomain) &&
+ Cast(aOther)->Subsumes(this, ConsiderDocumentDomain);
+ return NS_OK;
+}
+
+bool
+BasePrincipal::EqualsIgnoringAddonId(nsIPrincipal *aOther)
+{
+ MOZ_ASSERT(aOther);
+
+ // Note that this will not work for expanded principals, nor is it intended
+ // to.
+ if (!dom::ChromeUtils::IsOriginAttributesEqualIgnoringAddonId(
+ OriginAttributesRef(), Cast(aOther)->OriginAttributesRef())) {
+ return false;
+ }
+
+ return SubsumesInternal(aOther, DontConsiderDocumentDomain) &&
+ Cast(aOther)->SubsumesInternal(this, DontConsiderDocumentDomain);
+}
+
+NS_IMETHODIMP
+BasePrincipal::Subsumes(nsIPrincipal *aOther, bool *aResult)
+{
+ NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
+ *aResult = Subsumes(aOther, DontConsiderDocumentDomain);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::SubsumesConsideringDomain(nsIPrincipal *aOther, bool *aResult)
+{
+ NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
+ *aResult = Subsumes(aOther, ConsiderDocumentDomain);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::CheckMayLoad(nsIURI* aURI, bool aReport, bool aAllowIfInheritsPrincipal)
+{
+ // Check the internal method first, which allows us to quickly approve loads
+ // for the System Principal.
+ if (MayLoadInternal(aURI)) {
+ return NS_OK;
+ }
+
+ nsresult rv;
+ if (aAllowIfInheritsPrincipal) {
+ // If the caller specified to allow loads of URIs that inherit
+ // our principal, allow the load if this URI inherits its principal.
+ bool doesInheritSecurityContext;
+ rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
+ &doesInheritSecurityContext);
+ if (NS_SUCCEEDED(rv) && doesInheritSecurityContext) {
+ return NS_OK;
+ }
+ }
+
+ bool fetchableByAnyone;
+ rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_FETCHABLE_BY_ANYONE, &fetchableByAnyone);
+ if (NS_SUCCEEDED(rv) && fetchableByAnyone) {
+ return NS_OK;
+ }
+
+ if (aReport) {
+ nsCOMPtr<nsIURI> prinURI;
+ rv = GetURI(getter_AddRefs(prinURI));
+ if (NS_SUCCEEDED(rv) && prinURI) {
+ nsScriptSecurityManager::ReportError(nullptr, NS_LITERAL_STRING("CheckSameOriginError"), prinURI, aURI);
+ }
+ }
+
+ return NS_ERROR_DOM_BAD_URI;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetCsp(nsIContentSecurityPolicy** aCsp)
+{
+ NS_IF_ADDREF(*aCsp = mCSP);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::SetCsp(nsIContentSecurityPolicy* aCsp)
+{
+ // Never destroy an existing CSP on the principal.
+ // This method should only be called in rare cases.
+
+ MOZ_ASSERT(!mCSP, "do not destroy an existing CSP");
+ if (mCSP) {
+ return NS_ERROR_ALREADY_INITIALIZED;
+ }
+
+ mCSP = aCsp;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::EnsureCSP(nsIDOMDocument* aDocument,
+ nsIContentSecurityPolicy** aCSP)
+{
+ if (mCSP) {
+ // if there is a CSP already associated with this principal
+ // then just return that - do not overwrite it!!!
+ NS_IF_ADDREF(*aCSP = mCSP);
+ return NS_OK;
+ }
+
+ nsresult rv = NS_OK;
+ mCSP = do_CreateInstance("@mozilla.org/cspcontext;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Store the request context for violation reports
+ rv = aDocument ? mCSP->SetRequestContext(aDocument, nullptr)
+ : mCSP->SetRequestContext(nullptr, this);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_IF_ADDREF(*aCSP = mCSP);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetPreloadCsp(nsIContentSecurityPolicy** aPreloadCSP)
+{
+ NS_IF_ADDREF(*aPreloadCSP = mPreloadCSP);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::EnsurePreloadCSP(nsIDOMDocument* aDocument,
+ nsIContentSecurityPolicy** aPreloadCSP)
+{
+ if (mPreloadCSP) {
+ // if there is a speculative CSP already associated with this principal
+ // then just return that - do not overwrite it!!!
+ NS_IF_ADDREF(*aPreloadCSP = mPreloadCSP);
+ return NS_OK;
+ }
+
+ nsresult rv = NS_OK;
+ mPreloadCSP = do_CreateInstance("@mozilla.org/cspcontext;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Store the request context for violation reports
+ rv = aDocument ? mPreloadCSP->SetRequestContext(aDocument, nullptr)
+ : mPreloadCSP->SetRequestContext(nullptr, this);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_IF_ADDREF(*aPreloadCSP = mPreloadCSP);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetCspJSON(nsAString& outCSPinJSON)
+{
+ outCSPinJSON.Truncate();
+ dom::CSPPolicies jsonPolicies;
+
+ if (!mCSP) {
+ jsonPolicies.ToJSON(outCSPinJSON);
+ return NS_OK;
+ }
+ return mCSP->ToJSON(outCSPinJSON);
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetIsNullPrincipal(bool* aResult)
+{
+ *aResult = Kind() == eNullPrincipal;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetIsCodebasePrincipal(bool* aResult)
+{
+ *aResult = Kind() == eCodebasePrincipal;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetIsExpandedPrincipal(bool* aResult)
+{
+ *aResult = Kind() == eExpandedPrincipal;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetIsSystemPrincipal(bool* aResult)
+{
+ *aResult = Kind() == eSystemPrincipal;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetOriginAttributes(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
+{
+ if (NS_WARN_IF(!ToJSValue(aCx, mOriginAttributes, aVal))) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetOriginSuffix(nsACString& aOriginAttributes)
+{
+ mOriginAttributes.CreateSuffix(aOriginAttributes);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetAppStatus(uint16_t* aAppStatus)
+{
+ if (AppId() == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
+ NS_WARNING("Asking for app status on a principal with an unknown app id");
+ *aAppStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
+ return NS_OK;
+ }
+
+ *aAppStatus = nsScriptSecurityManager::AppStatusForPrincipal(this);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetAppId(uint32_t* aAppId)
+{
+ if (AppId() == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
+ MOZ_ASSERT(false);
+ *aAppId = nsIScriptSecurityManager::NO_APP_ID;
+ return NS_OK;
+ }
+
+ *aAppId = AppId();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetAddonId(nsAString& aAddonId)
+{
+ aAddonId.Assign(mOriginAttributes.mAddonId);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetUserContextId(uint32_t* aUserContextId)
+{
+ *aUserContextId = UserContextId();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetPrivateBrowsingId(uint32_t* aPrivateBrowsingId)
+{
+ *aPrivateBrowsingId = PrivateBrowsingId();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetIsInIsolatedMozBrowserElement(bool* aIsInIsolatedMozBrowserElement)
+{
+ *aIsInIsolatedMozBrowserElement = IsInIsolatedMozBrowserElement();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetUnknownAppId(bool* aUnknownAppId)
+{
+ *aUnknownAppId = AppId() == nsIScriptSecurityManager::UNKNOWN_APP_ID;
+ return NS_OK;
+}
+
+bool
+BasePrincipal::AddonHasPermission(const nsAString& aPerm)
+{
+ if (mOriginAttributes.mAddonId.IsEmpty()) {
+ return false;
+ }
+ nsCOMPtr<nsIAddonPolicyService> aps =
+ do_GetService("@mozilla.org/addons/policy-service;1");
+ NS_ENSURE_TRUE(aps, false);
+
+ bool retval = false;
+ nsresult rv = aps->AddonHasPermission(mOriginAttributes.mAddonId, aPerm, &retval);
+ NS_ENSURE_SUCCESS(rv, false);
+ return retval;
+}
+
+already_AddRefed<BasePrincipal>
+BasePrincipal::CreateCodebasePrincipal(nsIURI* aURI, const PrincipalOriginAttributes& aAttrs)
+{
+ // If the URI is supposed to inherit the security context of whoever loads it,
+ // we shouldn't make a codebase principal for it.
+ bool inheritsPrincipal;
+ nsresult rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
+ &inheritsPrincipal);
+ if (NS_FAILED(rv) || inheritsPrincipal) {
+ return nsNullPrincipal::Create(aAttrs);
+ }
+
+ // Check whether the URI knows what its principal is supposed to be.
+ nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(aURI);
+ if (uriPrinc) {
+ nsCOMPtr<nsIPrincipal> principal;
+ uriPrinc->GetPrincipal(getter_AddRefs(principal));
+ if (!principal) {
+ return nsNullPrincipal::Create(aAttrs);
+ }
+ RefPtr<BasePrincipal> concrete = Cast(principal);
+ return concrete.forget();
+ }
+
+ // Mint a codebase principal.
+ RefPtr<nsPrincipal> codebase = new nsPrincipal();
+ rv = codebase->Init(aURI, aAttrs);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+ return codebase.forget();
+}
+
+already_AddRefed<BasePrincipal>
+BasePrincipal::CreateCodebasePrincipal(const nsACString& aOrigin)
+{
+ MOZ_ASSERT(!StringBeginsWith(aOrigin, NS_LITERAL_CSTRING("[")),
+ "CreateCodebasePrincipal does not support System and Expanded principals");
+
+ MOZ_ASSERT(!StringBeginsWith(aOrigin, NS_LITERAL_CSTRING(NS_NULLPRINCIPAL_SCHEME ":")),
+ "CreateCodebasePrincipal does not support nsNullPrincipal");
+
+ nsAutoCString originNoSuffix;
+ mozilla::PrincipalOriginAttributes attrs;
+ if (!attrs.PopulateFromOrigin(aOrigin, originNoSuffix)) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ return BasePrincipal::CreateCodebasePrincipal(uri, attrs);
+}
+
+already_AddRefed<BasePrincipal>
+BasePrincipal::CloneStrippingUserContextIdAndFirstPartyDomain()
+{
+ PrincipalOriginAttributes attrs = OriginAttributesRef();
+ attrs.StripUserContextIdAndFirstPartyDomain();
+
+ nsAutoCString originNoSuffix;
+ nsresult rv = GetOriginNoSuffix(originNoSuffix);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ nsCOMPtr<nsIURI> uri;
+ rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ return BasePrincipal::CreateCodebasePrincipal(uri, attrs);
+}
+
+bool
+BasePrincipal::AddonAllowsLoad(nsIURI* aURI)
+{
+ if (mOriginAttributes.mAddonId.IsEmpty()) {
+ return false;
+ }
+
+ nsCOMPtr<nsIAddonPolicyService> aps = do_GetService("@mozilla.org/addons/policy-service;1");
+ NS_ENSURE_TRUE(aps, false);
+
+ bool allowed = false;
+ nsresult rv = aps->AddonMayLoadURI(mOriginAttributes.mAddonId, aURI, &allowed);
+ return NS_SUCCEEDED(rv) && allowed;
+}
+
+} // namespace mozilla
diff --git a/caps/BasePrincipal.h b/caps/BasePrincipal.h
new file mode 100644
index 000000000..a2cc219bb
--- /dev/null
+++ b/caps/BasePrincipal.h
@@ -0,0 +1,342 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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 mozilla_BasePrincipal_h
+#define mozilla_BasePrincipal_h
+
+#include "nsIPrincipal.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsJSPrincipals.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/ChromeUtilsBinding.h"
+
+class nsIContentSecurityPolicy;
+class nsIObjectOutputStream;
+class nsIObjectInputStream;
+class nsIURI;
+
+class nsExpandedPrincipal;
+
+namespace mozilla {
+
+class GenericOriginAttributes;
+
+// Base OriginAttributes class. This has several subclass flavors, and is not
+// directly constructable itself.
+class OriginAttributes : public dom::OriginAttributesDictionary
+{
+public:
+ bool operator==(const OriginAttributes& aOther) const
+ {
+ return mAppId == aOther.mAppId &&
+ mInIsolatedMozBrowser == aOther.mInIsolatedMozBrowser &&
+ mAddonId == aOther.mAddonId &&
+ mUserContextId == aOther.mUserContextId &&
+ mPrivateBrowsingId == aOther.mPrivateBrowsingId &&
+ mFirstPartyDomain == aOther.mFirstPartyDomain;
+ }
+ bool operator!=(const OriginAttributes& aOther) const
+ {
+ return !(*this == aOther);
+ }
+
+ // Serializes/Deserializes non-default values into the suffix format, i.e.
+ // |!key1=value1&key2=value2|. If there are no non-default attributes, this
+ // returns an empty string.
+ void CreateSuffix(nsACString& aStr) const;
+
+ // Don't use this method for anything else than debugging!
+ void CreateAnonymizedSuffix(nsACString& aStr) const;
+
+ MOZ_MUST_USE bool PopulateFromSuffix(const nsACString& aStr);
+
+ // Populates the attributes from a string like
+ // |uri!key1=value1&key2=value2| and returns the uri without the suffix.
+ MOZ_MUST_USE bool PopulateFromOrigin(const nsACString& aOrigin,
+ nsACString& aOriginNoSuffix);
+
+ // Helper function to match mIsPrivateBrowsing to existing private browsing
+ // flags. Once all other flags are removed, this can be removed too.
+ void SyncAttributesWithPrivateBrowsing(bool aInPrivateBrowsing);
+
+ void SetFromGenericAttributes(const GenericOriginAttributes& aAttrs);
+
+ // check if "privacy.firstparty.isolate" is enabled.
+ static bool IsFirstPartyEnabled();
+
+protected:
+ OriginAttributes() {}
+ explicit OriginAttributes(const OriginAttributesDictionary& aOther)
+ : OriginAttributesDictionary(aOther) {}
+};
+
+class PrincipalOriginAttributes;
+class DocShellOriginAttributes;
+class NeckoOriginAttributes;
+
+// Various classes in Gecko contain OriginAttributes members, and those
+// OriginAttributes get propagated to other classes according to certain rules.
+// For example, the OriginAttributes on the docshell affect the OriginAttributes
+// for the principal of a document loaded inside it, whose OriginAttributes in
+// turn affect those of network loads and child docshells. To codify and
+// centralize these rules, we introduce separate subclasses for the different
+// flavors, and a variety of InheritFrom* methods to implement the transfer
+// behavior.
+
+// For OriginAttributes stored on principals.
+class PrincipalOriginAttributes : public OriginAttributes
+{
+public:
+ PrincipalOriginAttributes() {}
+ PrincipalOriginAttributes(uint32_t aAppId, bool aInIsolatedMozBrowser)
+ {
+ mAppId = aAppId;
+ mInIsolatedMozBrowser = aInIsolatedMozBrowser;
+ }
+
+ // Inheriting OriginAttributes from docshell to document when user navigates.
+ //
+ // @param aAttrs Origin Attributes of the docshell.
+ // @param aURI The URI of the document.
+ void InheritFromDocShellToDoc(const DocShellOriginAttributes& aAttrs,
+ const nsIURI* aURI);
+
+ // Inherit OriginAttributes from Necko.
+ void InheritFromNecko(const NeckoOriginAttributes& aAttrs);
+
+ void StripUserContextIdAndFirstPartyDomain();
+};
+
+// For OriginAttributes stored on docshells / loadcontexts / browsing contexts.
+class DocShellOriginAttributes : public OriginAttributes
+{
+public:
+ DocShellOriginAttributes() {}
+ DocShellOriginAttributes(uint32_t aAppId, bool aInIsolatedMozBrowser)
+ {
+ mAppId = aAppId;
+ mInIsolatedMozBrowser = aInIsolatedMozBrowser;
+ }
+
+ // Inheriting OriginAttributes from document to child docshell when an
+ // <iframe> is created.
+ //
+ // @param aAttrs Origin Attributes of the document.
+ void
+ InheritFromDocToChildDocShell(const PrincipalOriginAttributes& aAttrs);
+};
+
+// For OriginAttributes stored on Necko.
+class NeckoOriginAttributes : public OriginAttributes
+{
+public:
+ NeckoOriginAttributes() {}
+ NeckoOriginAttributes(uint32_t aAppId, bool aInIsolatedMozBrowser)
+ {
+ mAppId = aAppId;
+ mInIsolatedMozBrowser = aInIsolatedMozBrowser;
+ }
+
+ // Inheriting OriginAttributes from document to necko when a network request
+ // is made.
+ void InheritFromDocToNecko(const PrincipalOriginAttributes& aAttrs);
+
+ // Inheriting OriginAttributes from a docshell when loading a top-level
+ // document.
+ void InheritFromDocShellToNecko(const DocShellOriginAttributes& aAttrs,
+ const bool aIsTopLevelDocument = false,
+ nsIURI* aURI = nullptr);
+};
+
+// For operating on OriginAttributes not associated with any data structure.
+class GenericOriginAttributes : public OriginAttributes
+{
+public:
+ GenericOriginAttributes() {}
+ explicit GenericOriginAttributes(const OriginAttributesDictionary& aOther)
+ : OriginAttributes(aOther) {}
+};
+
+class OriginAttributesPattern : public dom::OriginAttributesPatternDictionary
+{
+public:
+ // To convert a JSON string to an OriginAttributesPattern, do the following:
+ //
+ // OriginAttributesPattern pattern;
+ // if (!pattern.Init(aJSONString)) {
+ // ... // handle failure.
+ // }
+ OriginAttributesPattern() {}
+
+ explicit OriginAttributesPattern(const OriginAttributesPatternDictionary& aOther)
+ : OriginAttributesPatternDictionary(aOther) {}
+
+ // Performs a match of |aAttrs| against this pattern.
+ bool Matches(const OriginAttributes& aAttrs) const
+ {
+ if (mAppId.WasPassed() && mAppId.Value() != aAttrs.mAppId) {
+ return false;
+ }
+
+ if (mInIsolatedMozBrowser.WasPassed() && mInIsolatedMozBrowser.Value() != aAttrs.mInIsolatedMozBrowser) {
+ return false;
+ }
+
+ if (mAddonId.WasPassed() && mAddonId.Value() != aAttrs.mAddonId) {
+ return false;
+ }
+
+ if (mUserContextId.WasPassed() && mUserContextId.Value() != aAttrs.mUserContextId) {
+ return false;
+ }
+
+ if (mPrivateBrowsingId.WasPassed() && mPrivateBrowsingId.Value() != aAttrs.mPrivateBrowsingId) {
+ return false;
+ }
+
+ if (mFirstPartyDomain.WasPassed() && mFirstPartyDomain.Value() != aAttrs.mFirstPartyDomain) {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool Overlaps(const OriginAttributesPattern& aOther) const
+ {
+ if (mAppId.WasPassed() && aOther.mAppId.WasPassed() &&
+ mAppId.Value() != aOther.mAppId.Value()) {
+ return false;
+ }
+
+ if (mInIsolatedMozBrowser.WasPassed() &&
+ aOther.mInIsolatedMozBrowser.WasPassed() &&
+ mInIsolatedMozBrowser.Value() != aOther.mInIsolatedMozBrowser.Value()) {
+ return false;
+ }
+
+ if (mAddonId.WasPassed() && aOther.mAddonId.WasPassed() &&
+ mAddonId.Value() != aOther.mAddonId.Value()) {
+ return false;
+ }
+
+ if (mUserContextId.WasPassed() && aOther.mUserContextId.WasPassed() &&
+ mUserContextId.Value() != aOther.mUserContextId.Value()) {
+ return false;
+ }
+
+ if (mPrivateBrowsingId.WasPassed() && aOther.mPrivateBrowsingId.WasPassed() &&
+ mPrivateBrowsingId.Value() != aOther.mPrivateBrowsingId.Value()) {
+ return false;
+ }
+
+ if (mFirstPartyDomain.WasPassed() && aOther.mFirstPartyDomain.WasPassed() &&
+ mFirstPartyDomain.Value() != aOther.mFirstPartyDomain.Value()) {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+/*
+ * Base class from which all nsIPrincipal implementations inherit. Use this for
+ * default implementations and other commonalities between principal
+ * implementations.
+ *
+ * We should merge nsJSPrincipals into this class at some point.
+ */
+class BasePrincipal : public nsJSPrincipals
+{
+public:
+ BasePrincipal();
+
+ enum DocumentDomainConsideration { DontConsiderDocumentDomain, ConsiderDocumentDomain};
+ bool Subsumes(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration);
+
+ NS_IMETHOD GetOrigin(nsACString& aOrigin) final;
+ NS_IMETHOD GetOriginNoSuffix(nsACString& aOrigin) final;
+ NS_IMETHOD Equals(nsIPrincipal* other, bool* _retval) final;
+ NS_IMETHOD EqualsConsideringDomain(nsIPrincipal* other, bool* _retval) final;
+ NS_IMETHOD Subsumes(nsIPrincipal* other, bool* _retval) final;
+ NS_IMETHOD SubsumesConsideringDomain(nsIPrincipal* other, bool* _retval) final;
+ NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal) final;
+ NS_IMETHOD GetCsp(nsIContentSecurityPolicy** aCsp) override;
+ NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
+ NS_IMETHOD EnsureCSP(nsIDOMDocument* aDocument, nsIContentSecurityPolicy** aCSP) override;
+ NS_IMETHOD GetPreloadCsp(nsIContentSecurityPolicy** aPreloadCSP) override;
+ NS_IMETHOD EnsurePreloadCSP(nsIDOMDocument* aDocument, nsIContentSecurityPolicy** aCSP) override;
+ NS_IMETHOD GetCspJSON(nsAString& outCSPinJSON) override;
+ NS_IMETHOD GetIsNullPrincipal(bool* aResult) override;
+ NS_IMETHOD GetIsCodebasePrincipal(bool* aResult) override;
+ NS_IMETHOD GetIsExpandedPrincipal(bool* aResult) override;
+ NS_IMETHOD GetIsSystemPrincipal(bool* aResult) override;
+ NS_IMETHOD GetOriginAttributes(JSContext* aCx, JS::MutableHandle<JS::Value> aVal) final;
+ NS_IMETHOD GetOriginSuffix(nsACString& aOriginSuffix) final;
+ NS_IMETHOD GetAppStatus(uint16_t* aAppStatus) final;
+ NS_IMETHOD GetAppId(uint32_t* aAppStatus) final;
+ NS_IMETHOD GetAddonId(nsAString& aAddonId) final;
+ NS_IMETHOD GetIsInIsolatedMozBrowserElement(bool* aIsInIsolatedMozBrowserElement) final;
+ NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId) final;
+ NS_IMETHOD GetUserContextId(uint32_t* aUserContextId) final;
+ NS_IMETHOD GetPrivateBrowsingId(uint32_t* aPrivateBrowsingId) final;
+
+ bool EqualsIgnoringAddonId(nsIPrincipal *aOther);
+
+ virtual bool AddonHasPermission(const nsAString& aPerm);
+
+ virtual bool IsOnCSSUnprefixingWhitelist() override { return false; }
+
+ virtual bool IsCodebasePrincipal() const { return false; };
+
+ static BasePrincipal* Cast(nsIPrincipal* aPrin) { return static_cast<BasePrincipal*>(aPrin); }
+ static already_AddRefed<BasePrincipal>
+ CreateCodebasePrincipal(nsIURI* aURI, const PrincipalOriginAttributes& aAttrs);
+ static already_AddRefed<BasePrincipal> CreateCodebasePrincipal(const nsACString& aOrigin);
+
+ const PrincipalOriginAttributes& OriginAttributesRef() { return mOriginAttributes; }
+ uint32_t AppId() const { return mOriginAttributes.mAppId; }
+ uint32_t UserContextId() const { return mOriginAttributes.mUserContextId; }
+ uint32_t PrivateBrowsingId() const { return mOriginAttributes.mPrivateBrowsingId; }
+ bool IsInIsolatedMozBrowserElement() const { return mOriginAttributes.mInIsolatedMozBrowser; }
+
+ enum PrincipalKind {
+ eNullPrincipal,
+ eCodebasePrincipal,
+ eExpandedPrincipal,
+ eSystemPrincipal
+ };
+
+ virtual PrincipalKind Kind() = 0;
+
+ already_AddRefed<BasePrincipal> CloneStrippingUserContextIdAndFirstPartyDomain();
+
+protected:
+ virtual ~BasePrincipal();
+
+ virtual nsresult GetOriginInternal(nsACString& aOrigin) = 0;
+ // Note that this does not check OriginAttributes. Callers that depend on
+ // those must call Subsumes instead.
+ virtual bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsider) = 0;
+
+ // Internal, side-effect-free check to determine whether the concrete
+ // principal would allow the load ignoring any common behavior implemented in
+ // BasePrincipal::CheckMayLoad.
+ virtual bool MayLoadInternal(nsIURI* aURI) = 0;
+ friend class ::nsExpandedPrincipal;
+
+ // Helper to check whether this principal is associated with an addon that
+ // allows unprivileged code to load aURI.
+ bool AddonAllowsLoad(nsIURI* aURI);
+
+ nsCOMPtr<nsIContentSecurityPolicy> mCSP;
+ nsCOMPtr<nsIContentSecurityPolicy> mPreloadCSP;
+ PrincipalOriginAttributes mOriginAttributes;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_BasePrincipal_h */
diff --git a/caps/DomainPolicy.cpp b/caps/DomainPolicy.cpp
new file mode 100644
index 000000000..16029a2bd
--- /dev/null
+++ b/caps/DomainPolicy.cpp
@@ -0,0 +1,260 @@
+/* -*- 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 "mozilla/dom/ContentParent.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "mozilla/Unused.h"
+#include "nsIMessageManager.h"
+#include "nsScriptSecurityManager.h"
+
+namespace mozilla {
+
+using namespace ipc;
+using namespace dom;
+
+NS_IMPL_ISUPPORTS(DomainPolicy, nsIDomainPolicy)
+
+static nsresult
+BroadcastDomainSetChange(DomainSetType aSetType, DomainSetChangeType aChangeType,
+ nsIURI* aDomain = nullptr)
+{
+ MOZ_ASSERT(XRE_IsParentProcess(),
+ "DomainPolicy should only be exposed to the chrome process.");
+
+ nsTArray<ContentParent*> parents;
+ ContentParent::GetAll(parents);
+ if (!parents.Length()) {
+ return NS_OK;
+ }
+
+ OptionalURIParams uri;
+ SerializeURI(aDomain, uri);
+
+ for (uint32_t i = 0; i < parents.Length(); i++) {
+ Unused << parents[i]->SendDomainSetChanged(aSetType, aChangeType, uri);
+ }
+ return NS_OK;
+}
+
+DomainPolicy::DomainPolicy() : mBlacklist(new DomainSet(BLACKLIST))
+ , mSuperBlacklist(new DomainSet(SUPER_BLACKLIST))
+ , mWhitelist(new DomainSet(WHITELIST))
+ , mSuperWhitelist(new DomainSet(SUPER_WHITELIST))
+{
+ if (XRE_IsParentProcess()) {
+ BroadcastDomainSetChange(NO_TYPE, ACTIVATE_POLICY);
+ }
+}
+
+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.get();
+ set.forget(aSet);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DomainPolicy::GetSuperBlacklist(nsIDomainSet** aSet)
+{
+ nsCOMPtr<nsIDomainSet> set = mSuperBlacklist.get();
+ set.forget(aSet);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DomainPolicy::GetWhitelist(nsIDomainSet** aSet)
+{
+ nsCOMPtr<nsIDomainSet> set = mWhitelist.get();
+ set.forget(aSet);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DomainPolicy::GetSuperWhitelist(nsIDomainSet** aSet)
+{
+ nsCOMPtr<nsIDomainSet> set = mSuperWhitelist.get();
+ 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* ssm = nsScriptSecurityManager::GetScriptSecurityManager();
+ if (ssm) {
+ ssm->DeactivateDomainPolicy();
+ }
+ if (XRE_IsParentProcess()) {
+ BroadcastDomainSetChange(NO_TYPE, DEACTIVATE_POLICY);
+ }
+ return NS_OK;
+}
+
+void
+DomainPolicy::CloneDomainPolicy(DomainPolicyClone* aClone)
+{
+ aClone->active() = true;
+ mBlacklist->CloneSet(&aClone->blacklist());
+ mSuperBlacklist->CloneSet(&aClone->superBlacklist());
+ mWhitelist->CloneSet(&aClone->whitelist());
+ mSuperWhitelist->CloneSet(&aClone->superWhitelist());
+}
+
+static
+void
+CopyURIs(const InfallibleTArray<URIParams>& aDomains, nsIDomainSet* aSet)
+{
+ for (uint32_t i = 0; i < aDomains.Length(); i++) {
+ nsCOMPtr<nsIURI> uri = DeserializeURI(aDomains[i]);
+ aSet->Add(uri);
+ }
+}
+
+void
+DomainPolicy::ApplyClone(DomainPolicyClone* aClone)
+{
+ CopyURIs(aClone->blacklist(), mBlacklist);
+ CopyURIs(aClone->whitelist(), mWhitelist);
+ CopyURIs(aClone->superBlacklist(), mSuperBlacklist);
+ CopyURIs(aClone->superWhitelist(), mSuperWhitelist);
+}
+
+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_ISUPPORTS(DomainSet, nsIDomainSet)
+
+NS_IMETHODIMP
+DomainSet::Add(nsIURI* aDomain)
+{
+ nsCOMPtr<nsIURI> clone = GetCanonicalClone(aDomain);
+ NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE);
+ mHashTable.PutEntry(clone);
+ if (XRE_IsParentProcess())
+ return BroadcastDomainSetChange(mType, ADD_DOMAIN, aDomain);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DomainSet::Remove(nsIURI* aDomain)
+{
+ nsCOMPtr<nsIURI> clone = GetCanonicalClone(aDomain);
+ NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE);
+ mHashTable.RemoveEntry(clone);
+ if (XRE_IsParentProcess())
+ return BroadcastDomainSetChange(mType, REMOVE_DOMAIN, aDomain);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DomainSet::Clear()
+{
+ mHashTable.Clear();
+ if (XRE_IsParentProcess())
+ return BroadcastDomainSetChange(mType, CLEAR_DOMAINS);
+
+ 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;
+
+}
+
+NS_IMETHODIMP
+DomainSet::GetType(uint32_t* aType)
+{
+ *aType = mType;
+ return NS_OK;
+}
+
+void
+DomainSet::CloneSet(InfallibleTArray<URIParams>* aDomains)
+{
+ for (auto iter = mHashTable.Iter(); !iter.Done(); iter.Next()) {
+ nsIURI* key = iter.Get()->GetKey();
+
+ URIParams uri;
+ SerializeURI(key, uri);
+
+ aDomains->AppendElement(uri);
+ }
+}
+
+} /* namespace mozilla */
diff --git a/caps/DomainPolicy.h b/caps/DomainPolicy.h
new file mode 100644
index 000000000..86648fe0b
--- /dev/null
+++ b/caps/DomainPolicy.h
@@ -0,0 +1,72 @@
+/* -*- 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 {
+
+namespace ipc {
+class URIParams;
+} // namespace ipc
+
+enum DomainSetChangeType{
+ ACTIVATE_POLICY,
+ DEACTIVATE_POLICY,
+ ADD_DOMAIN,
+ REMOVE_DOMAIN,
+ CLEAR_DOMAINS
+};
+
+enum DomainSetType{
+ NO_TYPE,
+ BLACKLIST,
+ SUPER_BLACKLIST,
+ WHITELIST,
+ SUPER_WHITELIST
+};
+
+class DomainSet final : public nsIDomainSet
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMAINSET
+
+ explicit DomainSet(DomainSetType aType)
+ : mType(aType)
+ {}
+
+ void CloneSet(InfallibleTArray<mozilla::ipc::URIParams>* aDomains);
+
+protected:
+ virtual ~DomainSet() {}
+ nsTHashtable<nsURIHashKey> mHashTable;
+ DomainSetType mType;
+};
+
+class DomainPolicy final : public nsIDomainPolicy
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMAINPOLICY
+ DomainPolicy();
+
+private:
+ virtual ~DomainPolicy();
+
+ RefPtr<DomainSet> mBlacklist;
+ RefPtr<DomainSet> mSuperBlacklist;
+ RefPtr<DomainSet> mWhitelist;
+ RefPtr<DomainSet> mSuperWhitelist;
+};
+
+} /* namespace mozilla */
+
+#endif /* DomainPolicy_h__ */
diff --git a/caps/moz.build b/caps/moz.build
new file mode 100644
index 000000000..58b45e360
--- /dev/null
+++ b/caps/moz.build
@@ -0,0 +1,63 @@
+# -*- Mode: python; 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/.
+
+MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
+MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
+BROWSER_CHROME_MANIFESTS += ['tests/mochitest/browser.ini']
+XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
+
+# Hack to make this file available as a resource:// URI.
+TESTING_JS_MODULES += [
+ 'tests/mochitest/resource_test_file.html',
+]
+
+XPIDL_SOURCES += [
+ 'nsIAddonPolicyService.idl',
+ 'nsIDomainPolicy.idl',
+ 'nsIPrincipal.idl',
+ 'nsIScriptSecurityManager.idl',
+]
+
+XPIDL_MODULE = 'caps'
+
+EXPORTS += [
+ 'nsJSPrincipals.h',
+ 'nsNullPrincipal.h',
+ 'nsNullPrincipalURI.h',
+]
+
+EXPORTS.mozilla = [
+ 'BasePrincipal.h'
+]
+
+SOURCES += [
+ # Compile this separately since nsExceptionHandler.h conflicts
+ # with something from nsNullPrincipal.cpp.
+ 'BasePrincipal.cpp',
+]
+
+UNIFIED_SOURCES += [
+ 'DomainPolicy.cpp',
+ 'nsJSPrincipals.cpp',
+ 'nsNullPrincipal.cpp',
+ 'nsNullPrincipalURI.cpp',
+ 'nsPrincipal.cpp',
+ 'nsScriptSecurityManager.cpp',
+ 'nsSystemPrincipal.cpp',
+]
+
+LOCAL_INCLUDES += [
+ '/docshell/base',
+ '/dom/base',
+ '/js/xpconnect/src',
+]
+
+if CONFIG['ENABLE_TESTS']:
+ DIRS += ['tests/gtest']
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
diff --git a/caps/nsIAddonPolicyService.idl b/caps/nsIAddonPolicyService.idl
new file mode 100644
index 000000000..cf444acaa
--- /dev/null
+++ b/caps/nsIAddonPolicyService.idl
@@ -0,0 +1,78 @@
+/* -*- Mode: C; tab-width: 8; 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"
+#include "nsIURI.idl"
+
+/**
+ * This interface allows the security manager to query custom per-addon security
+ * policy.
+ */
+[scriptable,uuid(8a034ef9-9d14-4c5d-8319-06c1ab574baa)]
+interface nsIAddonPolicyService : nsISupports
+{
+ /**
+ * Returns the base content security policy, which is applied to all
+ * extension documents, in addition to any custom policies.
+ */
+ readonly attribute AString baseCSP;
+
+ /**
+ * Returns the default content security policy which applies to extension
+ * documents which do not specify any custom policies.
+ */
+ readonly attribute AString defaultCSP;
+
+ /**
+ * Returns the content security policy which applies to documents belonging
+ * to the extension with the given ID. This may be either a custom policy,
+ * if one was supplied, or the default policy if one was not.
+ */
+ AString getAddonCSP(in AString aAddonId);
+
+ /**
+ * Returns the generated background page as a data-URI, if any. If the addon
+ * does not have an auto-generated background page, an empty string is
+ * returned.
+ */
+ ACString getGeneratedBackgroundPageUrl(in ACString aAddonId);
+
+ /**
+ * Returns true if the addon was granted the |aPerm| API permission.
+ */
+ boolean addonHasPermission(in AString aAddonId, in AString aPerm);
+
+ /**
+ * Returns true if unprivileged code associated with the given addon may load
+ * data from |aURI|.
+ */
+ boolean addonMayLoadURI(in AString aAddonId, in nsIURI aURI);
+
+ /**
+ * Returns true if a given extension:// URI is web-accessible.
+ */
+ boolean extensionURILoadableByAnyone(in nsIURI aURI);
+
+ /**
+ * Maps an extension URI to the ID of the addon it belongs to.
+ */
+ AString extensionURIToAddonId(in nsIURI aURI);
+};
+
+/**
+ * This interface exposes functionality related to add-on content policy
+ * enforcement.
+ */
+[scriptable,uuid(7a4fe60b-9131-45f5-83f3-dc63b5d71a5d)]
+interface nsIAddonContentPolicy : nsISupports
+{
+ /**
+ * Checks a custom content security policy string, to ensure that it meets
+ * minimum security requirements. Returns null for valid policies, or a
+ * string describing the error for invalid policies.
+ */
+ AString validateAddonCSP(in AString aPolicyString);
+};
diff --git a/caps/nsIDomainPolicy.idl b/caps/nsIDomainPolicy.idl
new file mode 100644
index 000000000..54b0de4db
--- /dev/null
+++ b/caps/nsIDomainPolicy.idl
@@ -0,0 +1,80 @@
+/* -*- 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;
+
+%{ C++
+namespace mozilla {
+namespace dom {
+class DomainPolicyClone;
+}
+}
+%}
+
+[ptr] native DomainPolicyClonePtr(mozilla::dom::DomainPolicyClone);
+
+/*
+ * 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(82b24a20-6701-4d40-a0f9-f5dc7321b555)]
+interface nsIDomainPolicy : nsISupports
+{
+ readonly attribute nsIDomainSet blacklist;
+ readonly attribute nsIDomainSet superBlacklist;
+ readonly attribute nsIDomainSet whitelist;
+ readonly attribute nsIDomainSet superWhitelist;
+
+ void deactivate();
+
+ [noscript, notxpcom] void cloneDomainPolicy(in DomainPolicyClonePtr aClone);
+ [noscript, notxpcom] void applyClone(in DomainPolicyClonePtr aClone);
+};
+
+[scriptable, builtinclass, uuid(665c981b-0a0f-4229-ac06-a826e02d4f69)]
+interface nsIDomainSet : nsISupports
+{
+ /*
+ * The type of the set. See: DomainSetType
+ */
+ [noscript] readonly attribute uint32_t type;
+
+ /*
+ * 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);
+};
diff --git a/caps/nsIPrincipal.idl b/caps/nsIPrincipal.idl
new file mode 100644
index 000000000..d278decdb
--- /dev/null
+++ b/caps/nsIPrincipal.idl
@@ -0,0 +1,367 @@
+/* -*- 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/. */
+
+/* Defines the abstract interface for a principal. */
+
+#include "nsISerializable.idl"
+
+%{C++
+struct JSPrincipals;
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+%}
+
+interface nsIURI;
+interface nsIContentSecurityPolicy;
+interface nsIDOMDocument;
+
+[ptr] native JSContext(JSContext);
+[ptr] native JSPrincipals(JSPrincipals);
+[ptr] native PrincipalArray(nsTArray<nsCOMPtr<nsIPrincipal> >);
+
+[scriptable, builtinclass, uuid(3da7b133-f1a0-4de9-a2bc-5c49014c1077)]
+interface nsIPrincipal : nsISerializable
+{
+ /**
+ * Returns whether the other principal is equivalent to this principal.
+ * Principals are considered equal if they are the same principal, or
+ * they have the same origin.
+ */
+ boolean equals(in nsIPrincipal other);
+
+ /**
+ * Like equals, but takes document.domain changes into account.
+ */
+ boolean equalsConsideringDomain(in nsIPrincipal other);
+
+ %{C++
+ inline bool Equals(nsIPrincipal* aOther) {
+ bool equal = false;
+ return NS_SUCCEEDED(Equals(aOther, &equal)) && equal;
+ }
+
+ inline bool EqualsConsideringDomain(nsIPrincipal* aOther) {
+ bool equal = false;
+ return NS_SUCCEEDED(EqualsConsideringDomain(aOther, &equal)) && equal;
+ }
+ %}
+
+ /**
+ * Returns a hash value for the principal.
+ */
+ [noscript] readonly attribute unsigned long hashValue;
+
+ /**
+ * The codebase URI to which this principal pertains. This is
+ * generally the document URI.
+ */
+ readonly attribute nsIURI URI;
+
+ /**
+ * The domain URI to which this principal pertains.
+ * This is null unless script successfully sets document.domain to our URI
+ * or a superdomain of our URI.
+ * Setting this has no effect on the URI.
+ * See https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin
+ */
+ [noscript] attribute nsIURI domain;
+
+ /**
+ * Returns whether the other principal is equal to or weaker than this
+ * principal. Principals are equal if they are the same object or they
+ * have the same origin.
+ *
+ * Thus a principal always subsumes itself.
+ *
+ * The system principal subsumes itself and all other principals.
+ *
+ * A null principal (corresponding to an unknown, hence assumed minimally
+ * privileged, security context) is not equal to any other principal
+ * (including other null principals), and therefore does not subsume
+ * anything but itself.
+ */
+ boolean subsumes(in nsIPrincipal other);
+
+ /**
+ * Same as the previous method, subsumes(), but takes document.domain into
+ * account.
+ */
+ boolean subsumesConsideringDomain(in nsIPrincipal other);
+
+ %{C++
+ inline bool Subsumes(nsIPrincipal* aOther) {
+ bool subsumes = false;
+ return NS_SUCCEEDED(Subsumes(aOther, &subsumes)) && subsumes;
+ }
+
+ inline bool SubsumesConsideringDomain(nsIPrincipal* aOther) {
+ bool subsumes = false;
+ return NS_SUCCEEDED(SubsumesConsideringDomain(aOther, &subsumes)) && subsumes;
+ }
+ %}
+
+ /**
+ * Checks whether this principal is allowed to load the network resource
+ * located at the given URI under the same-origin policy. This means that
+ * codebase principals are only allowed to load resources from the same
+ * domain, the system principal is allowed to load anything, and null
+ * principals can only load URIs where they are the principal. This is
+ * changed by the optional flag allowIfInheritsPrincipal (which defaults to
+ * false) which allows URIs that inherit their loader's principal.
+ *
+ * If the load is allowed this function does nothing. If the load is not
+ * allowed the function throws NS_ERROR_DOM_BAD_URI.
+ *
+ * NOTE: Other policies might override this, such as the Access-Control
+ * specification.
+ * NOTE: The 'domain' attribute has no effect on the behaviour of this
+ * function.
+ *
+ *
+ * @param uri The URI about to be loaded.
+ * @param report If true, will report a warning to the console service
+ * if the load is not allowed.
+ * @param allowIfInheritsPrincipal If true, the load is allowed if the
+ * loadee inherits the principal of the
+ * loader.
+ * @throws NS_ERROR_DOM_BAD_URI if the load is not allowed.
+ */
+ void checkMayLoad(in nsIURI uri, in boolean report,
+ in boolean allowIfInheritsPrincipal);
+
+ /**
+ * A Content Security Policy associated with this principal.
+ * Use this function to query the associated CSP with this principal.
+ * Please *only* use this function to *set* a CSP when you know exactly what you are doing.
+ * Most likely you want to call ensureCSP instead of setCSP.
+ */
+ [noscript] attribute nsIContentSecurityPolicy csp;
+
+ /*
+ * Use this function to query a CSP associated with this principal.
+ * If no CSP is associated with this principal then one is created
+ * internally and setRequestContext is called on the CSP using aDocument.
+ *
+ * Please note if aDocument is null, then setRequestContext on the
+ * CSP object is called using the current principal.
+ */
+ [noscript] nsIContentSecurityPolicy ensureCSP(in nsIDOMDocument aDocument);
+
+ /**
+ * A speculative Content Security Policy associated with this
+ * principal. Set during speculative loading (preloading) and
+ * used *only* for preloads.
+ *
+ * If you want to query the CSP associated with that principal,
+ * then this is *not* what you want. Instead query 'csp'.
+ */
+ [noscript] readonly attribute nsIContentSecurityPolicy preloadCsp;
+
+ /*
+ * Use this function to query a speculative CSP associated with this
+ * principal. If no speculative CSP is associated with this principal
+ * then one is created internally and setRequestContext is called on
+ * the CSP using aDocument.
+ *
+ * Please note if aDocument is null, then setRequestContext on the
+ * speculative CSP object is called using the current principal.
+ */
+ [noscript] nsIContentSecurityPolicy ensurePreloadCSP(in nsIDOMDocument aDocument);
+
+ /**
+ * The CSP of the principal in JSON notation.
+ * Note, that the CSP itself is not exposed to JS, but script
+ * should be able to obtain a JSON representation of the CSP.
+ */
+ readonly attribute AString cspJSON;
+
+ /**
+ * A dictionary of the non-default origin attributes associated with this
+ * nsIPrincipal.
+ *
+ * Attributes are tokens that are taken into account when determining whether
+ * two principals are same-origin - if any attributes differ, the principals
+ * are cross-origin, even if the scheme, host, and port are the same.
+ * Attributes should also be considered for all security and bucketing decisions,
+ * even those which make non-standard comparisons (like cookies, which ignore
+ * scheme, or quotas, which ignore subdomains).
+ *
+ * If you're looking for an easy-to-use canonical stringification of the origin
+ * attributes, see |originSuffix| below.
+ */
+ [implicit_jscontext]
+ readonly attribute jsval originAttributes;
+
+ /**
+ * A canonical representation of the origin for this principal. This
+ * consists of a base string (which, for codebase principals, is of the
+ * format scheme://host:port), concatenated with |originAttributes| (see
+ * below).
+ *
+ * We maintain the invariant that principalA.equals(principalB) if and only
+ * if principalA.origin == principalB.origin.
+ */
+ readonly attribute ACString origin;
+
+ /**
+ * The base part of |origin| without the concatenation with |originSuffix|.
+ * This doesn't have the important invariants described above with |origin|,
+ * and as such should only be used for legacy situations.
+ */
+ readonly attribute ACString originNoSuffix;
+
+ /**
+ * A string of the form !key1=value1&key2=value2, where each pair represents
+ * an attribute with a non-default value. If all attributes have default
+ * values, this is the empty string.
+ *
+ * The value of .originSuffix is automatically serialized into .origin, so any
+ * consumers using that are automatically origin-attribute-aware. Consumers with
+ * special requirements must inspect and compare .originSuffix manually.
+ */
+ readonly attribute AUTF8String originSuffix;
+
+ /**
+ * The base domain of the codebase URI to which this principal pertains
+ * (generally the document URI), handling null principals and
+ * non-hierarchical schemes correctly.
+ */
+ readonly attribute ACString baseDomain;
+
+ const short APP_STATUS_NOT_INSTALLED = 0;
+ const short APP_STATUS_INSTALLED = 1;
+ const short APP_STATUS_PRIVILEGED = 2;
+ const short APP_STATUS_CERTIFIED = 3;
+
+ /**
+ * Gets the principal's app status, which indicates whether the principal
+ * corresponds to "app code", and if it does, how privileged that code is.
+ * This method returns one of the APP_STATUS constants above.
+ *
+ * Note that a principal may have
+ *
+ * appId != nsIScriptSecurityManager::NO_APP_ID &&
+ * appId != nsIScriptSecurityManager::UNKNOWN_APP_ID
+ *
+ * and still have appStatus == APP_STATUS_NOT_INSTALLED. That's because
+ * appId identifies the app that contains this principal, but a window
+ * might be contained in an app and not be running code that the app has
+ * vouched for. For example, the window might be inside an <iframe
+ * mozbrowser>, or the window's origin might not match the app's origin.
+ *
+ * If you're doing a check to determine "does this principal correspond to
+ * app code?", you must check appStatus; checking appId != NO_APP_ID is not
+ * sufficient.
+ */
+ [infallible] readonly attribute unsigned short appStatus;
+
+ /**
+ * Gets the id of the app this principal is inside. If this principal is
+ * not inside an app, returns nsIScriptSecurityManager::NO_APP_ID.
+ *
+ * Note that this principal does not necessarily have the permissions of
+ * the app identified by appId. For example, this principal might
+ * correspond to an iframe whose origin differs from that of the app frame
+ * containing it. In this case, the iframe will have the appId of its
+ * containing app frame, but the iframe must not run with the app's
+ * permissions.
+ *
+ * Similarly, this principal might correspond to an <iframe mozbrowser>
+ * inside an app frame; in this case, the content inside the iframe should
+ * not have any of the app's permissions, even if the iframe is at the same
+ * origin as the app.
+ *
+ * If you're doing a security check based on appId, you must check
+ * appStatus as well.
+ */
+ [infallible] readonly attribute unsigned long appId;
+
+ /**
+ * Gets the ID of the add-on this principal belongs to.
+ */
+ readonly attribute AString addonId;
+
+ /**
+ * Gets the id of the user context this principal is inside. If this
+ * principal is inside the default userContext, this returns
+ * nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID.
+ */
+ [infallible] readonly attribute unsigned long userContextId;
+
+ /**
+ * Gets the id of the private browsing state of the context containing
+ * this principal. If the principal has a private browsing value of 0, it
+ * is not in private browsing.
+ */
+ [infallible] readonly attribute unsigned long privateBrowsingId;
+
+ /**
+ * Returns true iff the principal is inside an isolated mozbrowser element.
+ * <iframe mozbrowser mozapp> and <xul:browser> are not considered to be
+ * mozbrowser elements. <iframe mozbrowser noisolation> does not count as
+ * isolated since isolation is disabled. Isolation can only be disabled if
+ * the containing document is chrome.
+ */
+ [infallible] readonly attribute boolean isInIsolatedMozBrowserElement;
+
+ /**
+ * Returns true if this principal has an unknown appId. This shouldn't
+ * generally be used. We only expose it due to not providing the correct
+ * appId everywhere where we construct principals.
+ */
+ [infallible] readonly attribute boolean unknownAppId;
+
+ /**
+ * Returns true iff this is a null principal (corresponding to an
+ * unknown, hence assumed minimally privileged, security context).
+ */
+ [infallible] readonly attribute boolean isNullPrincipal;
+
+ /**
+ * Returns true iff this principal corresponds to a codebase origin.
+ */
+ [infallible] readonly attribute boolean isCodebasePrincipal;
+
+ /**
+ * Returns true iff this is an expanded principal.
+ */
+ [infallible] readonly attribute boolean isExpandedPrincipal;
+
+ /**
+ * Returns true iff this is the system principal.
+ */
+ [infallible] readonly attribute boolean isSystemPrincipal;
+
+ /**
+ * Returns true if this principal's origin is recognized as being on the
+ * whitelist of sites that can use the CSS Unprefixing Service.
+ *
+ * (This interface provides a trivial implementation, just returning false;
+ * subclasses can implement something more complex as-needed.)
+ */
+ [noscript,notxpcom,nostdcall] bool IsOnCSSUnprefixingWhitelist();
+};
+
+/**
+ * If nsSystemPrincipal is too risky to use, but we want a principal to access
+ * more than one origin, nsExpandedPrincipals letting us define an array of
+ * principals it subsumes. So script with an nsExpandedPrincipals will gain
+ * same origin access when at least one of its principals it contains gained
+ * sameorigin acccess. An nsExpandedPrincipal will be subsumed by the system
+ * principal, and by another nsExpandedPrincipal that has all its principals.
+ * It is added for jetpack content-scripts to let them interact with the
+ * content and a well defined set of other domains, without the risk of
+ * leaking out a system principal to the content. See: Bug 734891
+ */
+[uuid(f3e177Df-6a5e-489f-80a7-2dd1481471d8)]
+interface nsIExpandedPrincipal : nsISupports
+{
+ /**
+ * An array of principals that the expanded principal subsumes.
+ * Note: this list is not reference counted, it is shared, so
+ * should not be changed and should only be used ephemerally.
+ */
+ [noscript] readonly attribute PrincipalArray whiteList;
+};
diff --git a/caps/nsIScriptSecurityManager.idl b/caps/nsIScriptSecurityManager.idl
new file mode 100644
index 000000000..08debc528
--- /dev/null
+++ b/caps/nsIScriptSecurityManager.idl
@@ -0,0 +1,305 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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"
+#include "nsIPrincipal.idl"
+interface nsIURI;
+interface nsIChannel;
+interface nsIClassInfo;
+interface nsIDocShell;
+interface nsIDomainPolicy;
+interface nsILoadContext;
+
+%{ C++
+#include "jspubtd.h"
+
+namespace mozilla {
+namespace dom {
+class DomainPolicyClone;
+}
+}
+%}
+
+[ptr] native JSContextPtr(JSContext);
+[ptr] native JSObjectPtr(JSObject);
+[ptr] native DomainPolicyClonePtr(mozilla::dom::DomainPolicyClone);
+
+[scriptable, uuid(51daad87-3a0c-44cc-b620-7356801c9022)]
+interface nsIScriptSecurityManager : nsISupports
+{
+ /**
+ * For each of these hooks returning NS_OK means 'let the action continue'.
+ * Returning an error code means 'veto the action'. XPConnect will return
+ * false to the js engine if the action is vetoed. The implementor of this
+ * interface is responsible for setting a JS exception into the JSContext
+ * if that is appropriate.
+ */
+ [noscript] void canCreateWrapper(in JSContextPtr aJSContext,
+ in nsIIDRef aIID,
+ in nsISupports aObj,
+ in nsIClassInfo aClassInfo);
+
+ [noscript] void canCreateInstance(in JSContextPtr aJSContext,
+ in nsCIDRef aCID);
+
+ [noscript] void canGetService(in JSContextPtr aJSContext,
+ in nsCIDRef aCID);
+
+ /**
+ * Check that the script currently running in context "cx" can load "uri".
+ *
+ * Will return error code NS_ERROR_DOM_BAD_URI if the load request
+ * should be denied.
+ *
+ * @param cx the JSContext of the script causing the load
+ * @param uri the URI that is being loaded
+ */
+ [noscript] void checkLoadURIFromScript(in JSContextPtr cx, in nsIURI uri);
+
+ /**
+ * Default CheckLoadURI permissions
+ */
+ // Default permissions
+ const unsigned long STANDARD = 0;
+
+ // Indicate that the load is a load of a new document that is not
+ // user-triggered. Here "user-triggered" could be broadly interpreted --
+ // for example, scripted sets of window.location.href might be treated as
+ // "user-triggered" in some circumstances. A typical example of a load
+ // that is not user-triggered is a <meta> refresh load. If this flag is
+ // set, the load will be denied if the originating principal's URI has the
+ // nsIProtocolHandler::URI_FORBIDS_AUTOMATIC_DOCUMENT_REPLACEMENT flag set.
+ const unsigned long LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT = 1 << 0;
+
+ // Allow the loading of chrome URLs by non-chrome URLs. Use with great
+ // care! This will actually allow the loading of any URI which has the
+ // nsIProtocolHandler::URI_IS_UI_RESOURCE protocol handler flag set. Ths
+ // probably means at least chrome: and resource:.
+ const unsigned long ALLOW_CHROME = 1 << 1;
+
+ // Don't allow URLs which would inherit the caller's principal (such as
+ // javascript: or data:) to load. See
+ // nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT.
+ const unsigned long DISALLOW_INHERIT_PRINCIPAL = 1 << 2;
+
+ // Alias for DISALLOW_INHERIT_PRINCIPAL for backwards compat with
+ // JS-implemented extensions.
+ const unsigned long DISALLOW_SCRIPT_OR_DATA = DISALLOW_INHERIT_PRINCIPAL;
+
+ // Don't allow javascript: URLs to load
+ // WARNING: Support for this value was added in Mozilla 1.7.8 and
+ // Firefox 1.0.4. Use in prior versions WILL BE IGNORED.
+ // When using this, make sure that you actually want DISALLOW_SCRIPT, not
+ // DISALLOW_INHERIT_PRINCIPAL
+ const unsigned long DISALLOW_SCRIPT = 1 << 3;
+
+ // Do not report errors if we just want to check if a principal can load
+ // a URI to not unnecessarily spam the error console.
+ const unsigned long DONT_REPORT_ERRORS = 1 << 4;
+
+ /**
+ * Check that content with principal aPrincipal can load "uri".
+ *
+ * Will return error code NS_ERROR_DOM_BAD_URI if the load request
+ * should be denied.
+ *
+ * @param aPrincipal the principal identifying the actor causing the load
+ * @param uri the URI that is being loaded
+ * @param flags the permission set, see above
+ */
+ void checkLoadURIWithPrincipal(in nsIPrincipal aPrincipal,
+ in nsIURI uri,
+ in unsigned long flags);
+
+ /**
+ * Similar to checkLoadURIWithPrincipal but there are two differences:
+ *
+ * 1) The URI is a string, not a URI object.
+ * 2) This function assumes that the URI may still be subject to fixup (and
+ * hence will check whether fixed-up versions of the URI are allowed to
+ * load as well); if any of the versions of this URI is not allowed, this
+ * function will return error code NS_ERROR_DOM_BAD_URI.
+ */
+ void checkLoadURIStrWithPrincipal(in nsIPrincipal aPrincipal,
+ in AUTF8String uri,
+ in unsigned long flags);
+
+ ///////////////// Principals ///////////////////////
+
+ /**
+ * Return the all-powerful system principal.
+ */
+ nsIPrincipal getSystemPrincipal();
+
+ /**
+ * Returns a principal that has the given information.
+ * @param appId is the app id of the principal. It can't be UNKNOWN_APP_ID.
+ * @param inMozBrowser is true if the principal has to be considered as
+ * inside a mozbrowser frame.
+ *
+ * @deprecated use createCodebasePrincipal instead.
+ */
+ [deprecated] nsIPrincipal getAppCodebasePrincipal(in nsIURI uri,
+ in unsigned long appId,
+ in boolean inMozBrowser);
+
+ /**
+ * Returns a principal that has the appId and inMozBrowser of the load
+ * context.
+ * @param loadContext to get appId/inMozBrowser from.
+ */
+ nsIPrincipal getLoadContextCodebasePrincipal(in nsIURI uri,
+ in nsILoadContext loadContext);
+
+ /**
+ * Returns a principal that has the appId and inMozBrowser of the docshell
+ * inside a mozbrowser frame.
+ * @param docShell to get appId/inMozBrowser from.
+ */
+ nsIPrincipal getDocShellCodebasePrincipal(in nsIURI uri,
+ in nsIDocShell docShell);
+
+ /**
+ * Returns a principal with that has the same origin as uri and is not part
+ * of an appliction.
+ * The returned principal will have appId = NO_APP_ID.
+ *
+ * @deprecated use createCodebasePrincipal instead.
+ */
+ [deprecated] nsIPrincipal getNoAppCodebasePrincipal(in nsIURI uri);
+
+ /**
+ * Legacy method for getting a principal with no origin attributes.
+ *
+ * @deprecated use createCodebasePrincipal instead.
+ */
+ [deprecated] nsIPrincipal getCodebasePrincipal(in nsIURI uri);
+
+ /**
+ * Returns a principal whose origin is composed of |uri| and |originAttributes|.
+ * See nsIPrincipal.idl for a description of origin attributes, and
+ * ChromeUtils.webidl for a list of origin attributes and their defaults.
+ */
+ [implicit_jscontext]
+ nsIPrincipal createCodebasePrincipal(in nsIURI uri, in jsval originAttributes);
+
+ /**
+ * Returns a principal whose origin is the one we pass in.
+ * See nsIPrincipal.idl for a description of origin attributes, and
+ * ChromeUtils.webidl for a list of origin attributes and their defaults.
+ */
+ nsIPrincipal createCodebasePrincipalFromOrigin(in ACString origin);
+
+ /**
+ * Returns a unique nonce principal with |originAttributes|.
+ * See nsIPrincipal.idl for a description of origin attributes, and
+ * ChromeUtils.webidl for a list of origin attributes and their defaults.
+ */
+ [implicit_jscontext]
+ nsIPrincipal createNullPrincipal(in jsval originAttributes);
+
+ /**
+ * Returns OK if aSourceURI and target have the same "origin"
+ * (scheme, host, and port).
+ * ReportError flag suppresses error reports for functions that
+ * don't need reporting.
+ */
+ void checkSameOriginURI(in nsIURI aSourceURI,
+ in nsIURI aTargetURI,
+ in boolean reportError);
+ /**
+ * Get the principal for the given channel. This will typically be the
+ * channel owner if there is one, and the codebase principal for the
+ * channel's URI otherwise. aChannel must not be null.
+ */
+ nsIPrincipal getChannelResultPrincipal(in nsIChannel aChannel);
+
+ /**
+ * Temporary API until bug 1220687 is fixed.
+ *
+ * Returns the same value as getChannelResultPrincipal, but ignoring
+ * sandboxing. Specifically, if sandboxing would have prevented the
+ * channel's triggering principal from being returned by
+ * getChannelResultPrincipal, the triggering principal will be returned
+ * by this method.
+ *
+ * Note that this method only ignores sandboxing of the channel in
+ * question, it does not ignore sandboxing of any channels further up a
+ * document chain. The triggering principal itself may still be the null
+ * principal due to sandboxing further up a document chain. In that regard
+ * the ignoring of sandboxing is limited.
+ */
+ [noscript, nostdcall]
+ nsIPrincipal getChannelResultPrincipalIfNotSandboxed(in nsIChannel aChannel);
+
+ /**
+ * Get the codebase principal for the channel's URI.
+ * aChannel must not be null.
+ */
+ nsIPrincipal getChannelURIPrincipal(in nsIChannel aChannel);
+
+ /**
+ * Check whether a given principal is a system principal. This allows us
+ * to avoid handing back the system principal to script while allowing
+ * script to check whether a given principal is system.
+ */
+ boolean isSystemPrincipal(in nsIPrincipal aPrincipal);
+%{C++
+ bool IsSystemPrincipal(nsIPrincipal* aPrincipal) {
+ bool isSystem = false;
+ IsSystemPrincipal(aPrincipal, &isSystem);
+ return isSystem;
+ }
+%}
+
+ const unsigned long NO_APP_ID = 0;
+ const unsigned long UNKNOWN_APP_ID = 4294967295; // UINT32_MAX
+ const unsigned long SAFEBROWSING_APP_ID = 4294967294; // UINT32_MAX - 1
+
+ const unsigned long DEFAULT_USER_CONTEXT_ID = 0;
+
+ /**
+ * 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;
+
+ /**
+ * Only the parent process can directly access domain policies, child
+ * processes only have a read-only mirror to the one in the parent.
+ * For child processes the mirror is updated via messages
+ * and ContentChild will hold the DomainPolicy by calling
+ * ActivateDomainPolicyInternal directly. New consumer to this
+ * function should not be addded.
+ */
+ [noscript] nsIDomainPolicy activateDomainPolicyInternal();
+
+ /**
+ * This function is for internal use only. Every time a child process is spawned, we
+ * must clone any active domain policies in the parent to the new child.
+ */
+ [noscript, notxpcom] void cloneDomainPolicy(in DomainPolicyClonePtr aClone);
+
+ /**
+ * 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"
+%}
diff --git a/caps/nsJSPrincipals.cpp b/caps/nsJSPrincipals.cpp
new file mode 100644
index 000000000..0f3afa14e
--- /dev/null
+++ b/caps/nsJSPrincipals.cpp
@@ -0,0 +1,299 @@
+/* -*- 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 "xpcpublic.h"
+#include "nsString.h"
+#include "nsIObjectOutputStream.h"
+#include "nsIObjectInputStream.h"
+#include "nsJSPrincipals.h"
+#include "plstr.h"
+#include "nsXPIDLString.h"
+#include "nsCOMPtr.h"
+#include "nsIServiceManager.h"
+#include "nsMemory.h"
+#include "nsStringBuffer.h"
+
+#include "mozilla/dom/StructuredCloneTags.h"
+// for mozilla::dom::workers::kJSPrincipalsDebugToken
+#include "mozilla/dom/workers/Workers.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::ipc;
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsJSPrincipals::AddRef()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ NS_PRECONDITION(int32_t(refcount) >= 0, "illegal refcnt");
+ nsrefcnt count = ++refcount;
+ NS_LOG_ADDREF(this, count, "nsJSPrincipals", sizeof(*this));
+ return count;
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsJSPrincipals::Release()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ NS_PRECONDITION(0 != refcount, "dup release");
+ nsrefcnt count = --refcount;
+ NS_LOG_RELEASE(this, count, "nsJSPrincipals");
+ if (count == 0) {
+ delete this;
+ }
+
+ return count;
+}
+
+/* static */ bool
+nsJSPrincipals::Subsume(JSPrincipals *jsprin, JSPrincipals *other)
+{
+ bool result;
+ nsresult rv = nsJSPrincipals::get(jsprin)->Subsumes(nsJSPrincipals::get(other), &result);
+ return NS_SUCCEEDED(rv) && result;
+}
+
+/* static */ void
+nsJSPrincipals::Destroy(JSPrincipals *jsprin)
+{
+ // The JS runtime can call this method during the last GC when
+ // nsScriptSecurityManager is destroyed. So we must not assume here that
+ // the security manager still exists.
+
+ nsJSPrincipals *nsjsprin = nsJSPrincipals::get(jsprin);
+
+ // We need to destroy the nsIPrincipal. We'll do this by adding
+ // to the refcount and calling release
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+ // The refcount logging considers AddRef-to-1 to indicate creation,
+ // so trick it into thinking it's otherwise, but balance the
+ // Release() we do below.
+ nsjsprin->refcount++;
+ nsjsprin->AddRef();
+ nsjsprin->refcount--;
+#else
+ nsjsprin->refcount++;
+#endif
+ nsjsprin->Release();
+}
+
+#ifdef DEBUG
+
+// Defined here so one can do principals->dump() in the debugger
+JS_PUBLIC_API(void)
+JSPrincipals::dump()
+{
+ if (debugToken == nsJSPrincipals::DEBUG_TOKEN) {
+ nsAutoCString str;
+ nsresult rv = static_cast<nsJSPrincipals *>(this)->GetScriptLocation(str);
+ fprintf(stderr, "nsIPrincipal (%p) = %s\n", static_cast<void*>(this),
+ NS_SUCCEEDED(rv) ? str.get() : "(unknown)");
+ } else if (debugToken == dom::workers::kJSPrincipalsDebugToken) {
+ fprintf(stderr, "Web Worker principal singleton (%p)\n", this);
+ } else {
+ fprintf(stderr,
+ "!!! JSPrincipals (%p) is not nsJSPrincipals instance - bad token: "
+ "actual=0x%x expected=0x%x\n",
+ this, unsigned(debugToken), unsigned(nsJSPrincipals::DEBUG_TOKEN));
+ }
+}
+
+#endif
+
+/* static */ bool
+nsJSPrincipals::ReadPrincipals(JSContext* aCx, JSStructuredCloneReader* aReader,
+ JSPrincipals** aOutPrincipals)
+{
+ uint32_t tag;
+ uint32_t unused;
+ if (!JS_ReadUint32Pair(aReader, &tag, &unused)) {
+ return false;
+ }
+
+ if (!(tag == SCTAG_DOM_NULL_PRINCIPAL ||
+ tag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
+ tag == SCTAG_DOM_CONTENT_PRINCIPAL ||
+ tag == SCTAG_DOM_EXPANDED_PRINCIPAL)) {
+ xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+ return false;
+ }
+
+ return ReadKnownPrincipalType(aCx, aReader, tag, aOutPrincipals);
+}
+
+static bool
+ReadSuffixAndSpec(JSStructuredCloneReader* aReader,
+ PrincipalOriginAttributes& aAttrs,
+ nsACString& aSpec)
+{
+ uint32_t suffixLength, specLength;
+ if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) {
+ return false;
+ }
+
+ nsAutoCString suffix;
+ if (!suffix.SetLength(suffixLength, fallible)) {
+ return false;
+ }
+
+ if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) {
+ return false;
+ }
+
+ if (!aAttrs.PopulateFromSuffix(suffix)) {
+ return false;
+ }
+
+ if (!aSpec.SetLength(specLength, fallible)) {
+ return false;
+ }
+
+ if (!JS_ReadBytes(aReader, aSpec.BeginWriting(), specLength)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+ReadPrincipalInfo(JSStructuredCloneReader* aReader,
+ uint32_t aTag,
+ PrincipalInfo& aInfo)
+{
+ if (aTag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
+ aInfo = SystemPrincipalInfo();
+ } else if (aTag == SCTAG_DOM_NULL_PRINCIPAL) {
+ PrincipalOriginAttributes attrs;
+ nsAutoCString dummy;
+ if (!ReadSuffixAndSpec(aReader, attrs, dummy)) {
+ return false;
+ }
+ aInfo = NullPrincipalInfo(attrs);
+ } else if (aTag == SCTAG_DOM_EXPANDED_PRINCIPAL) {
+ uint32_t length, unused;
+ if (!JS_ReadUint32Pair(aReader, &length, &unused)) {
+ return false;
+ }
+
+ ExpandedPrincipalInfo expanded;
+
+ for (uint32_t i = 0; i < length; i++) {
+ uint32_t tag;
+ if (!JS_ReadUint32Pair(aReader, &tag, &unused)) {
+ return false;
+ }
+
+ PrincipalInfo sub;
+ if (!ReadPrincipalInfo(aReader, tag, sub)) {
+ return false;
+ }
+ expanded.whitelist().AppendElement(sub);
+ }
+
+ aInfo = expanded;
+ } else if (aTag == SCTAG_DOM_CONTENT_PRINCIPAL) {
+ PrincipalOriginAttributes attrs;
+ nsAutoCString spec;
+ if (!ReadSuffixAndSpec(aReader, attrs, spec)) {
+ return false;
+ }
+
+ aInfo = ContentPrincipalInfo(attrs, void_t(), spec);
+ } else {
+ MOZ_CRASH("unexpected principal structured clone tag");
+ }
+
+ return true;
+}
+
+/* static */ bool
+nsJSPrincipals::ReadKnownPrincipalType(JSContext* aCx,
+ JSStructuredCloneReader* aReader,
+ uint32_t aTag,
+ JSPrincipals** aOutPrincipals)
+{
+ MOZ_ASSERT(aTag == SCTAG_DOM_NULL_PRINCIPAL ||
+ aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
+ aTag == SCTAG_DOM_CONTENT_PRINCIPAL ||
+ aTag == SCTAG_DOM_EXPANDED_PRINCIPAL);
+
+ if (NS_WARN_IF(!NS_IsMainThread())) {
+ xpc::Throw(aCx, NS_ERROR_UNCATCHABLE_EXCEPTION);
+ return false;
+ }
+
+ PrincipalInfo info;
+ if (!ReadPrincipalInfo(aReader, aTag, info)) {
+ return false;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIPrincipal> prin = PrincipalInfoToPrincipal(info, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+ return false;
+ }
+
+ *aOutPrincipals = get(prin.forget().take());
+ return true;
+}
+
+static bool
+WriteSuffixAndSpec(JSStructuredCloneWriter* aWriter,
+ const PrincipalOriginAttributes& aAttrs,
+ const nsCString& aSpec)
+{
+ nsAutoCString suffix;
+ aAttrs.CreateSuffix(suffix);
+
+ return JS_WriteUint32Pair(aWriter, suffix.Length(), aSpec.Length()) &&
+ JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
+ JS_WriteBytes(aWriter, aSpec.get(), aSpec.Length());
+}
+
+static bool
+WritePrincipalInfo(JSStructuredCloneWriter* aWriter, const PrincipalInfo& aInfo)
+{
+ if (aInfo.type() == PrincipalInfo::TNullPrincipalInfo) {
+ const NullPrincipalInfo& nullInfo = aInfo;
+ return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NULL_PRINCIPAL, 0) &&
+ WriteSuffixAndSpec(aWriter, nullInfo.attrs(), EmptyCString());
+ }
+ if (aInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
+ return JS_WriteUint32Pair(aWriter, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
+ }
+ if (aInfo.type() == PrincipalInfo::TExpandedPrincipalInfo) {
+ const ExpandedPrincipalInfo& expanded = aInfo;
+ if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_EXPANDED_PRINCIPAL, 0) ||
+ !JS_WriteUint32Pair(aWriter, expanded.whitelist().Length(), 0)) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < expanded.whitelist().Length(); i++) {
+ if (!WritePrincipalInfo(aWriter, expanded.whitelist()[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ MOZ_ASSERT(aInfo.type() == PrincipalInfo::TContentPrincipalInfo);
+ const ContentPrincipalInfo& cInfo = aInfo;
+ return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) &&
+ WriteSuffixAndSpec(aWriter, cInfo.attrs(), cInfo.spec());
+}
+
+bool
+nsJSPrincipals::write(JSContext* aCx, JSStructuredCloneWriter* aWriter)
+{
+ PrincipalInfo info;
+ if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(this, &info)))) {
+ xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+ return false;
+ }
+
+ return WritePrincipalInfo(aWriter, info);
+}
diff --git a/caps/nsJSPrincipals.h b/caps/nsJSPrincipals.h
new file mode 100644
index 000000000..5499345da
--- /dev/null
+++ b/caps/nsJSPrincipals.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+/* describes principals by their orginating uris*/
+
+#ifndef nsJSPrincipals_h__
+#define nsJSPrincipals_h__
+#include "jsapi.h"
+#include "nsIPrincipal.h"
+
+class nsJSPrincipals : public nsIPrincipal, public JSPrincipals
+{
+public:
+ /* SpiderMonkey security callbacks. */
+ static bool Subsume(JSPrincipals *jsprin, JSPrincipals *other);
+ static void Destroy(JSPrincipals *jsprin);
+
+ /* JSReadPrincipalsOp for nsJSPrincipals */
+ static bool ReadPrincipals(JSContext* aCx, JSStructuredCloneReader* aReader,
+ JSPrincipals** aOutPrincipals);
+
+ static bool ReadKnownPrincipalType(JSContext* aCx,
+ JSStructuredCloneReader* aReader,
+ uint32_t aTag,
+ JSPrincipals** aOutPrincipals);
+
+ bool write(JSContext* aCx, JSStructuredCloneWriter* aWriter) final;
+
+ /*
+ * Get a weak reference to nsIPrincipal associated with the given JS
+ * principal, and vice-versa.
+ */
+ static nsJSPrincipals* get(JSPrincipals *principals) {
+ nsJSPrincipals *self = static_cast<nsJSPrincipals *>(principals);
+ MOZ_ASSERT_IF(self, self->debugToken == DEBUG_TOKEN);
+ return self;
+ }
+ static nsJSPrincipals* get(nsIPrincipal *principal) {
+ nsJSPrincipals *self = static_cast<nsJSPrincipals *>(principal);
+ MOZ_ASSERT_IF(self, self->debugToken == DEBUG_TOKEN);
+ return self;
+ }
+
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override;
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override;
+
+ nsJSPrincipals() {
+ refcount = 0;
+ setDebugToken(DEBUG_TOKEN);
+ }
+
+ /**
+ * Return a string that can be used as JS script filename in error reports.
+ */
+ virtual nsresult GetScriptLocation(nsACString &aStr) = 0;
+ static const uint32_t DEBUG_TOKEN = 0x0bf41760;
+
+protected:
+ virtual ~nsJSPrincipals() {
+ setDebugToken(0);
+ }
+
+};
+
+#endif /* nsJSPrincipals_h__ */
diff --git a/caps/nsNullPrincipal.cpp b/caps/nsNullPrincipal.cpp
new file mode 100644
index 000000000..6ebf0f129
--- /dev/null
+++ b/caps/nsNullPrincipal.cpp
@@ -0,0 +1,193 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 sts=2 ts=2 et 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/. */
+
+/**
+ * This is the principal that has no rights and can't be accessed by
+ * anything other than itself and chrome; null principals are not
+ * same-origin with anything but themselves.
+ */
+
+#include "mozilla/ArrayUtils.h"
+
+#include "nsDocShell.h"
+#include "nsNullPrincipal.h"
+#include "nsNullPrincipalURI.h"
+#include "nsMemory.h"
+#include "nsIURIWithPrincipal.h"
+#include "nsIClassInfoImpl.h"
+#include "nsNetCID.h"
+#include "nsError.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsPrincipal.h"
+#include "nsScriptSecurityManager.h"
+#include "pratom.h"
+
+using namespace mozilla;
+
+NS_IMPL_CLASSINFO(nsNullPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
+ NS_NULLPRINCIPAL_CID)
+NS_IMPL_QUERY_INTERFACE_CI(nsNullPrincipal,
+ nsIPrincipal,
+ nsISerializable)
+NS_IMPL_CI_INTERFACE_GETTER(nsNullPrincipal,
+ nsIPrincipal,
+ nsISerializable)
+
+/* static */ already_AddRefed<nsNullPrincipal>
+nsNullPrincipal::CreateWithInheritedAttributes(nsIPrincipal* aInheritFrom)
+{
+ RefPtr<nsNullPrincipal> nullPrin = new nsNullPrincipal();
+ nsresult rv = nullPrin->Init(Cast(aInheritFrom)->OriginAttributesRef());
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+ return nullPrin.forget();
+}
+
+/* static */ already_AddRefed<nsNullPrincipal>
+nsNullPrincipal::CreateWithInheritedAttributes(nsIDocShell* aDocShell)
+{
+ PrincipalOriginAttributes attrs;
+ attrs.InheritFromDocShellToDoc(nsDocShell::Cast(aDocShell)->GetOriginAttributes(), nullptr);
+
+ RefPtr<nsNullPrincipal> nullPrin = new nsNullPrincipal();
+ nsresult rv = nullPrin->Init(attrs);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+ return nullPrin.forget();
+}
+
+/* static */ already_AddRefed<nsNullPrincipal>
+nsNullPrincipal::Create(const PrincipalOriginAttributes& aOriginAttributes)
+{
+ RefPtr<nsNullPrincipal> nullPrin = new nsNullPrincipal();
+ nsresult rv = nullPrin->Init(aOriginAttributes);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+
+ return nullPrin.forget();
+}
+
+nsresult
+nsNullPrincipal::Init(const PrincipalOriginAttributes& aOriginAttributes)
+{
+ mOriginAttributes = aOriginAttributes;
+
+ mURI = nsNullPrincipalURI::Create();
+ NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_AVAILABLE);
+
+ return NS_OK;
+}
+
+nsresult
+nsNullPrincipal::GetScriptLocation(nsACString &aStr)
+{
+ return mURI->GetSpec(aStr);
+}
+
+/**
+ * nsIPrincipal implementation
+ */
+
+NS_IMETHODIMP
+nsNullPrincipal::GetHashValue(uint32_t *aResult)
+{
+ *aResult = (NS_PTR_TO_INT32(this) >> 2);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNullPrincipal::SetCsp(nsIContentSecurityPolicy* aCsp) {
+ // Never destroy an existing CSP on the principal.
+ // This method should only be called in rare cases.
+
+ MOZ_ASSERT(!mCSP, "do not destroy an existing CSP");
+ if (mCSP) {
+ return NS_ERROR_ALREADY_INITIALIZED;
+ }
+
+ mCSP = aCsp;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNullPrincipal::GetURI(nsIURI** aURI)
+{
+ return NS_EnsureSafeToReturn(mURI, aURI);
+}
+
+NS_IMETHODIMP
+nsNullPrincipal::GetDomain(nsIURI** aDomain)
+{
+ return NS_EnsureSafeToReturn(mURI, aDomain);
+}
+
+NS_IMETHODIMP
+nsNullPrincipal::SetDomain(nsIURI* aDomain)
+{
+ // I think the right thing to do here is to just throw... Silently failing
+ // seems counterproductive.
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsresult
+nsNullPrincipal::GetOriginInternal(nsACString& aOrigin)
+{
+ return mURI->GetSpec(aOrigin);
+}
+
+bool
+nsNullPrincipal::MayLoadInternal(nsIURI* aURI)
+{
+ // Also allow the load if we are the principal of the URI being checked.
+ nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(aURI);
+ if (uriPrinc) {
+ nsCOMPtr<nsIPrincipal> principal;
+ uriPrinc->GetPrincipal(getter_AddRefs(principal));
+
+ if (principal == this) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+NS_IMETHODIMP
+nsNullPrincipal::GetBaseDomain(nsACString& aBaseDomain)
+{
+ // For a null principal, we use our unique uuid as the base domain.
+ return mURI->GetPath(aBaseDomain);
+}
+
+/**
+ * nsISerializable implementation
+ */
+NS_IMETHODIMP
+nsNullPrincipal::Read(nsIObjectInputStream* aStream)
+{
+ // Note - nsNullPrincipal use NS_GENERIC_FACTORY_CONSTRUCTOR_INIT, which means
+ // that the Init() method has already been invoked by the time we deserialize.
+ // This is in contrast to nsPrincipal, which uses NS_GENERIC_FACTORY_CONSTRUCTOR,
+ // in which case ::Read needs to invoke Init().
+ nsAutoCString suffix;
+ nsresult rv = aStream->ReadCString(suffix);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool ok = mOriginAttributes.PopulateFromSuffix(suffix);
+ NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNullPrincipal::Write(nsIObjectOutputStream* aStream)
+{
+ nsAutoCString suffix;
+ OriginAttributesRef().CreateSuffix(suffix);
+
+ nsresult rv = aStream->WriteStringZ(suffix.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
diff --git a/caps/nsNullPrincipal.h b/caps/nsNullPrincipal.h
new file mode 100644
index 000000000..b29474773
--- /dev/null
+++ b/caps/nsNullPrincipal.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/**
+ * This is the principal that has no rights and can't be accessed by
+ * anything other than itself and chrome; null principals are not
+ * same-origin with anything but themselves.
+ */
+
+#ifndef nsNullPrincipal_h__
+#define nsNullPrincipal_h__
+
+#include "nsIPrincipal.h"
+#include "nsJSPrincipals.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsCOMPtr.h"
+#include "nsIContentSecurityPolicy.h"
+
+#include "mozilla/BasePrincipal.h"
+
+class nsIDocShell;
+class nsIURI;
+
+#define NS_NULLPRINCIPAL_CID \
+{ 0xbd066e5f, 0x146f, 0x4472, \
+ { 0x83, 0x31, 0x7b, 0xfd, 0x05, 0xb1, 0xed, 0x90 } }
+#define NS_NULLPRINCIPAL_CONTRACTID "@mozilla.org/nullprincipal;1"
+
+#define NS_NULLPRINCIPAL_SCHEME "moz-nullprincipal"
+
+class nsNullPrincipal final : public mozilla::BasePrincipal
+{
+public:
+ // This should only be used by deserialization, and the factory constructor.
+ // Other consumers should use the Create and CreateWithInheritedAttributes
+ // methods.
+ nsNullPrincipal() {}
+
+ NS_DECL_NSISERIALIZABLE
+
+ NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
+ NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
+ NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
+ NS_IMETHOD GetURI(nsIURI** aURI) override;
+ NS_IMETHOD GetDomain(nsIURI** aDomain) override;
+ NS_IMETHOD SetDomain(nsIURI* aDomain) override;
+ NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
+ nsresult GetOriginInternal(nsACString& aOrigin) override;
+
+ static already_AddRefed<nsNullPrincipal> CreateWithInheritedAttributes(nsIPrincipal* aInheritFrom);
+
+ static already_AddRefed<nsNullPrincipal> CreateWithInheritedAttributes(nsIDocShell* aDocShell);
+
+ static already_AddRefed<nsNullPrincipal>
+ Create(const mozilla::PrincipalOriginAttributes& aOriginAttributes = mozilla::PrincipalOriginAttributes());
+
+ nsresult Init(const mozilla::PrincipalOriginAttributes& aOriginAttributes = mozilla::PrincipalOriginAttributes());
+
+ virtual nsresult GetScriptLocation(nsACString &aStr) override;
+
+ PrincipalKind Kind() override { return eNullPrincipal; }
+
+ protected:
+ virtual ~nsNullPrincipal() {}
+
+ bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override
+ {
+ return aOther == this;
+ }
+
+ bool MayLoadInternal(nsIURI* aURI) override;
+
+ nsCOMPtr<nsIURI> mURI;
+};
+
+#endif // nsNullPrincipal_h__
diff --git a/caps/nsNullPrincipalURI.cpp b/caps/nsNullPrincipalURI.cpp
new file mode 100644
index 000000000..891a29bd8
--- /dev/null
+++ b/caps/nsNullPrincipalURI.cpp
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2 expandtab
+ * 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 "nsNullPrincipalURI.h"
+
+#include "mozilla/DebugOnly.h"
+#include "mozilla/MemoryReporting.h"
+
+#include "mozilla/ipc/URIParams.h"
+
+#include "nsEscape.h"
+#include "nsCRT.h"
+#include "nsIUUIDGenerator.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//// nsNullPrincipalURI
+
+nsNullPrincipalURI::nsNullPrincipalURI()
+ : mPath(mPathBytes, ArrayLength(mPathBytes), ArrayLength(mPathBytes) - 1)
+{
+}
+
+nsNullPrincipalURI::nsNullPrincipalURI(const nsNullPrincipalURI& aOther)
+ : mPath(mPathBytes, ArrayLength(mPathBytes), ArrayLength(mPathBytes) - 1)
+{
+ mPath.Assign(aOther.mPath);
+}
+
+nsresult
+nsNullPrincipalURI::Init()
+{
+ // FIXME: bug 327161 -- make sure the uuid generator is reseeding-resistant.
+ nsCOMPtr<nsIUUIDGenerator> uuidgen = services::GetUUIDGenerator();
+ NS_ENSURE_TRUE(uuidgen, NS_ERROR_NOT_AVAILABLE);
+
+ nsID id;
+ nsresult rv = uuidgen->GenerateUUIDInPlace(&id);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ MOZ_ASSERT(mPathBytes == mPath.BeginWriting());
+
+ id.ToProvidedString(mPathBytes);
+
+ MOZ_ASSERT(mPath.Length() == NSID_LENGTH - 1);
+ MOZ_ASSERT(strlen(mPath.get()) == NSID_LENGTH - 1);
+
+ return NS_OK;
+}
+
+/* static */
+already_AddRefed<nsNullPrincipalURI>
+nsNullPrincipalURI::Create()
+{
+ RefPtr<nsNullPrincipalURI> uri = new nsNullPrincipalURI();
+ nsresult rv = uri->Init();
+ NS_ENSURE_SUCCESS(rv, nullptr);
+ return uri.forget();
+}
+
+static NS_DEFINE_CID(kNullPrincipalURIImplementationCID,
+ NS_NULLPRINCIPALURI_IMPLEMENTATION_CID);
+
+NS_IMPL_ADDREF(nsNullPrincipalURI)
+NS_IMPL_RELEASE(nsNullPrincipalURI)
+
+NS_INTERFACE_MAP_BEGIN(nsNullPrincipalURI)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURI)
+ if (aIID.Equals(kNullPrincipalURIImplementationCID))
+ foundInterface = static_cast<nsIURI *>(this);
+ else
+ NS_INTERFACE_MAP_ENTRY(nsIURI)
+ NS_INTERFACE_MAP_ENTRY(nsISizeOf)
+ NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableURI)
+NS_INTERFACE_MAP_END
+
+////////////////////////////////////////////////////////////////////////////////
+//// nsIURI
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetAsciiHost(nsACString &_host)
+{
+ _host.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetAsciiHostPort(nsACString &_hostport)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetAsciiSpec(nsACString &_spec)
+{
+ nsAutoCString buffer;
+ // Ignore the return value -- nsNullPrincipalURI::GetSpec() is infallible.
+ Unused << GetSpec(buffer);
+ // This uses the infallible version of |NS_EscapeURL| as |GetSpec| is
+ // already infallible.
+ NS_EscapeURL(buffer, esc_OnlyNonASCII | esc_AlwaysCopy, _spec);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetHost(nsACString &_host)
+{
+ _host.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::SetHost(const nsACString &aHost)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetHostPort(nsACString &_host)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::SetHostPort(const nsACString &aHost)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::SetHostAndPort(const nsACString &aHost)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetOriginCharset(nsACString &_charset)
+{
+ _charset.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetPassword(nsACString &_password)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::SetPassword(const nsACString &aPassword)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetPath(nsACString &_path)
+{
+ _path = mPath;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::SetPath(const nsACString &aPath)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetRef(nsACString &_ref)
+{
+ _ref.Truncate();
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::SetRef(const nsACString &aRef)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetPrePath(nsACString &_prePath)
+{
+ _prePath = NS_LITERAL_CSTRING(NS_NULLPRINCIPAL_SCHEME ":");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetPort(int32_t *_port)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::SetPort(int32_t aPort)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetScheme(nsACString &_scheme)
+{
+ _scheme = NS_LITERAL_CSTRING(NS_NULLPRINCIPAL_SCHEME);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::SetScheme(const nsACString &aScheme)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetSpec(nsACString &_spec)
+{
+ _spec = NS_LITERAL_CSTRING(NS_NULLPRINCIPAL_SCHEME ":") + mPath;
+ return NS_OK;
+}
+
+// result may contain unescaped UTF-8 characters
+NS_IMETHODIMP
+nsNullPrincipalURI::GetSpecIgnoringRef(nsACString &result)
+{
+ return GetSpec(result);
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetHasRef(bool *result)
+{
+ *result = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::SetSpec(const nsACString &aSpec)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetUsername(nsACString &_username)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::SetUsername(const nsACString &aUsername)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::GetUserPass(nsACString &_userPass)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::SetUserPass(const nsACString &aUserPass)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::Clone(nsIURI **_newURI)
+{
+ nsCOMPtr<nsIURI> uri = new nsNullPrincipalURI(*this);
+ uri.forget(_newURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::CloneIgnoringRef(nsIURI **_newURI)
+{
+ // GetRef/SetRef not supported by nsNullPrincipalURI, so
+ // CloneIgnoringRef() is the same as Clone().
+ return Clone(_newURI);
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::CloneWithNewRef(const nsACString& newRef, nsIURI **_newURI)
+{
+ // GetRef/SetRef not supported by nsNullPrincipalURI, so
+ // CloneWithNewRef() is the same as Clone().
+ return Clone(_newURI);
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::Equals(nsIURI *aOther, bool *_equals)
+{
+ *_equals = false;
+ RefPtr<nsNullPrincipalURI> otherURI;
+ nsresult rv = aOther->QueryInterface(kNullPrincipalURIImplementationCID,
+ getter_AddRefs(otherURI));
+ if (NS_SUCCEEDED(rv)) {
+ *_equals = mPath == otherURI->mPath;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::EqualsExceptRef(nsIURI *aOther, bool *_equals)
+{
+ // GetRef/SetRef not supported by nsNullPrincipalURI, so
+ // EqualsExceptRef() is the same as Equals().
+ return Equals(aOther, _equals);
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::Resolve(const nsACString &aRelativePath,
+ nsACString &_resolvedURI)
+{
+ _resolvedURI = aRelativePath;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNullPrincipalURI::SchemeIs(const char *aScheme, bool *_schemeIs)
+{
+ *_schemeIs = (0 == nsCRT::strcasecmp(NS_NULLPRINCIPAL_SCHEME, aScheme));
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// nsIIPCSerializableURI
+
+void
+nsNullPrincipalURI::Serialize(mozilla::ipc::URIParams &aParams)
+{
+ aParams = mozilla::ipc::NullPrincipalURIParams();
+}
+
+bool
+nsNullPrincipalURI::Deserialize(const mozilla::ipc::URIParams &aParams)
+{
+ if (aParams.type() != mozilla::ipc::URIParams::TNullPrincipalURIParams) {
+ MOZ_ASSERT_UNREACHABLE("unexpected URIParams type");
+ return false;
+ }
+
+ nsresult rv = Init();
+ NS_ENSURE_SUCCESS(rv, false);
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// nsISizeOf
+
+size_t
+nsNullPrincipalURI::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ return mPath.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+}
+
+size_t
+nsNullPrincipalURI::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
diff --git a/caps/nsNullPrincipalURI.h b/caps/nsNullPrincipalURI.h
new file mode 100644
index 000000000..63d7221c2
--- /dev/null
+++ b/caps/nsNullPrincipalURI.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2 expandtab
+ * 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/. */
+
+/**
+ * This wraps nsSimpleURI so that all calls to it are done on the main thread.
+ */
+
+#ifndef __nsNullPrincipalURI_h__
+#define __nsNullPrincipalURI_h__
+
+#include "nsIURI.h"
+#include "nsISizeOf.h"
+#include "nsString.h"
+#include "mozilla/Attributes.h"
+#include "nsIIPCSerializableURI.h"
+#include "mozilla/MemoryReporting.h"
+#include "nsNullPrincipal.h"
+#include "nsID.h"
+
+// {51fcd543-3b52-41f7-b91b-6b54102236e6}
+#define NS_NULLPRINCIPALURI_IMPLEMENTATION_CID \
+ {0x51fcd543, 0x3b52, 0x41f7, \
+ {0xb9, 0x1b, 0x6b, 0x54, 0x10, 0x22, 0x36, 0xe6} }
+
+class nsNullPrincipalURI final : public nsIURI
+ , public nsISizeOf
+ , public nsIIPCSerializableURI
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURI
+ NS_DECL_NSIIPCSERIALIZABLEURI
+
+ // nsISizeOf
+ virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
+ virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
+
+ // NB: This constructor exists only for deserialization. Everyone
+ // else should call Create.
+ nsNullPrincipalURI();
+
+ // Returns null on failure.
+ static already_AddRefed<nsNullPrincipalURI> Create();
+
+private:
+ nsNullPrincipalURI(const nsNullPrincipalURI& aOther);
+
+ ~nsNullPrincipalURI() {}
+
+ nsresult Init();
+
+ char mPathBytes[NSID_LENGTH];
+ nsFixedCString mPath;
+};
+
+#endif // __nsNullPrincipalURI_h__
diff --git a/caps/nsPrincipal.cpp b/caps/nsPrincipal.cpp
new file mode 100644
index 000000000..4cb472369
--- /dev/null
+++ b/caps/nsPrincipal.cpp
@@ -0,0 +1,854 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et 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 "nsPrincipal.h"
+
+#include "mozIThirdPartyUtil.h"
+#include "nscore.h"
+#include "nsScriptSecurityManager.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "pratom.h"
+#include "nsIURI.h"
+#include "nsIURL.h"
+#include "nsIStandardURL.h"
+#include "nsIURIWithPrincipal.h"
+#include "nsJSPrincipals.h"
+#include "nsIEffectiveTLDService.h"
+#include "nsIClassInfoImpl.h"
+#include "nsIProtocolHandler.h"
+#include "nsError.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsNetCID.h"
+#include "jswrapper.h"
+
+#include "mozilla/dom/nsCSPContext.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/HashFunctions.h"
+
+#include "nsIAppsService.h"
+
+using namespace mozilla;
+
+static bool gIsWhitelistingTestDomains = false;
+static bool gCodeBasePrincipalSupport = false;
+
+static bool URIIsImmutable(nsIURI* aURI)
+{
+ nsCOMPtr<nsIMutable> mutableObj(do_QueryInterface(aURI));
+ bool isMutable;
+ return
+ mutableObj &&
+ NS_SUCCEEDED(mutableObj->GetMutable(&isMutable)) &&
+ !isMutable;
+}
+
+NS_IMPL_CLASSINFO(nsPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
+ NS_PRINCIPAL_CID)
+NS_IMPL_QUERY_INTERFACE_CI(nsPrincipal,
+ nsIPrincipal,
+ nsISerializable)
+NS_IMPL_CI_INTERFACE_GETTER(nsPrincipal,
+ nsIPrincipal,
+ nsISerializable)
+
+// Called at startup:
+/* static */ void
+nsPrincipal::InitializeStatics()
+{
+ Preferences::AddBoolVarCache(
+ &gIsWhitelistingTestDomains,
+ "layout.css.unprefixing-service.include-test-domains");
+
+ Preferences::AddBoolVarCache(&gCodeBasePrincipalSupport,
+ "signed.applets.codebase_principal_support",
+ false);
+}
+
+nsPrincipal::nsPrincipal()
+ : mCodebaseImmutable(false)
+ , mDomainImmutable(false)
+ , mInitialized(false)
+{ }
+
+nsPrincipal::~nsPrincipal()
+{
+ // let's clear the principal within the csp to avoid a tangling pointer
+ if (mCSP) {
+ static_cast<nsCSPContext*>(mCSP.get())->clearLoadingPrincipal();
+ }
+}
+
+nsresult
+nsPrincipal::Init(nsIURI *aCodebase, const PrincipalOriginAttributes& aOriginAttributes)
+{
+ NS_ENSURE_STATE(!mInitialized);
+ NS_ENSURE_ARG(aCodebase);
+
+ mInitialized = true;
+
+ mCodebase = NS_TryToMakeImmutable(aCodebase);
+ mCodebaseImmutable = URIIsImmutable(mCodebase);
+ mOriginAttributes = aOriginAttributes;
+
+ return NS_OK;
+}
+
+nsresult
+nsPrincipal::GetScriptLocation(nsACString &aStr)
+{
+ return mCodebase->GetSpec(aStr);
+}
+
+/* static */ nsresult
+nsPrincipal::GetOriginForURI(nsIURI* aURI, nsACString& aOrigin)
+{
+ if (!aURI) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIURI> origin = NS_GetInnermostURI(aURI);
+ if (!origin) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+// NB: This is only compiled for Thunderbird/Suite.
+#if IS_ORIGIN_IS_FULL_SPEC_DEFINED
+ bool fullSpec = false;
+ rv = NS_URIChainHasFlags(origin, nsIProtocolHandler::ORIGIN_IS_FULL_SPEC, &fullSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (fullSpec) {
+ return origin->GetAsciiSpec(aOrigin);
+ }
+#endif
+
+ nsAutoCString hostPort;
+
+ // chrome: URLs don't have a meaningful origin, so make
+ // sure we just get the full spec for them.
+ // XXX this should be removed in favor of the solution in
+ // bug 160042.
+ bool isChrome;
+ rv = origin->SchemeIs("chrome", &isChrome);
+ if (NS_SUCCEEDED(rv) && !isChrome) {
+ rv = origin->GetAsciiHostPort(hostPort);
+ // Some implementations return an empty string, treat it as no support
+ // for asciiHost by that implementation.
+ if (hostPort.IsEmpty()) {
+ rv = NS_ERROR_FAILURE;
+ }
+ }
+
+ // We want the invariant that prinA.origin == prinB.origin i.f.f.
+ // prinA.equals(prinB). However, this requires that we impose certain constraints
+ // on the behavior and origin semantics of principals, and in particular, forbid
+ // creating origin strings for principals whose equality constraints are not
+ // expressible as strings (i.e. object equality). Moreover, we want to forbid URIs
+ // containing the magic "^" we use as a separating character for origin
+ // attributes.
+ //
+ // These constraints can generally be achieved by restricting .origin to
+ // nsIStandardURL-based URIs, but there are a few other URI schemes that we need
+ // to handle.
+ bool isBehaved;
+ if ((NS_SUCCEEDED(origin->SchemeIs("about", &isBehaved)) && isBehaved) ||
+ (NS_SUCCEEDED(origin->SchemeIs("moz-safe-about", &isBehaved)) && isBehaved) ||
+ (NS_SUCCEEDED(origin->SchemeIs("indexeddb", &isBehaved)) && isBehaved)) {
+ rv = origin->GetAsciiSpec(aOrigin);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // These URIs could technically contain a '^', but they never should.
+ if (NS_WARN_IF(aOrigin.FindChar('^', 0) != -1)) {
+ aOrigin.Truncate();
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+ }
+
+ if (NS_SUCCEEDED(rv) && !isChrome) {
+ rv = origin->GetScheme(aOrigin);
+ NS_ENSURE_SUCCESS(rv, rv);
+ aOrigin.AppendLiteral("://");
+ aOrigin.Append(hostPort);
+ }
+ else {
+ // If we reached this branch, we can only create an origin if we have a nsIStandardURL.
+ // So, we query to a nsIStandardURL, and fail if we aren't an instance of an nsIStandardURL
+ // nsIStandardURLs have the good property of escaping the '^' character in their specs,
+ // which means that we can be sure that the caret character (which is reserved for delimiting
+ // the end of the spec, and the beginning of the origin attributes) is not present in the
+ // origin string
+ nsCOMPtr<nsIStandardURL> standardURL = do_QueryInterface(origin);
+ NS_ENSURE_TRUE(standardURL, NS_ERROR_FAILURE);
+ rv = origin->GetAsciiSpec(aOrigin);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsPrincipal::GetOriginInternal(nsACString& aOrigin)
+{
+ return GetOriginForURI(mCodebase, aOrigin);
+}
+
+bool
+nsPrincipal::SubsumesInternal(nsIPrincipal* aOther,
+ BasePrincipal::DocumentDomainConsideration aConsideration)
+{
+ MOZ_ASSERT(aOther);
+
+ // For nsPrincipal, Subsumes is equivalent to Equals.
+ if (aOther == this) {
+ return true;
+ }
+
+ // If either the subject or the object has changed its principal by
+ // explicitly setting document.domain then the other must also have
+ // done so in order to be considered the same origin. This prevents
+ // DNS spoofing based on document.domain (154930)
+ nsresult rv;
+ if (aConsideration == ConsiderDocumentDomain) {
+ // Get .domain on each principal.
+ nsCOMPtr<nsIURI> thisDomain, otherDomain;
+ GetDomain(getter_AddRefs(thisDomain));
+ aOther->GetDomain(getter_AddRefs(otherDomain));
+
+ // If either has .domain set, we have equality i.f.f. the domains match.
+ // Otherwise, we fall through to the non-document-domain-considering case.
+ if (thisDomain || otherDomain) {
+ return nsScriptSecurityManager::SecurityCompareURIs(thisDomain, otherDomain);
+ }
+ }
+
+ nsCOMPtr<nsIURI> otherURI;
+ rv = aOther->GetURI(getter_AddRefs(otherURI));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ // Compare codebases.
+ return nsScriptSecurityManager::SecurityCompareURIs(mCodebase, otherURI);
+}
+
+NS_IMETHODIMP
+nsPrincipal::GetURI(nsIURI** aURI)
+{
+ if (mCodebaseImmutable) {
+ NS_ADDREF(*aURI = mCodebase);
+ return NS_OK;
+ }
+
+ if (!mCodebase) {
+ *aURI = nullptr;
+ return NS_OK;
+ }
+
+ return NS_EnsureSafeToReturn(mCodebase, aURI);
+}
+
+bool
+nsPrincipal::MayLoadInternal(nsIURI* aURI)
+{
+ // See if aURI is something like a Blob URI that is actually associated with
+ // a principal.
+ nsCOMPtr<nsIURIWithPrincipal> uriWithPrin = do_QueryInterface(aURI);
+ nsCOMPtr<nsIPrincipal> uriPrin;
+ if (uriWithPrin) {
+ uriWithPrin->GetPrincipal(getter_AddRefs(uriPrin));
+ }
+ if (uriPrin) {
+ return nsIPrincipal::Subsumes(uriPrin);
+ }
+
+ // If this principal is associated with an addon, check whether that addon
+ // has been given permission to load from this domain.
+ if (AddonAllowsLoad(aURI)) {
+ return true;
+ }
+
+ if (nsScriptSecurityManager::SecurityCompareURIs(mCodebase, aURI)) {
+ return true;
+ }
+
+ // If strict file origin policy is in effect, local files will always fail
+ // SecurityCompareURIs unless they are identical. Explicitly check file origin
+ // policy, in that case.
+ if (nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
+ NS_URIIsLocalFile(aURI) &&
+ NS_RelaxStrictFileOriginPolicy(aURI, mCodebase)) {
+ return true;
+ }
+
+ return false;
+}
+
+void
+nsPrincipal::SetURI(nsIURI* aURI)
+{
+ mCodebase = NS_TryToMakeImmutable(aURI);
+ mCodebaseImmutable = URIIsImmutable(mCodebase);
+}
+
+NS_IMETHODIMP
+nsPrincipal::GetHashValue(uint32_t* aValue)
+{
+ NS_PRECONDITION(mCodebase, "Need a codebase");
+
+ *aValue = nsScriptSecurityManager::HashPrincipalByOrigin(this);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPrincipal::GetDomain(nsIURI** aDomain)
+{
+ if (!mDomain) {
+ *aDomain = nullptr;
+ return NS_OK;
+ }
+
+ if (mDomainImmutable) {
+ NS_ADDREF(*aDomain = mDomain);
+ return NS_OK;
+ }
+
+ return NS_EnsureSafeToReturn(mDomain, aDomain);
+}
+
+NS_IMETHODIMP
+nsPrincipal::SetDomain(nsIURI* aDomain)
+{
+ mDomain = NS_TryToMakeImmutable(aDomain);
+ mDomainImmutable = URIIsImmutable(mDomain);
+
+ // Recompute all wrappers between compartments using this principal and other
+ // non-chrome compartments.
+ AutoSafeJSContext cx;
+ JSPrincipals *principals = nsJSPrincipals::get(static_cast<nsIPrincipal*>(this));
+ bool success = js::RecomputeWrappers(cx, js::ContentCompartmentsOnly(),
+ js::CompartmentsWithPrincipals(principals));
+ NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
+ success = js::RecomputeWrappers(cx, js::CompartmentsWithPrincipals(principals),
+ js::ContentCompartmentsOnly());
+ NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPrincipal::GetBaseDomain(nsACString& aBaseDomain)
+{
+ // For a file URI, we return the file path.
+ if (NS_URIIsLocalFile(mCodebase)) {
+ nsCOMPtr<nsIURL> url = do_QueryInterface(mCodebase);
+
+ if (url) {
+ return url->GetFilePath(aBaseDomain);
+ }
+ }
+
+ bool hasNoRelativeFlag;
+ nsresult rv = NS_URIChainHasFlags(mCodebase,
+ nsIProtocolHandler::URI_NORELATIVE,
+ &hasNoRelativeFlag);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (hasNoRelativeFlag) {
+ return mCodebase->GetSpec(aBaseDomain);
+ }
+
+ // For everything else, we ask the TLD service via
+ // the ThirdPartyUtil.
+ nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
+ do_GetService(THIRDPARTYUTIL_CONTRACTID);
+ if (thirdPartyUtil) {
+ return thirdPartyUtil->GetBaseDomain(mCodebase, aBaseDomain);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPrincipal::Read(nsIObjectInputStream* aStream)
+{
+ nsCOMPtr<nsISupports> supports;
+ nsCOMPtr<nsIURI> codebase;
+ nsresult rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ codebase = do_QueryInterface(supports);
+
+ nsCOMPtr<nsIURI> domain;
+ rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ domain = do_QueryInterface(supports);
+
+ nsAutoCString suffix;
+ rv = aStream->ReadCString(suffix);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PrincipalOriginAttributes attrs;
+ bool ok = attrs.PopulateFromSuffix(suffix);
+ NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+
+ rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = Init(codebase, attrs);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mCSP = do_QueryInterface(supports, &rv);
+ // make sure setRequestContext is called after Init(),
+ // to make sure the principals URI been initalized.
+ if (mCSP) {
+ mCSP->SetRequestContext(nullptr, this);
+ }
+
+ SetDomain(domain);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPrincipal::Write(nsIObjectOutputStream* aStream)
+{
+ NS_ENSURE_STATE(mCodebase);
+ nsresult rv = NS_WriteOptionalCompoundObject(aStream, mCodebase, NS_GET_IID(nsIURI),
+ true);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = NS_WriteOptionalCompoundObject(aStream, mDomain, NS_GET_IID(nsIURI),
+ true);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsAutoCString suffix;
+ OriginAttributesRef().CreateSuffix(suffix);
+
+ rv = aStream->WriteStringZ(suffix.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_WriteOptionalCompoundObject(aStream, mCSP,
+ NS_GET_IID(nsIContentSecurityPolicy),
+ true);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // mCodebaseImmutable and mDomainImmutable will be recomputed based
+ // on the deserialized URIs in Read().
+
+ return NS_OK;
+}
+
+// Helper-function to indicate whether the CSS Unprefixing Service
+// whitelist should include dummy domains that are only intended for
+// use in testing. (Controlled by a pref.)
+static inline bool
+IsWhitelistingTestDomains()
+{
+ return gIsWhitelistingTestDomains;
+}
+
+// Checks if the given URI's host is on our "full domain" whitelist
+// (i.e. if it's an exact match against a domain that needs unprefixing)
+static bool
+IsOnFullDomainWhitelist(nsIURI* aURI)
+{
+ nsAutoCString hostStr;
+ nsresult rv = aURI->GetHost(hostStr);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ // NOTE: This static whitelist is expected to be short. If that changes,
+ // we should consider a different representation; e.g. hash-set, prefix tree.
+ static const nsLiteralCString sFullDomainsOnWhitelist[] = {
+ // 0th entry only active when testing:
+ NS_LITERAL_CSTRING("test1.example.org"),
+ NS_LITERAL_CSTRING("map.baidu.com"),
+ NS_LITERAL_CSTRING("3g.163.com"),
+ NS_LITERAL_CSTRING("3glogo.gtimg.com"), // for 3g.163.com
+ NS_LITERAL_CSTRING("info.3g.qq.com"), // for 3g.qq.com
+ NS_LITERAL_CSTRING("3gimg.qq.com"), // for 3g.qq.com
+ NS_LITERAL_CSTRING("img.m.baidu.com"), // for [shucheng|ks].baidu.com
+ NS_LITERAL_CSTRING("m.mogujie.com"),
+ NS_LITERAL_CSTRING("touch.qunar.com"),
+ NS_LITERAL_CSTRING("mjs.sinaimg.cn"), // for sina.cn
+ NS_LITERAL_CSTRING("static.qiyi.com"), // for m.iqiyi.com
+ NS_LITERAL_CSTRING("cdn.kuaidi100.com"), // for m.kuaidi100.com
+ NS_LITERAL_CSTRING("m.pc6.com"),
+ NS_LITERAL_CSTRING("m.haosou.com"),
+ NS_LITERAL_CSTRING("m.mi.com"),
+ NS_LITERAL_CSTRING("wappass.baidu.com"),
+ NS_LITERAL_CSTRING("m.video.baidu.com"),
+ NS_LITERAL_CSTRING("m.video.baidu.com"),
+ NS_LITERAL_CSTRING("imgcache.gtimg.cn"), // for m.v.qq.com
+ NS_LITERAL_CSTRING("s.tabelog.jp"),
+ NS_LITERAL_CSTRING("s.yimg.jp"), // for s.tabelog.jp
+ NS_LITERAL_CSTRING("i.yimg.jp"), // for *.yahoo.co.jp
+ NS_LITERAL_CSTRING("ai.yimg.jp"), // for *.yahoo.co.jp
+ NS_LITERAL_CSTRING("m.finance.yahoo.co.jp"),
+ NS_LITERAL_CSTRING("daily.c.yimg.jp"), // for sp.daily.co.jp
+ NS_LITERAL_CSTRING("stat100.ameba.jp"), // for ameblo.jp
+ NS_LITERAL_CSTRING("user.ameba.jp"), // for ameblo.jp
+ NS_LITERAL_CSTRING("www.goo.ne.jp"),
+ NS_LITERAL_CSTRING("x.gnst.jp"), // for mobile.gnavi.co.jp
+ NS_LITERAL_CSTRING("c.x.gnst.jp"), // for mobile.gnavi.co.jp
+ NS_LITERAL_CSTRING("www.smbc-card.com"),
+ NS_LITERAL_CSTRING("static.card.jp.rakuten-static.com"), // for rakuten-card.co.jp
+ NS_LITERAL_CSTRING("img.travel.rakuten.co.jp"), // for travel.rakuten.co.jp
+ NS_LITERAL_CSTRING("img.mixi.net"), // for mixi.jp
+ NS_LITERAL_CSTRING("girlschannel.net"),
+ NS_LITERAL_CSTRING("www.fancl.co.jp"),
+ NS_LITERAL_CSTRING("s.cosme.net"),
+ NS_LITERAL_CSTRING("www.sapporobeer.jp"),
+ NS_LITERAL_CSTRING("www.mapion.co.jp"),
+ NS_LITERAL_CSTRING("touch.navitime.co.jp"),
+ NS_LITERAL_CSTRING("sp.mbga.jp"),
+ NS_LITERAL_CSTRING("ava-a.sp.mbga.jp"), // for sp.mbga.jp
+ NS_LITERAL_CSTRING("www.ntv.co.jp"),
+ NS_LITERAL_CSTRING("mobile.suntory.co.jp"), // for suntory.jp
+ NS_LITERAL_CSTRING("www.aeonsquare.net"),
+ NS_LITERAL_CSTRING("mw.nikkei.com"),
+ NS_LITERAL_CSTRING("www.nhk.or.jp"),
+ NS_LITERAL_CSTRING("www.tokyo-sports.co.jp"),
+ NS_LITERAL_CSTRING("www.bellemaison.jp"),
+ NS_LITERAL_CSTRING("www.kuronekoyamato.co.jp"),
+ NS_LITERAL_CSTRING("formassist.jp"), // for orico.jp
+ NS_LITERAL_CSTRING("sp.m.reuters.co.jp"),
+ NS_LITERAL_CSTRING("www.atre.co.jp"),
+ NS_LITERAL_CSTRING("www.jtb.co.jp"),
+ NS_LITERAL_CSTRING("www.sharp.co.jp"),
+ NS_LITERAL_CSTRING("www.biccamera.com"),
+ NS_LITERAL_CSTRING("weathernews.jp"),
+ NS_LITERAL_CSTRING("cache.ymail.jp"), // for www.yamada-denkiweb.com
+ };
+ static const size_t sNumFullDomainsOnWhitelist =
+ MOZ_ARRAY_LENGTH(sFullDomainsOnWhitelist);
+
+ // Skip 0th (dummy) entry in whitelist, unless a pref is enabled.
+ const size_t firstWhitelistIdx = IsWhitelistingTestDomains() ? 0 : 1;
+
+ for (size_t i = firstWhitelistIdx; i < sNumFullDomainsOnWhitelist; ++i) {
+ if (hostStr == sFullDomainsOnWhitelist[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Checks if the given URI's host is on our "base domain" whitelist
+// (i.e. if it's a subdomain of some host that we've whitelisted as needing
+// unprefixing for all its subdomains)
+static bool
+IsOnBaseDomainWhitelist(nsIURI* aURI)
+{
+ static const nsLiteralCString sBaseDomainsOnWhitelist[] = {
+ // 0th entry only active when testing:
+ NS_LITERAL_CSTRING("test2.example.org"),
+ NS_LITERAL_CSTRING("tbcdn.cn"), // for m.taobao.com
+ NS_LITERAL_CSTRING("alicdn.com"), // for m.taobao.com
+ NS_LITERAL_CSTRING("dpfile.com"), // for m.dianping.com
+ NS_LITERAL_CSTRING("hao123img.com"), // for hao123.com
+ NS_LITERAL_CSTRING("tabelog.k-img.com"), // for s.tabelog.com
+ NS_LITERAL_CSTRING("tsite.jp"), // for *.tsite.jp
+ };
+ static const size_t sNumBaseDomainsOnWhitelist =
+ MOZ_ARRAY_LENGTH(sBaseDomainsOnWhitelist);
+
+ nsCOMPtr<nsIEffectiveTLDService> tldService =
+ do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
+
+ if (tldService) {
+ // Skip 0th test-entry in whitelist, unless the testing pref is enabled.
+ const size_t firstWhitelistIdx = IsWhitelistingTestDomains() ? 0 : 1;
+
+ // Right now, the test base-domain "test2.example.org" is the only entry in
+ // its whitelist with a nonzero "depth". So we'll only bother going beyond
+ // 0 depth (to 1) if that entry is enabled. (No point in slowing down the
+ // normal codepath, for the benefit of a disabled test domain.) If we add a
+ // "real" base-domain with a depth of >= 1 to our whitelist, we can get rid
+ // of this conditional & just make this a static variable.
+ const uint32_t maxSubdomainDepth = IsWhitelistingTestDomains() ? 1 : 0;
+
+ for (uint32_t subdomainDepth = 0;
+ subdomainDepth <= maxSubdomainDepth; ++subdomainDepth) {
+
+ // Get the base domain (to depth |subdomainDepth|) from passed-in URI:
+ nsAutoCString baseDomainStr;
+ nsresult rv = tldService->GetBaseDomain(aURI, subdomainDepth,
+ baseDomainStr);
+ if (NS_FAILED(rv)) {
+ // aURI doesn't have |subdomainDepth| levels of subdomains. If we got
+ // here without a match yet, then aURI is not on our whitelist.
+ return false;
+ }
+
+ // Compare the base domain against each entry in our whitelist:
+ for (size_t i = firstWhitelistIdx; i < sNumBaseDomainsOnWhitelist; ++i) {
+ if (baseDomainStr == sBaseDomainsOnWhitelist[i]) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+// The actual (non-cached) implementation of IsOnCSSUnprefixingWhitelist():
+static bool
+IsOnCSSUnprefixingWhitelistImpl(nsIURI* aURI)
+{
+ // Check scheme, so we can drop any non-HTTP/HTTPS URIs right away
+ nsAutoCString schemeStr;
+ nsresult rv = aURI->GetScheme(schemeStr);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ // Only proceed if scheme is "http" or "https"
+ if (!(StringBeginsWith(schemeStr, NS_LITERAL_CSTRING("http")) &&
+ (schemeStr.Length() == 4 ||
+ (schemeStr.Length() == 5 && schemeStr[4] == 's')))) {
+ return false;
+ }
+
+ return (IsOnFullDomainWhitelist(aURI) ||
+ IsOnBaseDomainWhitelist(aURI));
+}
+
+
+bool
+nsPrincipal::IsOnCSSUnprefixingWhitelist()
+{
+ if (mIsOnCSSUnprefixingWhitelist.isNothing()) {
+ // Value not cached -- perform our lazy whitelist-check.
+ // (NOTE: If our URI is mutable, we just assume it's not on the whitelist,
+ // since our caching strategy won't work. This isn't expected to be common.)
+ mIsOnCSSUnprefixingWhitelist.emplace(
+ mCodebaseImmutable &&
+ IsOnCSSUnprefixingWhitelistImpl(mCodebase));
+ }
+
+ return *mIsOnCSSUnprefixingWhitelist;
+}
+
+/************************************************************************************************************************/
+
+NS_IMPL_CLASSINFO(nsExpandedPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
+ NS_EXPANDEDPRINCIPAL_CID)
+NS_IMPL_QUERY_INTERFACE_CI(nsExpandedPrincipal,
+ nsIPrincipal,
+ nsIExpandedPrincipal)
+NS_IMPL_CI_INTERFACE_GETTER(nsExpandedPrincipal,
+ nsIPrincipal,
+ nsIExpandedPrincipal)
+
+struct OriginComparator
+{
+ bool LessThan(nsIPrincipal* a, nsIPrincipal* b) const
+ {
+ nsAutoCString originA;
+ nsresult rv = a->GetOrigin(originA);
+ NS_ENSURE_SUCCESS(rv, false);
+ nsAutoCString originB;
+ rv = b->GetOrigin(originB);
+ NS_ENSURE_SUCCESS(rv, false);
+ return originA < originB;
+ }
+
+ bool Equals(nsIPrincipal* a, nsIPrincipal* b) const
+ {
+ nsAutoCString originA;
+ nsresult rv = a->GetOrigin(originA);
+ NS_ENSURE_SUCCESS(rv, false);
+ nsAutoCString originB;
+ rv = b->GetOrigin(originB);
+ NS_ENSURE_SUCCESS(rv, false);
+ return a == b;
+ }
+};
+
+nsExpandedPrincipal::nsExpandedPrincipal(nsTArray<nsCOMPtr<nsIPrincipal>> &aWhiteList,
+ const PrincipalOriginAttributes& aAttrs)
+{
+ // We force the principals to be sorted by origin so that nsExpandedPrincipal
+ // origins can have a canonical form.
+ OriginComparator c;
+ for (size_t i = 0; i < aWhiteList.Length(); ++i) {
+ mPrincipals.InsertElementSorted(aWhiteList[i], c);
+ }
+ mOriginAttributes = aAttrs;
+}
+
+nsExpandedPrincipal::~nsExpandedPrincipal()
+{ }
+
+NS_IMETHODIMP
+nsExpandedPrincipal::GetDomain(nsIURI** aDomain)
+{
+ *aDomain = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsExpandedPrincipal::SetDomain(nsIURI* aDomain)
+{
+ return NS_OK;
+}
+
+nsresult
+nsExpandedPrincipal::GetOriginInternal(nsACString& aOrigin)
+{
+ aOrigin.AssignLiteral("[Expanded Principal [");
+ for (size_t i = 0; i < mPrincipals.Length(); ++i) {
+ if (i != 0) {
+ aOrigin.AppendLiteral(", ");
+ }
+
+ nsAutoCString subOrigin;
+ nsresult rv = mPrincipals.ElementAt(i)->GetOrigin(subOrigin);
+ NS_ENSURE_SUCCESS(rv, rv);
+ aOrigin.Append(subOrigin);
+ }
+
+ aOrigin.Append("]]");
+ return NS_OK;
+}
+
+bool
+nsExpandedPrincipal::SubsumesInternal(nsIPrincipal* aOther,
+ BasePrincipal::DocumentDomainConsideration aConsideration)
+{
+ // If aOther is an ExpandedPrincipal too, we break it down into its component
+ // nsIPrincipals, and check subsumes on each one.
+ nsCOMPtr<nsIExpandedPrincipal> expanded = do_QueryInterface(aOther);
+ if (expanded) {
+ nsTArray< nsCOMPtr<nsIPrincipal> >* otherList;
+ expanded->GetWhiteList(&otherList);
+ for (uint32_t i = 0; i < otherList->Length(); ++i){
+ // Use SubsumesInternal rather than Subsumes here, since OriginAttribute
+ // checks are only done between non-expanded sub-principals, and we don't
+ // need to incur the extra virtual call overhead.
+ if (!SubsumesInternal((*otherList)[i], aConsideration)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // We're dealing with a regular principal. One of our principals must subsume
+ // it.
+ for (uint32_t i = 0; i < mPrincipals.Length(); ++i) {
+ if (Cast(mPrincipals[i])->Subsumes(aOther, aConsideration)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+nsExpandedPrincipal::MayLoadInternal(nsIURI* uri)
+{
+ for (uint32_t i = 0; i < mPrincipals.Length(); ++i){
+ if (BasePrincipal::Cast(mPrincipals[i])->MayLoadInternal(uri)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+NS_IMETHODIMP
+nsExpandedPrincipal::GetHashValue(uint32_t* result)
+{
+ MOZ_CRASH("extended principal should never be used as key in a hash map");
+}
+
+NS_IMETHODIMP
+nsExpandedPrincipal::GetURI(nsIURI** aURI)
+{
+ *aURI = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsExpandedPrincipal::GetWhiteList(nsTArray<nsCOMPtr<nsIPrincipal> >** aWhiteList)
+{
+ *aWhiteList = &mPrincipals;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsExpandedPrincipal::GetBaseDomain(nsACString& aBaseDomain)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+bool
+nsExpandedPrincipal::AddonHasPermission(const nsAString& aPerm)
+{
+ for (size_t i = 0; i < mPrincipals.Length(); ++i) {
+ if (BasePrincipal::Cast(mPrincipals[i])->AddonHasPermission(aPerm)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+nsExpandedPrincipal::IsOnCSSUnprefixingWhitelist()
+{
+ // CSS Unprefixing Whitelist is a per-origin thing; doesn't really make sense
+ // for an expanded principal. (And probably shouldn't be needed.)
+ return false;
+}
+
+
+nsresult
+nsExpandedPrincipal::GetScriptLocation(nsACString& aStr)
+{
+ aStr.Assign("[Expanded Principal [");
+ for (size_t i = 0; i < mPrincipals.Length(); ++i) {
+ if (i != 0) {
+ aStr.AppendLiteral(", ");
+ }
+
+ nsAutoCString spec;
+ nsresult rv =
+ nsJSPrincipals::get(mPrincipals.ElementAt(i))->GetScriptLocation(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aStr.Append(spec);
+ }
+ aStr.Append("]]");
+ return NS_OK;
+}
+
+//////////////////////////////////////////
+// Methods implementing nsISerializable //
+//////////////////////////////////////////
+
+NS_IMETHODIMP
+nsExpandedPrincipal::Read(nsIObjectInputStream* aStream)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsExpandedPrincipal::Write(nsIObjectOutputStream* aStream)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/caps/nsPrincipal.h b/caps/nsPrincipal.h
new file mode 100644
index 000000000..d20d81ee3
--- /dev/null
+++ b/caps/nsPrincipal.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 nsPrincipal_h__
+#define nsPrincipal_h__
+
+#include "nsCOMPtr.h"
+#include "nsJSPrincipals.h"
+#include "nsTArray.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsIProtocolHandler.h"
+#include "nsNetUtil.h"
+#include "nsScriptSecurityManager.h"
+#include "mozilla/BasePrincipal.h"
+
+class nsPrincipal final : public mozilla::BasePrincipal
+{
+public:
+ NS_DECL_NSISERIALIZABLE
+ NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
+ NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
+ NS_IMETHOD GetURI(nsIURI** aURI) override;
+ NS_IMETHOD GetDomain(nsIURI** aDomain) override;
+ NS_IMETHOD SetDomain(nsIURI* aDomain) override;
+ NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
+ virtual bool IsOnCSSUnprefixingWhitelist() override;
+ bool IsCodebasePrincipal() const override { return true; }
+ nsresult GetOriginInternal(nsACString& aOrigin) override;
+
+ nsPrincipal();
+
+ // Init() must be called before the principal is in a usable state.
+ nsresult Init(nsIURI* aCodebase, const mozilla::PrincipalOriginAttributes& aOriginAttributes);
+
+ virtual nsresult GetScriptLocation(nsACString& aStr) override;
+ void SetURI(nsIURI* aURI);
+
+ /**
+ * Computes the puny-encoded origin of aURI.
+ */
+ static nsresult GetOriginForURI(nsIURI* aURI, nsACString& aOrigin);
+
+ /**
+ * Called at startup to setup static data, e.g. about:config pref-observers.
+ */
+ static void InitializeStatics();
+
+ PrincipalKind Kind() override { return eCodebasePrincipal; }
+
+ nsCOMPtr<nsIURI> mDomain;
+ nsCOMPtr<nsIURI> mCodebase;
+ // If mCodebaseImmutable is true, mCodebase is non-null and immutable
+ bool mCodebaseImmutable;
+ bool mDomainImmutable;
+ bool mInitialized;
+ mozilla::Maybe<bool> mIsOnCSSUnprefixingWhitelist; // Lazily-computed
+
+protected:
+ virtual ~nsPrincipal();
+
+ bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override;
+ bool MayLoadInternal(nsIURI* aURI) override;
+};
+
+class nsExpandedPrincipal : public nsIExpandedPrincipal, public mozilla::BasePrincipal
+{
+public:
+ nsExpandedPrincipal(nsTArray<nsCOMPtr<nsIPrincipal>> &aWhiteList,
+ const mozilla::PrincipalOriginAttributes& aAttrs);
+
+ NS_DECL_NSIEXPANDEDPRINCIPAL
+ NS_DECL_NSISERIALIZABLE
+ NS_IMETHOD_(MozExternalRefCountType) AddRef() override { return nsJSPrincipals::AddRef(); };
+ NS_IMETHOD_(MozExternalRefCountType) Release() override { return nsJSPrincipals::Release(); };
+ NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
+ NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
+ NS_IMETHOD GetURI(nsIURI** aURI) override;
+ NS_IMETHOD GetDomain(nsIURI** aDomain) override;
+ NS_IMETHOD SetDomain(nsIURI* aDomain) override;
+ NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
+ virtual bool AddonHasPermission(const nsAString& aPerm) override;
+ virtual bool IsOnCSSUnprefixingWhitelist() override;
+ virtual nsresult GetScriptLocation(nsACString &aStr) override;
+ nsresult GetOriginInternal(nsACString& aOrigin) override;
+
+ PrincipalKind Kind() override { return eExpandedPrincipal; }
+
+protected:
+ virtual ~nsExpandedPrincipal();
+
+ bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override;
+ bool MayLoadInternal(nsIURI* aURI) override;
+
+private:
+ nsTArray< nsCOMPtr<nsIPrincipal> > mPrincipals;
+};
+
+#define NS_PRINCIPAL_CONTRACTID "@mozilla.org/principal;1"
+#define NS_PRINCIPAL_CID \
+{ 0x653e0e4d, 0x3ee4, 0x45fa, \
+ { 0xb2, 0x72, 0x97, 0xc2, 0x0b, 0xc0, 0x1e, 0xb8 } }
+
+#define NS_EXPANDEDPRINCIPAL_CONTRACTID "@mozilla.org/expandedprincipal;1"
+#define NS_EXPANDEDPRINCIPAL_CID \
+{ 0xe8ee88b0, 0x5571, 0x4086, \
+ { 0xa4, 0x5b, 0x39, 0xa7, 0x16, 0x90, 0x6b, 0xdb } }
+
+#endif // nsPrincipal_h__
diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp
new file mode 100644
index 000000000..a219dcaed
--- /dev/null
+++ b/caps/nsScriptSecurityManager.cpp
@@ -0,0 +1,1759 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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 "nsScriptSecurityManager.h"
+
+#include "mozilla/ArrayUtils.h"
+
+#include "xpcpublic.h"
+#include "XPCWrapper.h"
+#include "nsIAppsService.h"
+#include "nsIInputStreamChannel.h"
+#include "nsILoadContext.h"
+#include "nsIServiceManager.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "nsIScriptContext.h"
+#include "nsIURL.h"
+#include "nsINestedURI.h"
+#include "nspr.h"
+#include "nsJSPrincipals.h"
+#include "mozilla/BasePrincipal.h"
+#include "nsSystemPrincipal.h"
+#include "nsPrincipal.h"
+#include "nsNullPrincipal.h"
+#include "DomainPolicy.h"
+#include "nsXPIDLString.h"
+#include "nsCRT.h"
+#include "nsCRTGlue.h"
+#include "nsDocShell.h"
+#include "nsError.h"
+#include "nsDOMCID.h"
+#include "nsTextFormatter.h"
+#include "nsIStringBundle.h"
+#include "nsNetUtil.h"
+#include "nsIEffectiveTLDService.h"
+#include "nsIProperties.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIFile.h"
+#include "nsIFileURL.h"
+#include "nsIZipReader.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsPIDOMWindow.h"
+#include "nsIDocShell.h"
+#include "nsIPrompt.h"
+#include "nsIWindowWatcher.h"
+#include "nsIConsoleService.h"
+#include "nsIObserverService.h"
+#include "nsIContent.h"
+#include "nsDOMJSUtils.h"
+#include "nsAboutProtocolUtils.h"
+#include "nsIClassInfo.h"
+#include "nsIURIFixup.h"
+#include "nsCDefaultURIFixup.h"
+#include "nsIChromeRegistry.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "mozIApplication.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/BindingUtils.h"
+#include <stdint.h>
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticPtr.h"
+#include "nsContentUtils.h"
+#include "nsJSUtils.h"
+#include "nsILoadInfo.h"
+#include "nsXPCOMStrings.h"
+
+// This should be probably defined on some other place... but I couldn't find it
+#define WEBAPPS_PERM_NAME "webapps-manage"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+nsIIOService *nsScriptSecurityManager::sIOService = nullptr;
+nsIStringBundle *nsScriptSecurityManager::sStrBundle = nullptr;
+JSContext *nsScriptSecurityManager::sContext = nullptr;
+bool nsScriptSecurityManager::sStrictFileOriginPolicy = true;
+
+///////////////////////////
+// Convenience Functions //
+///////////////////////////
+
+class nsAutoInPrincipalDomainOriginSetter {
+public:
+ nsAutoInPrincipalDomainOriginSetter() {
+ ++sInPrincipalDomainOrigin;
+ }
+ ~nsAutoInPrincipalDomainOriginSetter() {
+ --sInPrincipalDomainOrigin;
+ }
+ static uint32_t sInPrincipalDomainOrigin;
+};
+uint32_t nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin;
+
+static
+nsresult
+GetOriginFromURI(nsIURI* aURI, nsACString& aOrigin)
+{
+ if (nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin > 1) {
+ // Allow a single recursive call to GetPrincipalDomainOrigin, since that
+ // might be happening on a different principal from the first call. But
+ // after that, cut off the recursion; it just indicates that something
+ // we're doing in this method causes us to reenter a security check here.
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsAutoInPrincipalDomainOriginSetter autoSetter;
+
+ nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
+ NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
+
+ nsAutoCString hostPort;
+
+ nsresult rv = uri->GetHostPort(hostPort);
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString scheme;
+ rv = uri->GetScheme(scheme);
+ NS_ENSURE_SUCCESS(rv, rv);
+ aOrigin = scheme + NS_LITERAL_CSTRING("://") + hostPort;
+ }
+ else {
+ // Some URIs (e.g., nsSimpleURI) don't support host. Just
+ // get the full spec.
+ rv = uri->GetSpec(aOrigin);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+static
+nsresult
+GetPrincipalDomainOrigin(nsIPrincipal* aPrincipal,
+ nsACString& aOrigin)
+{
+
+ nsCOMPtr<nsIURI> uri;
+ aPrincipal->GetDomain(getter_AddRefs(uri));
+ if (!uri) {
+ aPrincipal->GetURI(getter_AddRefs(uri));
+ }
+ NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
+
+ return GetOriginFromURI(uri, aOrigin);
+}
+
+inline void SetPendingExceptionASCII(JSContext *cx, const char *aMsg)
+{
+ JS_ReportErrorASCII(cx, "%s", aMsg);
+}
+
+inline void SetPendingException(JSContext *cx, const char16_t *aMsg)
+{
+ NS_ConvertUTF16toUTF8 msg(aMsg);
+ JS_ReportErrorUTF8(cx, "%s", msg.get());
+}
+
+// Helper class to get stuff from the ClassInfo and not waste extra time with
+// virtual method calls for things it has already gotten
+class ClassInfoData
+{
+public:
+ ClassInfoData(nsIClassInfo *aClassInfo, const char *aName)
+ : mClassInfo(aClassInfo),
+ mName(const_cast<char *>(aName)),
+ mDidGetFlags(false),
+ mMustFreeName(false)
+ {
+ }
+
+ ~ClassInfoData()
+ {
+ if (mMustFreeName)
+ free(mName);
+ }
+
+ uint32_t GetFlags()
+ {
+ if (!mDidGetFlags) {
+ if (mClassInfo) {
+ nsresult rv = mClassInfo->GetFlags(&mFlags);
+ if (NS_FAILED(rv)) {
+ mFlags = 0;
+ }
+ } else {
+ mFlags = 0;
+ }
+
+ mDidGetFlags = true;
+ }
+
+ return mFlags;
+ }
+
+ bool IsDOMClass()
+ {
+ return !!(GetFlags() & nsIClassInfo::DOM_OBJECT);
+ }
+
+ const char* GetName()
+ {
+ if (!mName) {
+ if (mClassInfo) {
+ mClassInfo->GetClassDescription(&mName);
+ }
+
+ if (mName) {
+ mMustFreeName = true;
+ } else {
+ mName = const_cast<char *>("UnnamedClass");
+ }
+ }
+
+ return mName;
+ }
+
+private:
+ nsIClassInfo *mClassInfo; // WEAK
+ uint32_t mFlags;
+ char *mName;
+ bool mDidGetFlags;
+ bool mMustFreeName;
+};
+
+/* static */
+bool
+nsScriptSecurityManager::SecurityCompareURIs(nsIURI* aSourceURI,
+ nsIURI* aTargetURI)
+{
+ return NS_SecurityCompareURIs(aSourceURI, aTargetURI, sStrictFileOriginPolicy);
+}
+
+// SecurityHashURI is consistent with SecurityCompareURIs because NS_SecurityHashURI
+// is consistent with NS_SecurityCompareURIs. See nsNetUtil.h.
+uint32_t
+nsScriptSecurityManager::SecurityHashURI(nsIURI* aURI)
+{
+ return NS_SecurityHashURI(aURI);
+}
+
+uint16_t
+nsScriptSecurityManager::AppStatusForPrincipal(nsIPrincipal *aPrin)
+{
+ uint32_t appId = aPrin->GetAppId();
+
+ // After bug 1238160, the principal no longer knows how to answer "is this a
+ // browser element", which is really what this code path wants. Currently,
+ // desktop is the only platform where we intend to disable isolation on a
+ // browser frame, so non-desktop should be able to assume that
+ // inIsolatedMozBrowser is true for all mozbrowser frames. Additionally,
+ // apps are no longer used on desktop, so appId is always NO_APP_ID. We use
+ // a release assertion in nsFrameLoader::OwnerIsIsolatedMozBrowserFrame so
+ // that platforms with apps can assume inIsolatedMozBrowser is true for all
+ // mozbrowser frames.
+ bool inIsolatedMozBrowser = aPrin->GetIsInIsolatedMozBrowserElement();
+
+ NS_WARNING_ASSERTION(
+ appId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
+ "Asking for app status on a principal with an unknown app id");
+
+ // Installed apps have a valid app id (not NO_APP_ID or UNKNOWN_APP_ID)
+ // and they are not inside a mozbrowser.
+ if (appId == nsIScriptSecurityManager::NO_APP_ID ||
+ appId == nsIScriptSecurityManager::UNKNOWN_APP_ID ||
+ inIsolatedMozBrowser)
+ {
+ return nsIPrincipal::APP_STATUS_NOT_INSTALLED;
+ }
+
+ nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
+ NS_ENSURE_TRUE(appsService, nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+
+ nsCOMPtr<mozIApplication> app;
+ appsService->GetAppByLocalId(appId, getter_AddRefs(app));
+ NS_ENSURE_TRUE(app, nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+
+ uint16_t status = nsIPrincipal::APP_STATUS_INSTALLED;
+ NS_ENSURE_SUCCESS(app->GetAppStatus(&status),
+ nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+
+ nsString appOrigin;
+ NS_ENSURE_SUCCESS(app->GetOrigin(appOrigin),
+ nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+ nsCOMPtr<nsIURI> appURI;
+ NS_ENSURE_SUCCESS(NS_NewURI(getter_AddRefs(appURI), appOrigin),
+ nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+
+ // The app could contain a cross-origin iframe - make sure that the content
+ // is actually same-origin with the app.
+ MOZ_ASSERT(inIsolatedMozBrowser == false, "Checked this above");
+ nsAutoCString suffix;
+ PrincipalOriginAttributes attrs;
+ NS_ENSURE_TRUE(attrs.PopulateFromOrigin(NS_ConvertUTF16toUTF8(appOrigin), suffix),
+ nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+ attrs.mAppId = appId;
+ attrs.mInIsolatedMozBrowser = false;
+ nsCOMPtr<nsIPrincipal> appPrin = BasePrincipal::CreateCodebasePrincipal(appURI, attrs);
+ NS_ENSURE_TRUE(appPrin, nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+ return aPrin->Equals(appPrin) ? status
+ : nsIPrincipal::APP_STATUS_NOT_INSTALLED;
+}
+
+/*
+ * GetChannelResultPrincipal will return the principal that the resource
+ * returned by this channel will use. For example, if the resource is in
+ * a sandbox, it will return the nullprincipal. If the resource is forced
+ * to inherit principal, it will return the principal of its parent. If
+ * the load doesn't require sandboxing or inheriting, it will return the same
+ * principal as GetChannelURIPrincipal. Namely the principal of the URI
+ * that is being loaded.
+ */
+NS_IMETHODIMP
+nsScriptSecurityManager::GetChannelResultPrincipal(nsIChannel* aChannel,
+ nsIPrincipal** aPrincipal)
+{
+ return GetChannelResultPrincipal(aChannel, aPrincipal,
+ /*aIgnoreSandboxing*/ false);
+}
+
+nsresult
+nsScriptSecurityManager::GetChannelResultPrincipalIfNotSandboxed(nsIChannel* aChannel,
+ nsIPrincipal** aPrincipal)
+{
+ return GetChannelResultPrincipal(aChannel, aPrincipal,
+ /*aIgnoreSandboxing*/ true);
+}
+
+nsresult
+nsScriptSecurityManager::GetChannelResultPrincipal(nsIChannel* aChannel,
+ nsIPrincipal** aPrincipal,
+ bool aIgnoreSandboxing)
+{
+ NS_PRECONDITION(aChannel, "Must have channel!");
+ // Check whether we have an nsILoadInfo that says what we should do.
+ nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+ if (loadInfo && loadInfo->GetForceInheritPrincipalOverruleOwner()) {
+ nsCOMPtr<nsIPrincipal> principalToInherit = loadInfo->PrincipalToInherit();
+ if (!principalToInherit) {
+ principalToInherit = loadInfo->TriggeringPrincipal();
+ }
+ principalToInherit.forget(aPrincipal);
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsISupports> owner;
+ aChannel->GetOwner(getter_AddRefs(owner));
+ if (owner) {
+ CallQueryInterface(owner, aPrincipal);
+ if (*aPrincipal) {
+ return NS_OK;
+ }
+ }
+
+ if (loadInfo) {
+ if (!aIgnoreSandboxing && loadInfo->GetLoadingSandboxed()) {
+ RefPtr<nsNullPrincipal> prin;
+ if (loadInfo->LoadingPrincipal()) {
+ prin =
+ nsNullPrincipal::CreateWithInheritedAttributes(loadInfo->LoadingPrincipal());
+ } else {
+ NeckoOriginAttributes nAttrs;
+ loadInfo->GetOriginAttributes(&nAttrs);
+ PrincipalOriginAttributes pAttrs;
+ pAttrs.InheritFromNecko(nAttrs);
+ prin = nsNullPrincipal::Create(pAttrs);
+ }
+ prin.forget(aPrincipal);
+
+ // if the new NullPrincipal (above) loads an iframe[srcdoc], we
+ // need to inherit an existing CSP to avoid bypasses (bug 1073952).
+ // We continue inheriting for nested frames with e.g., data: URLs.
+ if (loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_SUBDOCUMENT) {
+ nsCOMPtr<nsIURI> uri;
+ aChannel->GetURI(getter_AddRefs(uri));
+ nsAutoCString URISpec;
+ uri->GetSpec(URISpec);
+ bool isData = (NS_SUCCEEDED(uri->SchemeIs("data", &isData)) && isData);
+ if (URISpec.EqualsLiteral("about:srcdoc") || isData) {
+ nsCOMPtr<nsIPrincipal> principalToInherit = loadInfo->PrincipalToInherit();
+ if (!principalToInherit) {
+ principalToInherit = loadInfo->TriggeringPrincipal();
+ }
+ nsCOMPtr<nsIContentSecurityPolicy> originalCSP;
+ principalToInherit->GetCsp(getter_AddRefs(originalCSP));
+ if (originalCSP) {
+ // if the principalToInherit had a CSP,
+ // add it to the newly created NullPrincipal
+ // (unless it already has one)
+ nsCOMPtr<nsIContentSecurityPolicy> nullPrincipalCSP;
+ (*aPrincipal)->GetCsp(getter_AddRefs(nullPrincipalCSP));
+ if (nullPrincipalCSP) {
+ MOZ_ASSERT(nullPrincipalCSP == originalCSP,
+ "There should be no other CSP here.");
+ // CSPs are equal, no need to set it again.
+ return NS_OK;
+ } else {
+ nsresult rv = (*aPrincipal)->SetCsp(originalCSP);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ }
+ }
+ return NS_OK;
+ }
+
+ bool forceInherit = loadInfo->GetForceInheritPrincipal();
+ if (aIgnoreSandboxing && !forceInherit) {
+ // Check if SEC_FORCE_INHERIT_PRINCIPAL was dropped because of
+ // sandboxing:
+ if (loadInfo->GetLoadingSandboxed() &&
+ loadInfo->GetForceInheritPrincipalDropped()) {
+ forceInherit = true;
+ }
+ }
+ if (forceInherit) {
+ nsCOMPtr<nsIPrincipal> principalToInherit = loadInfo->PrincipalToInherit();
+ if (!principalToInherit) {
+ principalToInherit = loadInfo->TriggeringPrincipal();
+ }
+ principalToInherit.forget(aPrincipal);
+ return NS_OK;
+ }
+
+ nsSecurityFlags securityFlags = loadInfo->GetSecurityMode();
+ // The data: inheritance flags should only apply to the initial load,
+ // not to loads that it might have redirected to.
+ if (loadInfo->RedirectChain().IsEmpty() &&
+ (securityFlags == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS ||
+ securityFlags == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS ||
+ securityFlags == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS)) {
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIPrincipal> principalToInherit = loadInfo->PrincipalToInherit();
+ if (!principalToInherit) {
+ principalToInherit = loadInfo->TriggeringPrincipal();
+ }
+ bool inheritForAboutBlank = loadInfo->GetAboutBlankInherits();
+
+ if (nsContentUtils::ChannelShouldInheritPrincipal(principalToInherit,
+ uri,
+ inheritForAboutBlank,
+ false)) {
+ principalToInherit.forget(aPrincipal);
+ return NS_OK;
+ }
+ }
+ }
+ return GetChannelURIPrincipal(aChannel, aPrincipal);
+}
+
+nsresult
+nsScriptSecurityManager::MaybeSetAddonIdFromURI(PrincipalOriginAttributes& aAttrs, nsIURI* aURI)
+{
+ nsAutoCString scheme;
+ nsresult rv = aURI->GetScheme(scheme);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (scheme.EqualsLiteral("moz-extension") && GetAddonPolicyService()) {
+ rv = GetAddonPolicyService()->ExtensionURIToAddonId(aURI, aAttrs.mAddonId);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+/* The principal of the URI that this channel is loading. This is never
+ * affected by things like sandboxed loads, or loads where we forcefully
+ * inherit the principal. Think of this as the principal of the server
+ * which this channel is loading from. Most callers should use
+ * GetChannelResultPrincipal instead of GetChannelURIPrincipal. Only
+ * call GetChannelURIPrincipal if you are sure that you want the
+ * principal that matches the uri, even in cases when the load is
+ * sandboxed or when the load could be a blob or data uri (i.e even when
+ * you encounter loads that may or may not be sandboxed and loads
+ * that may or may not inherit)."
+ */
+NS_IMETHODIMP
+nsScriptSecurityManager::GetChannelURIPrincipal(nsIChannel* aChannel,
+ nsIPrincipal** aPrincipal)
+{
+ NS_PRECONDITION(aChannel, "Must have channel!");
+
+ // Get the principal from the URI. Make sure this does the same thing
+ // as nsDocument::Reset and XULDocument::StartDocumentLoad.
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsILoadInfo> loadInfo;
+ aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
+
+ // Inherit the origin attributes from loadInfo.
+ // If this is a top-level document load, the origin attributes of the
+ // loadInfo will be set from nsDocShell::DoURILoad.
+ // For subresource loading, the origin attributes of the loadInfo is from
+ // its loadingPrincipal.
+ PrincipalOriginAttributes attrs;
+
+ // For addons loadInfo might be null.
+ if (loadInfo) {
+ attrs.InheritFromNecko(loadInfo->GetOriginAttributes());
+ }
+ rv = MaybeSetAddonIdFromURI(attrs, uri);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(uri, attrs);
+ prin.forget(aPrincipal);
+ return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::IsSystemPrincipal(nsIPrincipal* aPrincipal,
+ bool* aIsSystem)
+{
+ *aIsSystem = (aPrincipal == mSystemPrincipal);
+ return NS_OK;
+}
+
+/////////////////////////////
+// nsScriptSecurityManager //
+/////////////////////////////
+
+////////////////////////////////////
+// Methods implementing ISupports //
+////////////////////////////////////
+NS_IMPL_ISUPPORTS(nsScriptSecurityManager,
+ nsIScriptSecurityManager,
+ nsIObserver)
+
+///////////////////////////////////////////////////
+// Methods implementing nsIScriptSecurityManager //
+///////////////////////////////////////////////////
+
+///////////////// Security Checks /////////////////
+
+bool
+nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx)
+{
+ MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
+ nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal();
+ nsCOMPtr<nsIContentSecurityPolicy> csp;
+ nsresult rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
+ NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get CSP from principal.");
+
+ // don't do anything unless there's a CSP
+ if (!csp)
+ return true;
+
+ bool evalOK = true;
+ bool reportViolation = false;
+ rv = csp->GetAllowsEval(&reportViolation, &evalOK);
+
+ if (NS_FAILED(rv))
+ {
+ NS_WARNING("CSP: failed to get allowsEval");
+ return true; // fail open to not break sites.
+ }
+
+ if (reportViolation) {
+ nsAutoString fileName;
+ unsigned lineNum = 0;
+ NS_NAMED_LITERAL_STRING(scriptSample, "call to eval() or related function blocked by CSP");
+
+ JS::AutoFilename scriptFilename;
+ if (JS::DescribeScriptedCaller(cx, &scriptFilename, &lineNum)) {
+ if (const char *file = scriptFilename.get()) {
+ CopyUTF8toUTF16(nsDependentCString(file), fileName);
+ }
+ } else {
+ MOZ_ASSERT(!JS_IsExceptionPending(cx));
+ }
+ csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
+ fileName,
+ scriptSample,
+ lineNum,
+ EmptyString(),
+ EmptyString());
+ }
+
+ return evalOK;
+}
+
+// static
+bool
+nsScriptSecurityManager::JSPrincipalsSubsume(JSPrincipals *first,
+ JSPrincipals *second)
+{
+ return nsJSPrincipals::get(first)->Subsumes(nsJSPrincipals::get(second));
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::CheckSameOriginURI(nsIURI* aSourceURI,
+ nsIURI* aTargetURI,
+ bool reportError)
+{
+ if (!SecurityCompareURIs(aSourceURI, aTargetURI))
+ {
+ if (reportError) {
+ ReportError(nullptr, NS_LITERAL_STRING("CheckSameOriginError"),
+ aSourceURI, aTargetURI);
+ }
+ return NS_ERROR_DOM_BAD_URI;
+ }
+ return NS_OK;
+}
+
+/*static*/ uint32_t
+nsScriptSecurityManager::HashPrincipalByOrigin(nsIPrincipal* aPrincipal)
+{
+ nsCOMPtr<nsIURI> uri;
+ aPrincipal->GetDomain(getter_AddRefs(uri));
+ if (!uri)
+ aPrincipal->GetURI(getter_AddRefs(uri));
+ return SecurityHashURI(uri);
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::CheckLoadURIFromScript(JSContext *cx, nsIURI *aURI)
+{
+ // Get principal of currently executing script.
+ MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
+ nsIPrincipal* principal = nsContentUtils::SubjectPrincipal();
+ nsresult rv = CheckLoadURIWithPrincipal(principal, aURI,
+ nsIScriptSecurityManager::STANDARD);
+ if (NS_SUCCEEDED(rv)) {
+ // OK to load
+ return NS_OK;
+ }
+
+ // See if we're attempting to load a file: URI. If so, let a
+ // UniversalXPConnect capability trump the above check.
+ bool isFile = false;
+ bool isRes = false;
+ if (NS_FAILED(aURI->SchemeIs("file", &isFile)) ||
+ NS_FAILED(aURI->SchemeIs("resource", &isRes)))
+ return NS_ERROR_FAILURE;
+ if (isFile || isRes)
+ {
+ if (nsContentUtils::IsCallerChrome())
+ return NS_OK;
+ }
+
+ // Report error.
+ nsAutoCString spec;
+ if (NS_FAILED(aURI->GetAsciiSpec(spec)))
+ return NS_ERROR_FAILURE;
+ nsAutoCString msg("Access to '");
+ msg.Append(spec);
+ msg.AppendLiteral("' from script denied");
+ SetPendingExceptionASCII(cx, msg.get());
+ return NS_ERROR_DOM_BAD_URI;
+}
+
+/**
+ * Helper method to handle cases where a flag passed to
+ * CheckLoadURIWithPrincipal means denying loading if the given URI has certain
+ * nsIProtocolHandler flags set.
+ * @return if success, access is allowed. Otherwise, deny access
+ */
+static nsresult
+DenyAccessIfURIHasFlags(nsIURI* aURI, uint32_t aURIFlags)
+{
+ NS_PRECONDITION(aURI, "Must have URI!");
+
+ bool uriHasFlags;
+ nsresult rv =
+ NS_URIChainHasFlags(aURI, aURIFlags, &uriHasFlags);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (uriHasFlags) {
+ return NS_ERROR_DOM_BAD_URI;
+ }
+
+ return NS_OK;
+}
+
+static bool
+EqualOrSubdomain(nsIURI* aProbeArg, nsIURI* aBase)
+{
+ // Make a clone of the incoming URI, because we're going to mutate it.
+ nsCOMPtr<nsIURI> probe;
+ nsresult rv = aProbeArg->Clone(getter_AddRefs(probe));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ nsCOMPtr<nsIEffectiveTLDService> tldService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
+ NS_ENSURE_TRUE(tldService, false);
+ while (true) {
+ if (nsScriptSecurityManager::SecurityCompareURIs(probe, aBase)) {
+ return true;
+ }
+
+ nsAutoCString host, newHost;
+ rv = probe->GetHost(host);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ rv = tldService->GetNextSubDomain(host, newHost);
+ if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
+ return false;
+ }
+ NS_ENSURE_SUCCESS(rv, false);
+ rv = probe->SetHost(newHost);
+ NS_ENSURE_SUCCESS(rv, false);
+ }
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
+ nsIURI *aTargetURI,
+ uint32_t aFlags)
+{
+ NS_PRECONDITION(aPrincipal, "CheckLoadURIWithPrincipal must have a principal");
+ // If someone passes a flag that we don't understand, we should
+ // fail, because they may need a security check that we don't
+ // provide.
+ NS_ENSURE_FALSE(aFlags & ~(nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
+ nsIScriptSecurityManager::ALLOW_CHROME |
+ nsIScriptSecurityManager::DISALLOW_SCRIPT |
+ nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL |
+ nsIScriptSecurityManager::DONT_REPORT_ERRORS),
+ NS_ERROR_UNEXPECTED);
+ NS_ENSURE_ARG_POINTER(aPrincipal);
+ NS_ENSURE_ARG_POINTER(aTargetURI);
+
+ // If DISALLOW_INHERIT_PRINCIPAL is set, we prevent loading of URIs which
+ // would do such inheriting. That would be URIs that do not have their own
+ // security context. We do this even for the system principal.
+ if (aFlags & nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL) {
+ nsresult rv =
+ DenyAccessIfURIHasFlags(aTargetURI,
+ nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (aPrincipal == mSystemPrincipal) {
+ // Allow access
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIURI> sourceURI;
+ aPrincipal->GetURI(getter_AddRefs(sourceURI));
+ if (!sourceURI) {
+ nsCOMPtr<nsIExpandedPrincipal> expanded = do_QueryInterface(aPrincipal);
+ if (expanded) {
+ nsTArray< nsCOMPtr<nsIPrincipal> > *whiteList;
+ expanded->GetWhiteList(&whiteList);
+ for (uint32_t i = 0; i < whiteList->Length(); ++i) {
+ nsresult rv = CheckLoadURIWithPrincipal((*whiteList)[i],
+ aTargetURI,
+ aFlags);
+ if (NS_SUCCEEDED(rv)) {
+ // Allow access if it succeeded with one of the white listed principals
+ return NS_OK;
+ }
+ }
+ // None of our whitelisted principals worked.
+ return NS_ERROR_DOM_BAD_URI;
+ }
+ NS_ERROR("Non-system principals or expanded principal passed to CheckLoadURIWithPrincipal "
+ "must have a URI!");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // Automatic loads are not allowed from certain protocols.
+ if (aFlags & nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT) {
+ nsresult rv =
+ DenyAccessIfURIHasFlags(sourceURI,
+ nsIProtocolHandler::URI_FORBIDS_AUTOMATIC_DOCUMENT_REPLACEMENT);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // If either URI is a nested URI, get the base URI
+ nsCOMPtr<nsIURI> sourceBaseURI = NS_GetInnermostURI(sourceURI);
+ nsCOMPtr<nsIURI> targetBaseURI = NS_GetInnermostURI(aTargetURI);
+
+ //-- get the target scheme
+ nsAutoCString targetScheme;
+ nsresult rv = targetBaseURI->GetScheme(targetScheme);
+ if (NS_FAILED(rv)) return rv;
+
+ //-- Some callers do not allow loading javascript:
+ if ((aFlags & nsIScriptSecurityManager::DISALLOW_SCRIPT) &&
+ targetScheme.EqualsLiteral("javascript"))
+ {
+ return NS_ERROR_DOM_BAD_URI;
+ }
+
+ // Check for uris that are only loadable by principals that subsume them
+ bool hasFlags;
+ rv = NS_URIChainHasFlags(targetBaseURI,
+ nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS,
+ &hasFlags);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (hasFlags) {
+ // check nothing else in the URI chain has flags that prevent
+ // access:
+ rv = CheckLoadURIFlags(sourceURI, aTargetURI, sourceBaseURI,
+ targetBaseURI, aFlags);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // Check the principal is allowed to load the target.
+ return aPrincipal->CheckMayLoad(targetBaseURI, true, false);
+ }
+
+ //-- get the source scheme
+ nsAutoCString sourceScheme;
+ rv = sourceBaseURI->GetScheme(sourceScheme);
+ if (NS_FAILED(rv)) return rv;
+
+ // When comparing schemes, if the relevant pref is set, view-source URIs
+ // are reachable from same-protocol (so e.g. file: can link to
+ // view-source:file). This is required for reftests.
+ static bool sViewSourceReachableFromInner = false;
+ static bool sCachedViewSourcePref = false;
+ if (!sCachedViewSourcePref) {
+ sCachedViewSourcePref = true;
+ mozilla::Preferences::AddBoolVarCache(&sViewSourceReachableFromInner,
+ "security.view-source.reachable-from-inner-protocol");
+ }
+
+ bool targetIsViewSource = false;
+
+ if (sourceScheme.LowerCaseEqualsLiteral(NS_NULLPRINCIPAL_SCHEME)) {
+ // A null principal can target its own URI.
+ if (sourceURI == aTargetURI) {
+ return NS_OK;
+ }
+ }
+ else if (sViewSourceReachableFromInner &&
+ sourceScheme.EqualsIgnoreCase(targetScheme.get()) &&
+ NS_SUCCEEDED(aTargetURI->SchemeIs("view-source", &targetIsViewSource)) &&
+ targetIsViewSource)
+ {
+ // exception for foo: linking to view-source:foo for reftests...
+ return NS_OK;
+ }
+
+ // If we get here, check all the schemes can link to each other, from the top down:
+ nsCaseInsensitiveCStringComparator stringComparator;
+ nsCOMPtr<nsIURI> currentURI = sourceURI;
+ nsCOMPtr<nsIURI> currentOtherURI = aTargetURI;
+
+ bool denySameSchemeLinks = false;
+ rv = NS_URIChainHasFlags(aTargetURI, nsIProtocolHandler::URI_SCHEME_NOT_SELF_LINKABLE,
+ &denySameSchemeLinks);
+ if (NS_FAILED(rv)) return rv;
+
+ while (currentURI && currentOtherURI) {
+ nsAutoCString scheme, otherScheme;
+ currentURI->GetScheme(scheme);
+ currentOtherURI->GetScheme(otherScheme);
+
+ bool schemesMatch = scheme.Equals(otherScheme, stringComparator);
+ bool isSamePage = false;
+ // about: URIs are special snowflakes.
+ if (scheme.EqualsLiteral("about") && schemesMatch) {
+ nsAutoCString moduleName, otherModuleName;
+ // about: pages can always link to themselves:
+ isSamePage =
+ NS_SUCCEEDED(NS_GetAboutModuleName(currentURI, moduleName)) &&
+ NS_SUCCEEDED(NS_GetAboutModuleName(currentOtherURI, otherModuleName)) &&
+ moduleName.Equals(otherModuleName);
+ if (!isSamePage) {
+ // We will have allowed the load earlier if the source page has
+ // system principal. So we know the source has a content
+ // principal, and it's trying to link to something else.
+ // Linkable about: pages are always reachable, even if we hit
+ // the CheckLoadURIFlags call below.
+ // We punch only 1 other hole: iff the source is unlinkable,
+ // we let them link to other pages explicitly marked SAFE
+ // for content. This avoids world-linkable about: pages linking
+ // to non-world-linkable about: pages.
+ nsCOMPtr<nsIAboutModule> module, otherModule;
+ bool knowBothModules =
+ NS_SUCCEEDED(NS_GetAboutModule(currentURI, getter_AddRefs(module))) &&
+ NS_SUCCEEDED(NS_GetAboutModule(currentOtherURI, getter_AddRefs(otherModule)));
+ uint32_t aboutModuleFlags = 0;
+ uint32_t otherAboutModuleFlags = 0;
+ knowBothModules = knowBothModules &&
+ NS_SUCCEEDED(module->GetURIFlags(currentURI, &aboutModuleFlags)) &&
+ NS_SUCCEEDED(otherModule->GetURIFlags(currentOtherURI, &otherAboutModuleFlags));
+ if (knowBothModules) {
+ isSamePage =
+ !(aboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) &&
+ (otherAboutModuleFlags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT);
+ if (isSamePage && otherAboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) {
+ //XXXgijs: this is a hack. The target will be nested
+ // (with innerURI of moz-safe-about:whatever), and
+ // the source isn't, so we won't pass if we finish
+ // the loop. We *should* pass, though, so return here.
+ // This hack can go away when bug 1228118 is fixed.
+ return NS_OK;
+ }
+ }
+ }
+ } else {
+ bool equalExceptRef = false;
+ rv = currentURI->EqualsExceptRef(currentOtherURI, &equalExceptRef);
+ isSamePage = NS_SUCCEEDED(rv) && equalExceptRef;
+ }
+
+ // If schemes are not equal, or they're equal but the target URI
+ // is different from the source URI and doesn't always allow linking
+ // from the same scheme, check if the URI flags of the current target
+ // URI allow the current source URI to link to it.
+ // The policy is specified by the protocol flags on both URIs.
+ if (!schemesMatch || (denySameSchemeLinks && !isSamePage)) {
+ return CheckLoadURIFlags(currentURI, currentOtherURI,
+ sourceBaseURI, targetBaseURI, aFlags);
+ }
+ // Otherwise... check if we can nest another level:
+ nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(currentURI);
+ nsCOMPtr<nsINestedURI> nestedOtherURI = do_QueryInterface(currentOtherURI);
+
+ // If schemes match and neither URI is nested further, we're OK.
+ if (!nestedURI && !nestedOtherURI) {
+ return NS_OK;
+ }
+ // If one is nested and the other isn't, something is wrong.
+ if (!nestedURI != !nestedOtherURI) {
+ return NS_ERROR_DOM_BAD_URI;
+ }
+ // Otherwise, both should be nested and we'll go through the loop again.
+ nestedURI->GetInnerURI(getter_AddRefs(currentURI));
+ nestedOtherURI->GetInnerURI(getter_AddRefs(currentOtherURI));
+ }
+
+ // We should never get here. We should always return from inside the loop.
+ return NS_ERROR_DOM_BAD_URI;
+}
+
+/**
+ * Helper method to check whether the target URI and its innermost ("base") URI
+ * has protocol flags that should stop it from being loaded by the source URI
+ * (and/or the source URI's innermost ("base") URI), taking into account any
+ * nsIScriptSecurityManager flags originally passed to
+ * CheckLoadURIWithPrincipal and friends.
+ *
+ * @return if success, access is allowed. Otherwise, deny access
+ */
+nsresult
+nsScriptSecurityManager::CheckLoadURIFlags(nsIURI *aSourceURI,
+ nsIURI *aTargetURI,
+ nsIURI *aSourceBaseURI,
+ nsIURI *aTargetBaseURI,
+ uint32_t aFlags)
+{
+ // Note that the order of policy checks here is very important!
+ // We start from most restrictive and work our way down.
+ bool reportErrors = !(aFlags & nsIScriptSecurityManager::DONT_REPORT_ERRORS);
+ NS_NAMED_LITERAL_STRING(errorTag, "CheckLoadURIError");
+
+ nsAutoCString targetScheme;
+ nsresult rv = aTargetBaseURI->GetScheme(targetScheme);
+ if (NS_FAILED(rv)) return rv;
+
+ // Check for system target URI
+ rv = DenyAccessIfURIHasFlags(aTargetURI,
+ nsIProtocolHandler::URI_DANGEROUS_TO_LOAD);
+ if (NS_FAILED(rv)) {
+ // Deny access, since the origin principal is not system
+ if (reportErrors) {
+ ReportError(nullptr, errorTag, aSourceURI, aTargetURI);
+ }
+ return rv;
+ }
+
+ // Check for chrome target URI
+ bool hasFlags = false;
+ rv = NS_URIChainHasFlags(aTargetBaseURI,
+ nsIProtocolHandler::URI_IS_UI_RESOURCE,
+ &hasFlags);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (hasFlags) {
+ if (aFlags & nsIScriptSecurityManager::ALLOW_CHROME) {
+
+ // For now, don't change behavior for resource:// or moz-icon:// and
+ // just allow them.
+ if (!targetScheme.EqualsLiteral("chrome")) {
+ return NS_OK;
+ }
+
+ // Allow a URI_IS_UI_RESOURCE source to link to a URI_IS_UI_RESOURCE
+ // target if ALLOW_CHROME is set.
+ //
+ // ALLOW_CHROME is a flag that we pass on all loads _except_ docshell
+ // loads (since docshell loads run the loaded content with its origin
+ // principal). So we're effectively allowing resource://, chrome://,
+ // and moz-icon:// source URIs to load resource://, chrome://, and
+ // moz-icon:// files, so long as they're not loading it as a document.
+ bool sourceIsUIResource;
+ rv = NS_URIChainHasFlags(aSourceBaseURI,
+ nsIProtocolHandler::URI_IS_UI_RESOURCE,
+ &sourceIsUIResource);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (sourceIsUIResource) {
+ return NS_OK;
+ }
+
+ // Allow the load only if the chrome package is whitelisted.
+ nsCOMPtr<nsIXULChromeRegistry> reg(do_GetService(
+ NS_CHROMEREGISTRY_CONTRACTID));
+ if (reg) {
+ bool accessAllowed = false;
+ reg->AllowContentToAccess(aTargetBaseURI, &accessAllowed);
+ if (accessAllowed) {
+ return NS_OK;
+ }
+ }
+ }
+
+ // Special-case the hidden window: it's allowed to load
+ // URI_IS_UI_RESOURCE no matter what. Bug 1145470 tracks removing this.
+ nsAutoCString sourceSpec;
+ if (NS_SUCCEEDED(aSourceBaseURI->GetSpec(sourceSpec)) &&
+ sourceSpec.EqualsLiteral("resource://gre-resources/hiddenWindow.html")) {
+ return NS_OK;
+ }
+
+ if (reportErrors) {
+ ReportError(nullptr, errorTag, aSourceURI, aTargetURI);
+ }
+ return NS_ERROR_DOM_BAD_URI;
+ }
+
+ // Check for target URI pointing to a file
+ rv = NS_URIChainHasFlags(aTargetURI,
+ nsIProtocolHandler::URI_IS_LOCAL_FILE,
+ &hasFlags);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (hasFlags) {
+ // Allow domains that were whitelisted in the prefs. In 99.9% of cases,
+ // this array is empty.
+ for (nsIURI* uri : EnsureFileURIWhitelist()) {
+ if (EqualOrSubdomain(aSourceURI, uri)) {
+ return NS_OK;
+ }
+ }
+
+ // Allow chrome://
+ bool isChrome = false;
+ if (NS_SUCCEEDED(aSourceBaseURI->SchemeIs("chrome", &isChrome)) && isChrome) {
+ return NS_OK;
+ }
+
+ // Nothing else.
+ if (reportErrors) {
+ ReportError(nullptr, errorTag, aSourceURI, aTargetURI);
+ }
+ return NS_ERROR_DOM_BAD_URI;
+ }
+
+ // OK, everyone is allowed to load this, since unflagged handlers are
+ // deprecated but treated as URI_LOADABLE_BY_ANYONE. But check whether we
+ // need to warn. At some point we'll want to make this warning into an
+ // error and treat unflagged handlers as URI_DANGEROUS_TO_LOAD.
+ rv = NS_URIChainHasFlags(aTargetBaseURI,
+ nsIProtocolHandler::URI_LOADABLE_BY_ANYONE,
+ &hasFlags);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // NB: we also get here if the base URI is URI_LOADABLE_BY_SUBSUMERS,
+ // and none of the rest of the nested chain of URIs for aTargetURI
+ // prohibits the load, so avoid warning in that case:
+ bool hasSubsumersFlag = false;
+ rv = NS_URIChainHasFlags(aTargetBaseURI,
+ nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS,
+ &hasSubsumersFlag);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!hasFlags && !hasSubsumersFlag) {
+ nsXPIDLString message;
+ NS_ConvertASCIItoUTF16 ucsTargetScheme(targetScheme);
+ const char16_t* formatStrings[] = { ucsTargetScheme.get() };
+ rv = sStrBundle->
+ FormatStringFromName(u"ProtocolFlagError",
+ formatStrings,
+ ArrayLength(formatStrings),
+ getter_Copies(message));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIConsoleService> console(
+ do_GetService("@mozilla.org/consoleservice;1"));
+ NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
+
+ console->LogStringMessage(message.get());
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsScriptSecurityManager::ReportError(JSContext* cx, const nsAString& messageTag,
+ nsIURI* aSource, nsIURI* aTarget)
+{
+ nsresult rv;
+ NS_ENSURE_TRUE(aSource && aTarget, NS_ERROR_NULL_POINTER);
+
+ // Get the source URL spec
+ nsAutoCString sourceSpec;
+ rv = aSource->GetAsciiSpec(sourceSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the target URL spec
+ nsAutoCString targetSpec;
+ rv = aTarget->GetAsciiSpec(targetSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Localize the error message
+ nsXPIDLString message;
+ NS_ConvertASCIItoUTF16 ucsSourceSpec(sourceSpec);
+ NS_ConvertASCIItoUTF16 ucsTargetSpec(targetSpec);
+ const char16_t *formatStrings[] = { ucsSourceSpec.get(), ucsTargetSpec.get() };
+ rv = sStrBundle->FormatStringFromName(PromiseFlatString(messageTag).get(),
+ formatStrings,
+ ArrayLength(formatStrings),
+ getter_Copies(message));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If a JS context was passed in, set a JS exception.
+ // Otherwise, print the error message directly to the JS console
+ // and to standard output
+ if (cx)
+ {
+ SetPendingException(cx, message.get());
+ }
+ else // Print directly to the console
+ {
+ nsCOMPtr<nsIConsoleService> console(
+ do_GetService("@mozilla.org/consoleservice;1"));
+ NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
+
+ console->LogStringMessage(message.get());
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::CheckLoadURIStrWithPrincipal(nsIPrincipal* aPrincipal,
+ const nsACString& aTargetURIStr,
+ uint32_t aFlags)
+{
+ nsresult rv;
+ nsCOMPtr<nsIURI> target;
+ rv = NS_NewURI(getter_AddRefs(target), aTargetURIStr,
+ nullptr, nullptr, sIOService);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags);
+ if (rv == NS_ERROR_DOM_BAD_URI) {
+ // Don't warn because NS_ERROR_DOM_BAD_URI is one of the expected
+ // return values.
+ return rv;
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now start testing fixup -- since aTargetURIStr is a string, not
+ // an nsIURI, we may well end up fixing it up before loading.
+ // Note: This needs to stay in sync with the nsIURIFixup api.
+ nsCOMPtr<nsIURIFixup> fixup = do_GetService(NS_URIFIXUP_CONTRACTID);
+ if (!fixup) {
+ return rv;
+ }
+
+ uint32_t flags[] = {
+ nsIURIFixup::FIXUP_FLAG_NONE,
+ nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS,
+ nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP,
+ nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
+ nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP |
+ nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI
+ };
+
+ for (uint32_t i = 0; i < ArrayLength(flags); ++i) {
+ rv = fixup->CreateFixupURI(aTargetURIStr, flags[i], nullptr,
+ getter_AddRefs(target));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags);
+ if (rv == NS_ERROR_DOM_BAD_URI) {
+ // Don't warn because NS_ERROR_DOM_BAD_URI is one of the expected
+ // return values.
+ return rv;
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return rv;
+}
+
+///////////////// Principals ///////////////////////
+
+NS_IMETHODIMP
+nsScriptSecurityManager::GetSystemPrincipal(nsIPrincipal **result)
+{
+ NS_ADDREF(*result = mSystemPrincipal);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::GetNoAppCodebasePrincipal(nsIURI* aURI,
+ nsIPrincipal** aPrincipal)
+{
+ PrincipalOriginAttributes attrs(NO_APP_ID, false);
+ nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
+ prin.forget(aPrincipal);
+ return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::GetCodebasePrincipal(nsIURI* aURI,
+ nsIPrincipal** aPrincipal)
+{
+ return GetNoAppCodebasePrincipal(aURI, aPrincipal);
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::CreateCodebasePrincipal(nsIURI* aURI, JS::Handle<JS::Value> aOriginAttributes,
+ JSContext* aCx, nsIPrincipal** aPrincipal)
+{
+ PrincipalOriginAttributes attrs;
+ if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
+ prin.forget(aPrincipal);
+ return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::CreateCodebasePrincipalFromOrigin(const nsACString& aOrigin,
+ nsIPrincipal** aPrincipal)
+{
+ if (StringBeginsWith(aOrigin, NS_LITERAL_CSTRING("["))) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (StringBeginsWith(aOrigin, NS_LITERAL_CSTRING(NS_NULLPRINCIPAL_SCHEME ":"))) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aOrigin);
+ prin.forget(aPrincipal);
+ return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::CreateNullPrincipal(JS::Handle<JS::Value> aOriginAttributes,
+ JSContext* aCx, nsIPrincipal** aPrincipal)
+{
+ PrincipalOriginAttributes attrs;
+ if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ nsCOMPtr<nsIPrincipal> prin = nsNullPrincipal::Create(attrs);
+ prin.forget(aPrincipal);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::GetAppCodebasePrincipal(nsIURI* aURI,
+ uint32_t aAppId,
+ bool aInIsolatedMozBrowser,
+ nsIPrincipal** aPrincipal)
+{
+ NS_ENSURE_TRUE(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
+ NS_ERROR_INVALID_ARG);
+
+ PrincipalOriginAttributes attrs(aAppId, aInIsolatedMozBrowser);
+ nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
+ prin.forget(aPrincipal);
+ return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::
+ GetLoadContextCodebasePrincipal(nsIURI* aURI,
+ nsILoadContext* aLoadContext,
+ nsIPrincipal** aPrincipal)
+{
+ NS_ENSURE_STATE(aLoadContext);
+ DocShellOriginAttributes docShellAttrs;
+ bool result = aLoadContext->GetOriginAttributes(docShellAttrs);;
+ NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
+
+ PrincipalOriginAttributes attrs;
+ attrs.InheritFromDocShellToDoc(docShellAttrs, aURI);
+
+ nsresult rv = MaybeSetAddonIdFromURI(attrs, aURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
+ prin.forget(aPrincipal);
+ return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::GetDocShellCodebasePrincipal(nsIURI* aURI,
+ nsIDocShell* aDocShell,
+ nsIPrincipal** aPrincipal)
+{
+ PrincipalOriginAttributes attrs;
+ attrs.InheritFromDocShellToDoc(nsDocShell::Cast(aDocShell)->GetOriginAttributes(), aURI);
+
+ nsresult rv = MaybeSetAddonIdFromURI(attrs, aURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
+ prin.forget(aPrincipal);
+ return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
+}
+
+// static
+nsIPrincipal*
+nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj)
+{
+ JSCompartment *compartment = js::GetObjectCompartment(aObj);
+ JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
+ return nsJSPrincipals::get(principals);
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::CanCreateWrapper(JSContext *cx,
+ const nsIID &aIID,
+ nsISupports *aObj,
+ nsIClassInfo *aClassInfo)
+{
+// XXX Special case for nsIXPCException ?
+ ClassInfoData objClassInfo = ClassInfoData(aClassInfo, nullptr);
+ if (objClassInfo.IsDOMClass())
+ {
+ return NS_OK;
+ }
+
+ // We give remote-XUL whitelisted domains a free pass here. See bug 932906.
+ if (!xpc::AllowContentXBLScope(js::GetContextCompartment(cx)))
+ {
+ return NS_OK;
+ }
+
+ if (nsContentUtils::IsCallerChrome())
+ {
+ return NS_OK;
+ }
+
+ //-- Access denied, report an error
+ NS_ConvertUTF8toUTF16 strName("CreateWrapperDenied");
+ nsAutoCString origin;
+ nsIPrincipal* subjectPrincipal = nsContentUtils::SubjectPrincipal();
+ GetPrincipalDomainOrigin(subjectPrincipal, origin);
+ NS_ConvertUTF8toUTF16 originUnicode(origin);
+ NS_ConvertUTF8toUTF16 classInfoName(objClassInfo.GetName());
+ const char16_t* formatStrings[] = {
+ classInfoName.get(),
+ originUnicode.get()
+ };
+ uint32_t length = ArrayLength(formatStrings);
+ if (originUnicode.IsEmpty()) {
+ --length;
+ } else {
+ strName.AppendLiteral("ForOrigin");
+ }
+ nsXPIDLString errorMsg;
+ nsresult rv = sStrBundle->FormatStringFromName(strName.get(),
+ formatStrings,
+ length,
+ getter_Copies(errorMsg));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ SetPendingException(cx, errorMsg.get());
+ return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::CanCreateInstance(JSContext *cx,
+ const nsCID &aCID)
+{
+ if (nsContentUtils::IsCallerChrome()) {
+ return NS_OK;
+ }
+
+ //-- Access denied, report an error
+ nsAutoCString errorMsg("Permission denied to create instance of class. CID=");
+ char cidStr[NSID_LENGTH];
+ aCID.ToProvidedString(cidStr);
+ errorMsg.Append(cidStr);
+ SetPendingExceptionASCII(cx, errorMsg.get());
+ return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::CanGetService(JSContext *cx,
+ const nsCID &aCID)
+{
+ if (nsContentUtils::IsCallerChrome()) {
+ return NS_OK;
+ }
+
+ //-- Access denied, report an error
+ nsAutoCString errorMsg("Permission denied to get service. CID=");
+ char cidStr[NSID_LENGTH];
+ aCID.ToProvidedString(cidStr);
+ errorMsg.Append(cidStr);
+ SetPendingExceptionASCII(cx, errorMsg.get());
+ return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
+}
+
+/////////////////////////////////////
+// Method implementing nsIObserver //
+/////////////////////////////////////
+const char sJSEnabledPrefName[] = "javascript.enabled";
+const char sFileOriginPolicyPrefName[] =
+ "security.fileuri.strict_origin_policy";
+
+static const char* kObservedPrefs[] = {
+ sJSEnabledPrefName,
+ sFileOriginPolicyPrefName,
+ "capability.policy.",
+ nullptr
+};
+
+
+NS_IMETHODIMP
+nsScriptSecurityManager::Observe(nsISupports* aObject, const char* aTopic,
+ const char16_t* aMessage)
+{
+ ScriptSecurityPrefChanged();
+ return NS_OK;
+}
+
+/////////////////////////////////////////////
+// Constructor, Destructor, Initialization //
+/////////////////////////////////////////////
+nsScriptSecurityManager::nsScriptSecurityManager(void)
+ : mPrefInitialized(false)
+ , mIsJavaScriptEnabled(false)
+{
+ static_assert(sizeof(intptr_t) == sizeof(void*),
+ "intptr_t and void* have different lengths on this platform. "
+ "This may cause a security failure with the SecurityLevel union.");
+}
+
+nsresult nsScriptSecurityManager::Init()
+{
+ nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ InitPrefs();
+
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ if (!bundleService)
+ return NS_ERROR_FAILURE;
+
+ rv = bundleService->CreateBundle("chrome://global/locale/security/caps.properties", &sStrBundle);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Create our system principal singleton
+ RefPtr<nsSystemPrincipal> system = new nsSystemPrincipal();
+
+ mSystemPrincipal = system;
+
+ //-- Register security check callback in the JS engine
+ // Currently this is used to control access to function.caller
+ sContext = danger::GetJSContext();
+
+ static const JSSecurityCallbacks securityCallbacks = {
+ ContentSecurityPolicyPermitsJSAction,
+ JSPrincipalsSubsume,
+ };
+
+ MOZ_ASSERT(!JS_GetSecurityCallbacks(sContext));
+ JS_SetSecurityCallbacks(sContext, &securityCallbacks);
+ JS_InitDestroyPrincipalsCallback(sContext, nsJSPrincipals::Destroy);
+
+ JS_SetTrustedPrincipals(sContext, system);
+
+ return NS_OK;
+}
+
+static StaticRefPtr<nsScriptSecurityManager> gScriptSecMan;
+
+nsScriptSecurityManager::~nsScriptSecurityManager(void)
+{
+ Preferences::RemoveObservers(this, kObservedPrefs);
+ if (mDomainPolicy) {
+ mDomainPolicy->Deactivate();
+ }
+ // ContentChild might hold a reference to the domain policy,
+ // and it might release it only after the security manager is
+ // gone. But we can still assert this for the main process.
+ MOZ_ASSERT_IF(XRE_IsParentProcess(),
+ !mDomainPolicy);
+}
+
+void
+nsScriptSecurityManager::Shutdown()
+{
+ if (sContext) {
+ JS_SetSecurityCallbacks(sContext, nullptr);
+ JS_SetTrustedPrincipals(sContext, nullptr);
+ sContext = nullptr;
+ }
+
+ NS_IF_RELEASE(sIOService);
+ NS_IF_RELEASE(sStrBundle);
+}
+
+nsScriptSecurityManager *
+nsScriptSecurityManager::GetScriptSecurityManager()
+{
+ return gScriptSecMan;
+}
+
+/* static */ void
+nsScriptSecurityManager::InitStatics()
+{
+ RefPtr<nsScriptSecurityManager> ssManager = new nsScriptSecurityManager();
+ nsresult rv = ssManager->Init();
+ if (NS_FAILED(rv)) {
+ MOZ_CRASH("ssManager->Init() failed");
+ }
+
+ ClearOnShutdown(&gScriptSecMan);
+ gScriptSecMan = ssManager;
+}
+
+// Currently this nsGenericFactory constructor is used only from FastLoad
+// (XPCOM object deserialization) code, when "creating" the system principal
+// singleton.
+nsSystemPrincipal *
+nsScriptSecurityManager::SystemPrincipalSingletonConstructor()
+{
+ nsIPrincipal *sysprin = nullptr;
+ if (gScriptSecMan)
+ NS_ADDREF(sysprin = gScriptSecMan->mSystemPrincipal);
+ return static_cast<nsSystemPrincipal*>(sysprin);
+}
+
+struct IsWhitespace {
+ static bool Test(char aChar) { return NS_IsAsciiWhitespace(aChar); };
+};
+struct IsWhitespaceOrComma {
+ static bool Test(char aChar) { return aChar == ',' || NS_IsAsciiWhitespace(aChar); };
+};
+
+template <typename Predicate>
+uint32_t SkipPast(const nsCString& str, uint32_t base)
+{
+ while (base < str.Length() && Predicate::Test(str[base])) {
+ ++base;
+ }
+ return base;
+}
+
+template <typename Predicate>
+uint32_t SkipUntil(const nsCString& str, uint32_t base)
+{
+ while (base < str.Length() && !Predicate::Test(str[base])) {
+ ++base;
+ }
+ return base;
+}
+
+inline void
+nsScriptSecurityManager::ScriptSecurityPrefChanged()
+{
+ MOZ_ASSERT(mPrefInitialized);
+ mIsJavaScriptEnabled =
+ Preferences::GetBool(sJSEnabledPrefName, mIsJavaScriptEnabled);
+ sStrictFileOriginPolicy =
+ Preferences::GetBool(sFileOriginPolicyPrefName, false);
+ mFileURIWhitelist.reset();
+}
+
+void
+nsScriptSecurityManager::AddSitesToFileURIWhitelist(const nsCString& aSiteList)
+{
+ for (uint32_t base = SkipPast<IsWhitespace>(aSiteList, 0), bound = 0;
+ base < aSiteList.Length();
+ base = SkipPast<IsWhitespace>(aSiteList, bound))
+ {
+ // Grab the current site.
+ bound = SkipUntil<IsWhitespace>(aSiteList, base);
+ nsAutoCString site(Substring(aSiteList, base, bound - base));
+
+ // Check if the URI is schemeless. If so, add both http and https.
+ nsAutoCString unused;
+ if (NS_FAILED(sIOService->ExtractScheme(site, unused))) {
+ AddSitesToFileURIWhitelist(NS_LITERAL_CSTRING("http://") + site);
+ AddSitesToFileURIWhitelist(NS_LITERAL_CSTRING("https://") + site);
+ continue;
+ }
+
+ // Convert it to a URI and add it to our list.
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), site, nullptr, nullptr, sIOService);
+ if (NS_SUCCEEDED(rv)) {
+ mFileURIWhitelist.ref().AppendElement(uri);
+ } else {
+ nsCOMPtr<nsIConsoleService> console(do_GetService("@mozilla.org/consoleservice;1"));
+ if (console) {
+ nsAutoString msg = NS_LITERAL_STRING("Unable to to add site to file:// URI whitelist: ") +
+ NS_ConvertASCIItoUTF16(site);
+ console->LogStringMessage(msg.get());
+ }
+ }
+ }
+}
+
+nsresult
+nsScriptSecurityManager::InitPrefs()
+{
+ nsIPrefBranch* branch = Preferences::GetRootBranch();
+ NS_ENSURE_TRUE(branch, NS_ERROR_FAILURE);
+
+ mPrefInitialized = true;
+
+ // Set the initial value of the "javascript.enabled" prefs
+ ScriptSecurityPrefChanged();
+
+ // set observer callbacks in case the value of the prefs change
+ Preferences::AddStrongObservers(this, kObservedPrefs);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::GetDomainPolicyActive(bool *aRv)
+{
+ *aRv = !!mDomainPolicy;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::ActivateDomainPolicy(nsIDomainPolicy** aRv)
+{
+ if (!XRE_IsParentProcess()) {
+ return NS_ERROR_SERVICE_NOT_AVAILABLE;
+ }
+
+ return ActivateDomainPolicyInternal(aRv);
+}
+
+NS_IMETHODIMP
+nsScriptSecurityManager::ActivateDomainPolicyInternal(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 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;
+}
+
+void
+nsScriptSecurityManager::CloneDomainPolicy(DomainPolicyClone* aClone)
+{
+ MOZ_ASSERT(aClone);
+ if (mDomainPolicy) {
+ mDomainPolicy->CloneDomainPolicy(aClone);
+ } else {
+ aClone->active() = false;
+ }
+}
+
+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;
+}
+
+const nsTArray<nsCOMPtr<nsIURI>>&
+nsScriptSecurityManager::EnsureFileURIWhitelist()
+{
+ if (mFileURIWhitelist.isSome()) {
+ return mFileURIWhitelist.ref();
+ }
+
+ //
+ // Rebuild the set of principals for which we allow file:// URI loads. This
+ // implements a small subset of an old pref-based CAPS people that people
+ // have come to depend on. See bug 995943.
+ //
+
+ mFileURIWhitelist.emplace();
+ auto policies = mozilla::Preferences::GetCString("capability.policy.policynames");
+ for (uint32_t base = SkipPast<IsWhitespaceOrComma>(policies, 0), bound = 0;
+ base < policies.Length();
+ base = SkipPast<IsWhitespaceOrComma>(policies, bound))
+ {
+ // Grab the current policy name.
+ bound = SkipUntil<IsWhitespaceOrComma>(policies, base);
+ auto policyName = Substring(policies, base, bound - base);
+
+ // Figure out if this policy allows loading file:// URIs. If not, we can skip it.
+ nsCString checkLoadURIPrefName = NS_LITERAL_CSTRING("capability.policy.") +
+ policyName +
+ NS_LITERAL_CSTRING(".checkloaduri.enabled");
+ if (!Preferences::GetString(checkLoadURIPrefName.get()).LowerCaseEqualsLiteral("allaccess")) {
+ continue;
+ }
+
+ // Grab the list of domains associated with this policy.
+ nsCString domainPrefName = NS_LITERAL_CSTRING("capability.policy.") +
+ policyName +
+ NS_LITERAL_CSTRING(".sites");
+ auto siteList = Preferences::GetCString(domainPrefName.get());
+ AddSitesToFileURIWhitelist(siteList);
+ }
+
+ return mFileURIWhitelist.ref();
+}
diff --git a/caps/nsScriptSecurityManager.h b/caps/nsScriptSecurityManager.h
new file mode 100644
index 000000000..5dc0b393a
--- /dev/null
+++ b/caps/nsScriptSecurityManager.h
@@ -0,0 +1,162 @@
+/* -*- 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/. */
+
+#ifndef nsScriptSecurityManager_h__
+#define nsScriptSecurityManager_h__
+
+#include "nsIScriptSecurityManager.h"
+
+#include "nsIAddonPolicyService.h"
+#include "mozilla/Maybe.h"
+#include "nsIAddonPolicyService.h"
+#include "nsIPrincipal.h"
+#include "nsCOMPtr.h"
+#include "nsIObserver.h"
+#include "nsServiceManagerUtils.h"
+#include "plstr.h"
+#include "js/TypeDecls.h"
+
+#include <stdint.h>
+
+class nsCString;
+class nsIIOService;
+class nsIStringBundle;
+class nsSystemPrincipal;
+
+namespace mozilla {
+class PrincipalOriginAttributes;
+} // namespace mozilla
+
+/////////////////////////////
+// nsScriptSecurityManager //
+/////////////////////////////
+#define NS_SCRIPTSECURITYMANAGER_CID \
+{ 0x7ee2a4c0, 0x4b93, 0x17d3, \
+{ 0xba, 0x18, 0x00, 0x60, 0xb0, 0xf1, 0x99, 0xa2 }}
+
+class nsScriptSecurityManager final : public nsIScriptSecurityManager,
+ public nsIObserver
+{
+public:
+ static void Shutdown();
+
+ NS_DEFINE_STATIC_CID_ACCESSOR(NS_SCRIPTSECURITYMANAGER_CID)
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISCRIPTSECURITYMANAGER
+ NS_DECL_NSIOBSERVER
+
+ static nsScriptSecurityManager*
+ GetScriptSecurityManager();
+
+ // Invoked exactly once, by XPConnect.
+ static void InitStatics();
+
+ static nsSystemPrincipal*
+ SystemPrincipalSingletonConstructor();
+
+ /**
+ * Utility method for comparing two URIs. For security purposes, two URIs
+ * are equivalent if their schemes, hosts, and ports (if any) match. This
+ * method returns true if aSubjectURI and aObjectURI have the same origin,
+ * false otherwise.
+ */
+ static bool SecurityCompareURIs(nsIURI* aSourceURI, nsIURI* aTargetURI);
+ static uint32_t SecurityHashURI(nsIURI* aURI);
+
+ static uint16_t AppStatusForPrincipal(nsIPrincipal *aPrin);
+
+ static nsresult
+ ReportError(JSContext* cx, const nsAString& messageTag,
+ nsIURI* aSource, nsIURI* aTarget);
+
+ static uint32_t
+ HashPrincipalByOrigin(nsIPrincipal* aPrincipal);
+
+ static bool
+ GetStrictFileOriginPolicy()
+ {
+ return sStrictFileOriginPolicy;
+ }
+
+ void DeactivateDomainPolicy();
+
+private:
+
+ // GetScriptSecurityManager is the only call that can make one
+ nsScriptSecurityManager();
+ virtual ~nsScriptSecurityManager();
+
+ // Decides, based on CSP, whether or not eval() and stuff can be executed.
+ static bool
+ ContentSecurityPolicyPermitsJSAction(JSContext *cx);
+
+ static bool
+ JSPrincipalsSubsume(JSPrincipals *first, JSPrincipals *second);
+
+ // Returns null if a principal cannot be found; generally callers
+ // should error out at that point.
+ static nsIPrincipal* doGetObjectPrincipal(JSObject* obj);
+
+ nsresult
+ Init();
+
+ nsresult
+ InitPrefs();
+
+ inline void
+ ScriptSecurityPrefChanged();
+
+ inline void
+ AddSitesToFileURIWhitelist(const nsCString& aSiteList);
+
+ // If aURI is a moz-extension:// URI, set mAddonId to the associated addon.
+ nsresult MaybeSetAddonIdFromURI(mozilla::PrincipalOriginAttributes& aAttrs, nsIURI* aURI);
+
+ nsresult GetChannelResultPrincipal(nsIChannel* aChannel,
+ nsIPrincipal** aPrincipal,
+ bool aIgnoreSandboxing);
+
+ nsresult
+ CheckLoadURIFlags(nsIURI* aSourceURI, nsIURI* aTargetURI, nsIURI* aSourceBaseURI,
+ nsIURI* aTargetBaseURI, uint32_t aFlags);
+
+ // Returns the file URI whitelist, initializing it if it has not been
+ // initialized.
+ const nsTArray<nsCOMPtr<nsIURI>>& EnsureFileURIWhitelist();
+
+ nsCOMPtr<nsIPrincipal> mSystemPrincipal;
+ bool mPrefInitialized;
+ bool mIsJavaScriptEnabled;
+
+ // List of URIs whose domains and sub-domains are whitelisted to allow
+ // access to file: URIs. Lazily initialized; isNothing() when not yet
+ // initialized.
+ mozilla::Maybe<nsTArray<nsCOMPtr<nsIURI>>> mFileURIWhitelist;
+
+ // This machinery controls new-style domain policies. The old-style
+ // policy machinery will be removed soon.
+ nsCOMPtr<nsIDomainPolicy> mDomainPolicy;
+
+ // Cached addon policy service. We can't generate this in Init() because
+ // that's too early to get a service.
+ mozilla::Maybe<nsCOMPtr<nsIAddonPolicyService>> mAddonPolicyService;
+ nsIAddonPolicyService* GetAddonPolicyService()
+ {
+ if (mAddonPolicyService.isNothing()) {
+ mAddonPolicyService.emplace(do_GetService("@mozilla.org/addons/policy-service;1"));
+ }
+ return mAddonPolicyService.ref();
+ }
+
+ static bool sStrictFileOriginPolicy;
+
+ static nsIIOService *sIOService;
+ static nsIStringBundle *sStrBundle;
+ static JSContext *sContext;
+};
+
+#endif // nsScriptSecurityManager_h__
diff --git a/caps/nsSystemPrincipal.cpp b/caps/nsSystemPrincipal.cpp
new file mode 100644
index 000000000..7bfa20efc
--- /dev/null
+++ b/caps/nsSystemPrincipal.cpp
@@ -0,0 +1,141 @@
+/* -*- 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/. */
+
+/* The privileged system principal. */
+
+#include "nscore.h"
+#include "nsSystemPrincipal.h"
+#include "nsIComponentManager.h"
+#include "nsIServiceManager.h"
+#include "nsIURL.h"
+#include "nsCOMPtr.h"
+#include "nsXPIDLString.h"
+#include "nsReadableUtils.h"
+#include "nsCRT.h"
+#include "nsString.h"
+#include "nsIClassInfoImpl.h"
+#include "nsIScriptSecurityManager.h"
+#include "pratom.h"
+
+NS_IMPL_CLASSINFO(nsSystemPrincipal, nullptr,
+ nsIClassInfo::SINGLETON | nsIClassInfo::MAIN_THREAD_ONLY,
+ NS_SYSTEMPRINCIPAL_CID)
+NS_IMPL_QUERY_INTERFACE_CI(nsSystemPrincipal,
+ nsIPrincipal,
+ nsISerializable)
+NS_IMPL_CI_INTERFACE_GETTER(nsSystemPrincipal,
+ nsIPrincipal,
+ nsISerializable)
+
+#define SYSTEM_PRINCIPAL_SPEC "[System Principal]"
+
+nsresult
+nsSystemPrincipal::GetScriptLocation(nsACString &aStr)
+{
+ aStr.AssignLiteral(SYSTEM_PRINCIPAL_SPEC);
+ return NS_OK;
+}
+
+///////////////////////////////////////
+// Methods implementing nsIPrincipal //
+///////////////////////////////////////
+
+NS_IMETHODIMP
+nsSystemPrincipal::GetHashValue(uint32_t *result)
+{
+ *result = NS_PTR_TO_INT32(this);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSystemPrincipal::GetURI(nsIURI** aURI)
+{
+ *aURI = nullptr;
+ return NS_OK;
+}
+
+nsresult
+nsSystemPrincipal::GetOriginInternal(nsACString& aOrigin)
+{
+ aOrigin.AssignLiteral(SYSTEM_PRINCIPAL_SPEC);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSystemPrincipal::GetCsp(nsIContentSecurityPolicy** aCsp)
+{
+ *aCsp = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSystemPrincipal::SetCsp(nsIContentSecurityPolicy* aCsp)
+{
+ // Never destroy an existing CSP on the principal.
+ // This method should only be called in rare cases.
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsSystemPrincipal::EnsureCSP(nsIDOMDocument* aDocument,
+ nsIContentSecurityPolicy** aCSP)
+{
+ // CSP on a system principal makes no sense
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsSystemPrincipal::GetPreloadCsp(nsIContentSecurityPolicy** aPreloadCSP)
+{
+ *aPreloadCSP = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSystemPrincipal::EnsurePreloadCSP(nsIDOMDocument* aDocument,
+ nsIContentSecurityPolicy** aPreloadCSP)
+{
+ // CSP on a system principal makes no sense
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSystemPrincipal::GetDomain(nsIURI** aDomain)
+{
+ *aDomain = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSystemPrincipal::SetDomain(nsIURI* aDomain)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSystemPrincipal::GetBaseDomain(nsACString& aBaseDomain)
+{
+ // No base domain for chrome.
+ return NS_OK;
+}
+
+//////////////////////////////////////////
+// Methods implementing nsISerializable //
+//////////////////////////////////////////
+
+NS_IMETHODIMP
+nsSystemPrincipal::Read(nsIObjectInputStream* aStream)
+{
+ // no-op: CID is sufficient to identify the mSystemPrincipal singleton
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSystemPrincipal::Write(nsIObjectOutputStream* aStream)
+{
+ // no-op: CID is sufficient to identify the mSystemPrincipal singleton
+ return NS_OK;
+}
diff --git a/caps/nsSystemPrincipal.h b/caps/nsSystemPrincipal.h
new file mode 100644
index 000000000..b7466e70a
--- /dev/null
+++ b/caps/nsSystemPrincipal.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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/. */
+
+/* The privileged system principal. */
+
+#ifndef nsSystemPrincipal_h__
+#define nsSystemPrincipal_h__
+
+#include "nsIPrincipal.h"
+#include "nsJSPrincipals.h"
+
+#include "mozilla/BasePrincipal.h"
+
+#define NS_SYSTEMPRINCIPAL_CID \
+{ 0x4a6212db, 0xaccb, 0x11d3, \
+{ 0xb7, 0x65, 0x0, 0x60, 0xb0, 0xb6, 0xce, 0xcb }}
+#define NS_SYSTEMPRINCIPAL_CONTRACTID "@mozilla.org/systemprincipal;1"
+
+
+class nsSystemPrincipal final : public mozilla::BasePrincipal
+{
+public:
+ NS_DECL_NSISERIALIZABLE
+ NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
+ NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
+ NS_IMETHOD GetURI(nsIURI** aURI) override;
+ NS_IMETHOD GetDomain(nsIURI** aDomain) override;
+ NS_IMETHOD SetDomain(nsIURI* aDomain) override;
+ NS_IMETHOD GetCsp(nsIContentSecurityPolicy** aCsp) override;
+ NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
+ NS_IMETHOD EnsureCSP(nsIDOMDocument* aDocument, nsIContentSecurityPolicy** aCSP) override;
+ NS_IMETHOD GetPreloadCsp(nsIContentSecurityPolicy** aPreloadCSP) override;
+ NS_IMETHOD EnsurePreloadCSP(nsIDOMDocument* aDocument, nsIContentSecurityPolicy** aCSP) override;
+ NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
+ nsresult GetOriginInternal(nsACString& aOrigin) override;
+
+ nsSystemPrincipal() {}
+
+ virtual nsresult GetScriptLocation(nsACString &aStr) override;
+
+protected:
+ virtual ~nsSystemPrincipal(void) {}
+
+ bool SubsumesInternal(nsIPrincipal *aOther, DocumentDomainConsideration aConsideration) override
+ {
+ return true;
+ }
+
+ bool MayLoadInternal(nsIURI* aURI) override
+ {
+ return true;
+ }
+
+ PrincipalKind Kind() override { return eSystemPrincipal; }
+};
+
+#endif // nsSystemPrincipal_h__
diff --git a/caps/tests/gtest/TestOriginAttributes.cpp b/caps/tests/gtest/TestOriginAttributes.cpp
new file mode 100644
index 000000000..e11bf28eb
--- /dev/null
+++ b/caps/tests/gtest/TestOriginAttributes.cpp
@@ -0,0 +1,38 @@
+/* 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 "gtest/gtest.h"
+#include "mozilla/BasePrincipal.h"
+
+using mozilla::PrincipalOriginAttributes;
+
+static void
+TestSuffix(const PrincipalOriginAttributes& attrs)
+{
+ nsAutoCString suffix;
+ attrs.CreateSuffix(suffix);
+
+ PrincipalOriginAttributes attrsFromSuffix;
+ bool success = attrsFromSuffix.PopulateFromSuffix(suffix);
+ EXPECT_TRUE(success);
+
+ EXPECT_EQ(attrs, attrsFromSuffix);
+}
+
+TEST(PrincipalOriginAttributes, Suffix_default)
+{
+ PrincipalOriginAttributes attrs;
+ TestSuffix(attrs);
+}
+
+TEST(PrincipalOriginAttributes, Suffix_appId_inIsolatedMozBrowser)
+{
+ PrincipalOriginAttributes attrs(1, true);
+ TestSuffix(attrs);
+}
+
+TEST(PrincipalOriginAttributes, Suffix_maxAppId_inIsolatedMozBrowser)
+{
+ PrincipalOriginAttributes attrs(4294967295, true);
+ TestSuffix(attrs);
+}
diff --git a/caps/tests/gtest/moz.build b/caps/tests/gtest/moz.build
new file mode 100644
index 000000000..26447f54c
--- /dev/null
+++ b/caps/tests/gtest/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; 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/.
+
+UNIFIED_SOURCES += [
+ 'TestOriginAttributes.cpp'
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul-gtest'
diff --git a/caps/tests/mochitest/browser.ini b/caps/tests/mochitest/browser.ini
new file mode 100644
index 000000000..d8a1278f9
--- /dev/null
+++ b/caps/tests/mochitest/browser.ini
@@ -0,0 +1 @@
+[browser_checkloaduri.js]
diff --git a/caps/tests/mochitest/browser_checkloaduri.js b/caps/tests/mochitest/browser_checkloaduri.js
new file mode 100644
index 000000000..24a97c1c4
--- /dev/null
+++ b/caps/tests/mochitest/browser_checkloaduri.js
@@ -0,0 +1,292 @@
+"use strict";
+
+let ssm = Services.scriptSecurityManager;
+// This will show a directory listing, but we never actually load these so that's OK.
+const kDummyPage = getRootDirectory(gTestPath);
+
+const kAboutPagesRegistered = Promise.all([
+ BrowserTestUtils.registerAboutPage(
+ registerCleanupFunction, "test-chrome-privs", kDummyPage,
+ Ci.nsIAboutModule.ALLOW_SCRIPT),
+ BrowserTestUtils.registerAboutPage(
+ registerCleanupFunction, "test-chrome-privs2", kDummyPage,
+ Ci.nsIAboutModule.ALLOW_SCRIPT),
+ BrowserTestUtils.registerAboutPage(
+ registerCleanupFunction, "test-unknown-linkable", kDummyPage,
+ Ci.nsIAboutModule.MAKE_LINKABLE | Ci.nsIAboutModule.ALLOW_SCRIPT),
+ BrowserTestUtils.registerAboutPage(
+ registerCleanupFunction, "test-unknown-linkable2", kDummyPage,
+ Ci.nsIAboutModule.MAKE_LINKABLE | Ci.nsIAboutModule.ALLOW_SCRIPT),
+ BrowserTestUtils.registerAboutPage(
+ registerCleanupFunction, "test-unknown-unlinkable", kDummyPage,
+ Ci.nsIAboutModule.ALLOW_SCRIPT),
+ BrowserTestUtils.registerAboutPage(
+ registerCleanupFunction, "test-unknown-unlinkable2", kDummyPage,
+ Ci.nsIAboutModule.ALLOW_SCRIPT),
+ BrowserTestUtils.registerAboutPage(
+ registerCleanupFunction, "test-content-unlinkable", kDummyPage,
+ Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | Ci.nsIAboutModule.ALLOW_SCRIPT),
+ BrowserTestUtils.registerAboutPage(
+ registerCleanupFunction, "test-content-unlinkable2", kDummyPage,
+ Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | Ci.nsIAboutModule.ALLOW_SCRIPT),
+ BrowserTestUtils.registerAboutPage(
+ registerCleanupFunction, "test-content-linkable", kDummyPage,
+ Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | Ci.nsIAboutModule.MAKE_LINKABLE |
+ Ci.nsIAboutModule.ALLOW_SCRIPT),
+ BrowserTestUtils.registerAboutPage(
+ registerCleanupFunction, "test-content-linkable2", kDummyPage,
+ Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT | Ci.nsIAboutModule.MAKE_LINKABLE |
+ Ci.nsIAboutModule.ALLOW_SCRIPT),
+]);
+
+const URLs = new Map([
+ ["http://www.example.com", [
+ // For each of these entries, the booleans represent whether the parent URI can:
+ // - load them
+ // - load them without principal inheritance
+ // - whether the URI can be created at all (some protocol handlers will
+ // refuse to create certain variants)
+ ["http://www.example2.com", true, true, true],
+ ["feed:http://www.example2.com", false, false, true],
+ ["https://www.example2.com", true, true, true],
+ ["chrome://foo/content/bar.xul", false, false, true],
+ ["feed:chrome://foo/content/bar.xul", false, false, false],
+ ["view-source:http://www.example2.com", false, false, true],
+ ["view-source:https://www.example2.com", false, false, true],
+ ["view-source:feed:http://www.example2.com", false, false, true],
+ ["feed:view-source:http://www.example2.com", false, false, false],
+ ["data:text/html,Hi", true, false, true],
+ ["view-source:data:text/html,Hi", false, false, true],
+ ["javascript:alert('hi')", true, false, true],
+ ["moz://a", false, false, true],
+ ["about:test-chrome-privs", false, false, true],
+ ["about:test-unknown-unlinkable", false, false, true],
+ ["about:test-content-unlinkable", false, false, true],
+ ["about:test-content-linkable", true, true, true],
+ // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it:
+ ["about:test-unknown-linkable", false, false, true],
+ ]],
+ ["feed:http://www.example.com", [
+ ["http://www.example2.com", true, true, true],
+ ["feed:http://www.example2.com", true, true, true],
+ ["https://www.example2.com", true, true, true],
+ ["feed:https://www.example2.com", true, true, true],
+ ["chrome://foo/content/bar.xul", false, false, true],
+ ["feed:chrome://foo/content/bar.xul", false, false, false],
+ ["view-source:http://www.example2.com", false, false, true],
+ ["view-source:https://www.example2.com", false, false, true],
+ ["view-source:feed:http://www.example2.com", false, false, true],
+ ["feed:view-source:http://www.example2.com", false, false, false],
+ ["data:text/html,Hi", true, false, true],
+ ["view-source:data:text/html,Hi", false, false, true],
+ ["javascript:alert('hi')", true, false, true],
+ ["moz://a", false, false, true],
+ ["about:test-chrome-privs", false, false, true],
+ ["about:test-unknown-unlinkable", false, false, true],
+ ["about:test-content-unlinkable", false, false, true],
+ ["about:test-content-linkable", true, true, true],
+ // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it:
+ ["about:test-unknown-linkable", false, false, true],
+ ]],
+ ["view-source:http://www.example.com", [
+ ["http://www.example2.com", true, true, true],
+ ["feed:http://www.example2.com", false, false, true],
+ ["https://www.example2.com", true, true, true],
+ ["feed:https://www.example2.com", false, false, true],
+ ["chrome://foo/content/bar.xul", false, false, true],
+ ["feed:chrome://foo/content/bar.xul", false, false, false],
+ ["view-source:http://www.example2.com", true, true, true],
+ ["view-source:https://www.example2.com", true, true, true],
+ ["view-source:feed:http://www.example2.com", false, false, true],
+ ["feed:view-source:http://www.example2.com", false, false, false],
+ ["data:text/html,Hi", true, false, true],
+ ["view-source:data:text/html,Hi", true, false, true],
+ ["javascript:alert('hi')", true, false, true],
+ ["moz://a", false, false, true],
+ ["about:test-chrome-privs", false, false, true],
+ ["about:test-unknown-unlinkable", false, false, true],
+ ["about:test-content-unlinkable", false, false, true],
+ ["about:test-content-linkable", true, true, true],
+ // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it:
+ ["about:test-unknown-linkable", false, false, true],
+ ]],
+ // about: related tests.
+ ["about:test-chrome-privs", [
+ ["about:test-chrome-privs", true, true, true],
+ ["about:test-chrome-privs2", true, true, true],
+ ["about:test-chrome-privs2?foo#bar", true, true, true],
+ ["about:test-chrome-privs2?foo", true, true, true],
+ ["about:test-chrome-privs2#bar", true, true, true],
+
+ ["about:test-unknown-unlinkable", true, true, true],
+
+ ["about:test-content-unlinkable", true, true, true],
+ ["about:test-content-unlinkable?foo", true, true, true],
+ ["about:test-content-unlinkable?foo#bar", true, true, true],
+ ["about:test-content-unlinkable#bar", true, true, true],
+
+ ["about:test-content-linkable", true, true, true],
+
+ ["about:test-unknown-linkable", true, true, true],
+ ]],
+ ["about:test-unknown-unlinkable", [
+ ["about:test-chrome-privs", false, false, true],
+
+ // Can link to ourselves:
+ ["about:test-unknown-unlinkable", true, true, true],
+ // Can't link to unlinkable content if we're not sure it's privileged:
+ ["about:test-unknown-unlinkable2", false, false, true],
+
+ ["about:test-content-unlinkable", true, true, true],
+ ["about:test-content-unlinkable2", true, true, true],
+ ["about:test-content-unlinkable2?foo", true, true, true],
+ ["about:test-content-unlinkable2?foo#bar", true, true, true],
+ ["about:test-content-unlinkable2#bar", true, true, true],
+
+ ["about:test-content-linkable", true, true, true],
+
+ // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it:
+ ["about:test-unknown-linkable", false, false, true],
+ ]],
+ ["about:test-content-unlinkable", [
+ ["about:test-chrome-privs", false, false, true],
+
+ // Can't link to unlinkable content if we're not sure it's privileged:
+ ["about:test-unknown-unlinkable", false, false, true],
+
+ ["about:test-content-unlinkable", true, true, true],
+ ["about:test-content-unlinkable2", true, true, true],
+ ["about:test-content-unlinkable2?foo", true, true, true],
+ ["about:test-content-unlinkable2?foo#bar", true, true, true],
+ ["about:test-content-unlinkable2#bar", true, true, true],
+
+ ["about:test-content-linkable", true, true, true],
+ ["about:test-unknown-linkable", false, false, true],
+ ]],
+ ["about:test-unknown-linkable", [
+ ["about:test-chrome-privs", false, false, true],
+
+ // Linkable content can't link to unlinkable content.
+ ["about:test-unknown-unlinkable", false, false, true],
+
+ ["about:test-content-unlinkable", false, false, true],
+ ["about:test-content-unlinkable2", false, false, true],
+ ["about:test-content-unlinkable2?foo", false, false, true],
+ ["about:test-content-unlinkable2?foo#bar", false, false, true],
+ ["about:test-content-unlinkable2#bar", false, false, true],
+
+ // ... but it can link to other linkable content.
+ ["about:test-content-linkable", true, true, true],
+
+ // Can link to ourselves:
+ ["about:test-unknown-linkable", true, true, true],
+
+ // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it:
+ ["about:test-unknown-linkable2", false, false, true],
+ ]],
+ ["about:test-content-linkable", [
+ ["about:test-chrome-privs", false, false, true],
+
+ // Linkable content can't link to unlinkable content.
+ ["about:test-unknown-unlinkable", false, false, true],
+
+ ["about:test-content-unlinkable", false, false, true],
+
+ // ... but it can link to itself and other linkable content.
+ ["about:test-content-linkable", true, true, true],
+ ["about:test-content-linkable2", true, true, true],
+
+ // Because this page doesn't have SAFE_FOR_UNTRUSTED, the web can't link to it:
+ ["about:test-unknown-linkable", false, false, true],
+ ]],
+]);
+
+function testURL(source, target, canLoad, canLoadWithoutInherit, canCreate, flags) {
+ function getPrincipalDesc(principal) {
+ if (principal.URI) {
+ return principal.URI.spec;
+ }
+ if (principal.isSystemPrincipal) {
+ return "system principal";
+ }
+ if (principal.isNullPrincipal) {
+ return "null principal";
+ }
+ return "unknown principal";
+ }
+ let threw = false;
+ let targetURI;
+ try {
+ targetURI = makeURI(target);
+ } catch (ex) {
+ ok(!canCreate, "Shouldn't be passing URIs that we can't create. Failed to create: " + target);
+ return;
+ }
+ ok(canCreate, "Created a URI for " + target + " which should " +
+ (canCreate ? "" : "not ") + "be possible.");
+ try {
+ ssm.checkLoadURIWithPrincipal(source, targetURI, flags);
+ } catch (ex) {
+ info(ex.message);
+ threw = true;
+ }
+ let inheritDisallowed = flags & ssm.DISALLOW_INHERIT_PRINCIPAL;
+ let shouldThrow = inheritDisallowed ? !canLoadWithoutInherit : !canLoad;
+ ok(threw == shouldThrow,
+ "Should " + (shouldThrow ? "" : "not ") + "throw an error when loading " +
+ target + " from " + getPrincipalDesc(source) +
+ (inheritDisallowed ? " without" : " with") + " principal inheritance.");
+}
+
+add_task(function* () {
+ yield kAboutPagesRegistered;
+ let baseFlags = ssm.STANDARD | ssm.DONT_REPORT_ERRORS;
+ for (let [sourceString, targetsAndExpectations] of URLs) {
+ let source;
+ if (sourceString.startsWith("about:test-chrome-privs")) {
+ source = ssm.getSystemPrincipal();
+ } else {
+ source = ssm.createCodebasePrincipal(makeURI(sourceString), {});
+ }
+ for (let [target, canLoad, canLoadWithoutInherit, canCreate] of targetsAndExpectations) {
+ testURL(source, target, canLoad, canLoadWithoutInherit, canCreate, baseFlags);
+ testURL(source, target, canLoad, canLoadWithoutInherit, canCreate,
+ baseFlags | ssm.DISALLOW_INHERIT_PRINCIPAL);
+ }
+ }
+
+ // Now test blob URIs, which we need to do in-content.
+ yield BrowserTestUtils.withNewTab("http://www.example.com/", function* (browser) {
+ yield ContentTask.spawn(
+ browser,
+ testURL.toString(),
+ function* (testURLFn) {
+ let testURL = eval("(" + testURLFn + ")");
+ let ssm = Services.scriptSecurityManager;
+ let baseFlags = ssm.STANDARD | ssm.DONT_REPORT_ERRORS;
+ let makeURI = Cu.import("resource://gre/modules/BrowserUtils.jsm", {}).BrowserUtils.makeURI;
+ let b = new content.Blob(["I am a blob"]);
+ let contentBlobURI = content.URL.createObjectURL(b);
+ let contentPrincipal = content.document.nodePrincipal;
+ // Loading this blob URI from the content page should work:
+ testURL(contentPrincipal, contentBlobURI, true, true, true, baseFlags);
+ testURL(contentPrincipal, contentBlobURI, true, true, true,
+ baseFlags | ssm.DISALLOW_INHERIT_PRINCIPAL);
+
+ testURL(contentPrincipal, "view-source:" + contentBlobURI, false, false, true,
+ baseFlags);
+ testURL(contentPrincipal, "view-source:" + contentBlobURI, false, false, true,
+ baseFlags | ssm.DISALLOW_INHERIT_PRINCIPAL);
+
+ // Feed URIs for blobs can't be created, so need to pass false as the fourth param.
+ for (let prefix of ["feed:", "view-source:feed:", "feed:view-source:"]) {
+ testURL(contentPrincipal, prefix + contentBlobURI, false, false, false,
+ baseFlags);
+ testURL(contentPrincipal, prefix + contentBlobURI, false, false, false,
+ baseFlags | ssm.DISALLOW_INHERIT_PRINCIPAL);
+ }
+ }
+ );
+
+ });
+});
diff --git a/caps/tests/mochitest/chrome.ini b/caps/tests/mochitest/chrome.ini
new file mode 100644
index 000000000..ee71ad30b
--- /dev/null
+++ b/caps/tests/mochitest/chrome.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+skip-if = os == 'android'
+support-files =
+ file_disableScript.html
+ !/caps/tests/mochitest/file_disableScript.html
+
+[test_bug995943.xul]
+[test_addonMayLoad.html]
+[test_disableScript.xul]
diff --git a/caps/tests/mochitest/file_data.txt b/caps/tests/mochitest/file_data.txt
new file mode 100644
index 000000000..26d7bd848
--- /dev/null
+++ b/caps/tests/mochitest/file_data.txt
@@ -0,0 +1 @@
+server data fetched over XHR
diff --git a/caps/tests/mochitest/file_disableScript.html b/caps/tests/mochitest/file_disableScript.html
new file mode 100644
index 000000000..f4888cd58
--- /dev/null
+++ b/caps/tests/mochitest/file_disableScript.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+var gFiredOnload = false;
+var gFiredOnclick = false;
+</script>
+</head>
+<body onload="gFiredOnload = true;" onclick="gFiredOnclick = true;">
+</body>
+</html>
diff --git a/caps/tests/mochitest/mochitest.ini b/caps/tests/mochitest/mochitest.ini
new file mode 100644
index 000000000..866553ea2
--- /dev/null
+++ b/caps/tests/mochitest/mochitest.ini
@@ -0,0 +1,13 @@
+[DEFAULT]
+support-files =
+ file_data.txt
+ file_disableScript.html
+ !/js/xpconnect/tests/mochitest/file_empty.html
+
+[test_app_principal_equality.html]
+[test_bug246699.html]
+[test_bug292789.html]
+[test_bug423375.html]
+[test_bug470804.html]
+[test_disallowInheritPrincipal.html]
+[test_extensionURL.html]
diff --git a/caps/tests/mochitest/resource_test_file.html b/caps/tests/mochitest/resource_test_file.html
new file mode 100644
index 000000000..8201bd70e
--- /dev/null
+++ b/caps/tests/mochitest/resource_test_file.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<html><head><title>resource test file</title></head><body></body></html>
diff --git a/caps/tests/mochitest/test_addonMayLoad.html b/caps/tests/mochitest/test_addonMayLoad.html
new file mode 100644
index 000000000..286284bfe
--- /dev/null
+++ b/caps/tests/mochitest/test_addonMayLoad.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1180921
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1180921</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript;version=1.8">
+
+ /** Test for Bug 1180921 **/
+ const Cc = Components.classes;
+ const Ci = Components.interfaces;
+ const Cu = Components.utils;
+ Cu.import("resource://gre/modules/Services.jsm");
+ let ssm = Services.scriptSecurityManager;
+ let aps = Cc["@mozilla.org/addons/policy-service;1"].getService(Ci.nsIAddonPolicyService).wrappedJSObject;
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.registerCleanupFunction(function() {
+ aps.setAddonLoadURICallback('addonA', null);
+ aps.setAddonLoadURICallback('addonB', null);
+ });
+
+ function tryLoad(sb, uri) {
+ let p = new Promise(function(resolve, reject) {
+ Cu.exportFunction(resolve, sb, { defineAs: "finish" });
+ Cu.exportFunction(reject, sb, { defineAs: "error" });
+ sb.eval("try { (function () { " +
+ " var xhr = new XMLHttpRequest();" +
+ " xhr.onreadystatechange = function() { if (xhr.readyState == XMLHttpRequest.DONE) { finish(xhr.status == 200); } };" +
+ " xhr.open('GET', '" + uri + "', true);" +
+ " xhr.send();" +
+ "})() } catch (e) { error(e); }");
+ });
+ return p;
+ }
+
+ let exampleCom_addonA = new Cu.Sandbox(ssm.createCodebasePrincipal(Services.io.newURI('http://example.com', null, null), {addonId: 'addonA'}),
+ {wantGlobalProperties: ['XMLHttpRequest']});
+ let nullPrin_addonA = new Cu.Sandbox(ssm.createNullPrincipal({addonId: 'addonA'}),
+ {wantGlobalProperties: ['XMLHttpRequest']});
+ let exampleCom_addonB = new Cu.Sandbox(ssm.createCodebasePrincipal(Services.io.newURI('http://example.com', null, null), {addonId: 'addonB'}),
+ {wantGlobalProperties: ['XMLHttpRequest']});
+
+ function uriForDomain(d) { return d + '/tests/caps/tests/mochitest/file_data.txt' }
+
+ tryLoad(exampleCom_addonA, uriForDomain('http://example.com'))
+ .then(function(success) {
+ ok(success, "same-origin load should succeed for addon A");
+ return tryLoad(nullPrin_addonA, uriForDomain('http://example.com'));
+ }).then(function(success) {
+ ok(!success, "null-principal load should fail for addon A");
+ return tryLoad(exampleCom_addonB, uriForDomain('http://example.com'));
+ }).then(function(success) {
+ ok(success, "same-origin load should succeed for addon B");
+ return tryLoad(exampleCom_addonA, uriForDomain('http://test1.example.org'));
+ }).then(function(success) {
+ ok(!success, "cross-origin load should fail for addon A");
+ aps.setAddonLoadURICallback('addonA', function(uri) { return /test1/.test(uri.host); });
+ aps.setAddonLoadURICallback('addonB', function(uri) { return /test2/.test(uri.host); });
+ return tryLoad(exampleCom_addonA, uriForDomain('http://test1.example.org'));
+ }).then(function(success) {
+ ok(success, "whitelisted cross-origin load of test1 should succeed for addon A");
+ return tryLoad(nullPrin_addonA, uriForDomain('http://test1.example.org'));
+ }).then(function(success) {
+ ok(!success, "whitelisted null principal load of test1 should still fail for addon A");
+ return tryLoad(exampleCom_addonB, uriForDomain('http://test1.example.org'));
+ }).then(function(success) {
+ ok(!success, "non-whitelisted cross-origin load of test1 should fail for addon B");
+ return tryLoad(exampleCom_addonB, uriForDomain('http://test2.example.org'));
+ }).then(function(success) {
+ ok(success, "whitelisted cross-origin load of test2 should succeed for addon B");
+ return tryLoad(exampleCom_addonA, uriForDomain('http://test2.example.org'));
+ }).then(function(success) {
+ ok(!success, "non-whitelisted cross-origin load of test2 should fail for addon A");
+ SimpleTest.finish();
+ }, function(e) {
+ ok(false, "Rejected promise chain: " + e);
+ SimpleTest.finish();
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1180921">Mozilla Bug 1180921</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/caps/tests/mochitest/test_app_principal_equality.html b/caps/tests/mochitest/test_app_principal_equality.html
new file mode 100644
index 000000000..f59f1f789
--- /dev/null
+++ b/caps/tests/mochitest/test_app_principal_equality.html
@@ -0,0 +1,88 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=777467
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test app principal's equality</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=777467">Mozilla Bug 777467</a>
+<p id="display"></p>
+<script>
+
+/** Test for app principal's equality **/
+
+SimpleTest.waitForExplicitFinish();
+
+var permissions = new Promise(resolve => {
+ SpecialPowers.pushPermissions(
+ [{ type: "browser", allow: true, context: document },
+ { type: "embed-apps", allow: true, context: document }],
+ resolve);
+});
+
+permissions.then(() => {
+ $('content').innerHTML =
+ '<iframe src="error404"></iframe>\n' +
+ '<iframe mozbrowser src="error404"></iframe>\n' +
+ '<iframe mozapp="http://example.org/manifest.webapp" mozbrowser src="error404"></iframe>';
+
+ var iframes = document.getElementsByTagName("iframe");
+ var promises = []
+ for (var i = 0; i < promises.length; ++i) {
+ promises.push(new Promise(resolve => {
+ iframes[i].addEventListener("load", resolve);
+ }));
+ }
+
+ return Promise.all(promises);
+});
+
+var prefs = new Promise(resolve => {
+ SpecialPowers.pushPrefEnv(
+ { set: [[ "dom.mozBrowserFramesEnabled", true ],
+ [ "dom.ipc.browser_frames.oop_by_default", false ]] },
+ resolve);
+});
+</script>
+<div id="content" style="display: none;">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+function canAccessDocument(win) {
+ var result = true;
+ try {
+ win.document;
+ } catch(e) {
+ result = false;
+ }
+ return result;
+}
+
+var loaded = new Promise(resolve => addLoadEvent(resolve));
+
+Promise.all([ permissions, prefs, loaded ]).then(runTest);
+
+function runTest() {
+ // Test the witness frame (we can access same-origin frame).
+ is(canAccessDocument(frames[0]), true,
+ "should be able to access the first frame");
+
+ // Test different app/browserElement frames.
+ for (var i=1; i<frames.length; ++i) {
+ is(canAccessDocument(frames[i]), false,
+ "should not be able to access the other frames");
+ }
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/caps/tests/mochitest/test_bug246699.html b/caps/tests/mochitest/test_bug246699.html
new file mode 100644
index 000000000..bb733e5df
--- /dev/null
+++ b/caps/tests/mochitest/test_bug246699.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=246699
+-->
+<head>
+ <title>Test for Bug 246699</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=246699">Mozilla Bug 246699</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id="load-frame"></iframe>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/**
+ ** Test for Bug 246699
+ ** (should produce stack information for caps errors)
+ **/
+function isError(e)
+{
+ return e.constructor.name === "Error" || e.constructor.name === "TypeError";
+}
+
+function hasStack(e)
+{
+ return isError(e) && /inciteCaps/.test(e.stack);
+}
+
+function inciteCaps(f)
+{
+ try {
+ f();
+ return "operation succeeded";
+ } catch (e if hasStack(e)) {
+ return "denied-stack";
+ } catch (e) {
+ return "unexpected: " + e;
+ }
+}
+
+function tryChromeLoad()
+{
+ window.frames[0].location = "chrome://global/content/mozilla.xhtml";
+}
+
+function tryComponentsClasses()
+{
+ return SpecialPowers.Components.classes["@mozilla.org/dummy;1"];
+}
+
+
+is(inciteCaps(tryChromeLoad), "denied-stack",
+ "should get stack for content-loading-chrome rejection");
+is(inciteCaps(tryComponentsClasses), "denied-stack",
+ "should get stack for SpecialPowers.Components.classes rejection");
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/caps/tests/mochitest/test_bug292789.html b/caps/tests/mochitest/test_bug292789.html
new file mode 100644
index 000000000..291ba00c1
--- /dev/null
+++ b/caps/tests/mochitest/test_bug292789.html
@@ -0,0 +1,105 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=292789
+-->
+<head>
+ <title>Test for Bug 292789</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=292789">Mozilla Bug 292789</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <script src="chrome://global/content/treeUtils.js"></script>
+ <script type="application/javascript;version=1.8" src="chrome://mozapps/content/xpinstall/xpinstallConfirm.js"></script>
+ <script id="resjs" type="application/javascript;version=1.8"></script>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 292789
+ **
+ ** Selectively allow access to whitelisted chrome packages
+ ** even for ALLOW_CHROME mechanisms (<script>, <img> etc)
+ **/
+
+SimpleTest.waitForExplicitFinish();
+
+/** <script src=""> test **/
+function testScriptSrc(aCallback) {
+ is(typeof gTreeUtils.sort, "function",
+ "content can still load <script> from chrome://global");
+ is(typeof XPInstallConfirm, "undefined",
+ "content should not be able to load <script> from chrome://mozapps");
+
+ /** make sure the last one didn't pass because someone
+ ** moved the resource
+ **/
+ var resjs = document.getElementById("resjs");
+ resjs.onload = scriptOnload;
+ resjs.src = "resource://gre/chrome/toolkit/content/mozapps/xpinstall/xpinstallConfirm.js";
+ document.getElementById("content").appendChild(resjs);
+
+ function scriptOnload() {
+ is(typeof XPInstallConfirm, "object",
+ "xpinstallConfirm.js has not moved unexpectedly");
+
+ // trigger the callback
+ if (aCallback)
+ aCallback();
+ }
+}
+
+/** <img src=""> tests **/
+var img_global = "chrome://global/skin/icons/Error.png";
+var img_mozapps = "chrome://mozapps/skin/plugins/contentPluginClose.png";
+var res_mozapps = "resource://gre/chrome/toolkit/skin/classic/mozapps/plugins/contentPluginClose.png";
+
+var imgTests = [[img_global, "success"],
+ [img_mozapps, "fail"],
+ [res_mozapps, "success"]];
+
+var curImgTest = 0;
+
+function runImgTest() {
+ var test = imgTests[curImgTest++];
+ var callback = curImgTest == imgTests.length ? finishTest : runImgTest;
+ loadImage(test[0], test[1], callback);
+}
+
+function finishTest() {
+ SimpleTest.finish();
+}
+
+function fail(event) {
+ is("fail", event.target.expected,
+ "content should not be allowed to load "+event.target.src);
+ if (event.target.callback)
+ event.target.callback();
+}
+
+function success(event) {
+ is("success", event.target.expected,
+ "content should be able to load "+event.target.src);
+ if (event.target.callback)
+ event.target.callback();
+}
+
+function loadImage(uri, expect, callback) {
+ var img = document.createElement("img");
+ img.onerror = fail;
+ img.onload = success;
+ img.expected = expect;
+ img.callback = callback;
+ img.src = uri;
+ //document.getElementById("content").appendChild(img);
+}
+
+// Start off the script src test, and have it start the img tests when complete.
+testScriptSrc(runImgTest);
+</script>
+</pre>
+</body>
+</html>
diff --git a/caps/tests/mochitest/test_bug423375.html b/caps/tests/mochitest/test_bug423375.html
new file mode 100644
index 000000000..1cd2a7a82
--- /dev/null
+++ b/caps/tests/mochitest/test_bug423375.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=423375
+-->
+<head>
+ <title>Test for Bug 423375</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=423375">Mozilla Bug 423375</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id="load-frame"></iframe>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/**
+ ** Test for Bug 423375
+ ** (content shouldn't be able to load chrome: or resource:)
+ **/
+function tryLoad(url)
+{
+ try {
+ window.frames[0].location = url;
+ return "loaded";
+ } catch (e if /Access.*denied/.test(String(e))) {
+ return "denied";
+ } catch (e) {
+ return "unexpected: " + e;
+ }
+}
+
+is(tryLoad("chrome://global/content/mozilla.xhtml"), "denied",
+ "content should have been prevented from loading chrome: URL");
+is(tryLoad("resource://gre-resources/html.css"), "denied",
+ "content should have been prevented from loading resource: URL");
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/caps/tests/mochitest/test_bug470804.html b/caps/tests/mochitest/test_bug470804.html
new file mode 100644
index 000000000..9ac88c8d7
--- /dev/null
+++ b/caps/tests/mochitest/test_bug470804.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=470804
+-->
+<head>
+ <title>Test for Bug 470804</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=470804">Mozilla Bug 470804</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 470804
+ Passing a null targetURL to checkLoadURIWithPrincipal shouldn't crash
+ **/
+
+const nsIScriptSecurityManager = SpecialPowers.Ci.nsIScriptSecurityManager;
+var secMan = SpecialPowers.Services.scriptSecurityManager;
+var principal = SpecialPowers.wrap(document).nodePrincipal;
+isnot(principal, undefined, "Should have a principal");
+isnot(principal, null, "Should have a non-null principal");
+is(secMan.isSystemPrincipal(principal), false,
+ "Shouldn't have system principal here");
+try {
+ secMan.checkLoadURIWithPrincipal(principal, null,
+ nsIScriptSecurityManager.STANDARD);
+} catch (e) {
+ // throwing is fine, it's just crashing that's bad
+}
+ok(true, "Survival", "We should get here without crashing");
+</script>
+</pre>
+</body>
+</html>
diff --git a/caps/tests/mochitest/test_bug995943.xul b/caps/tests/mochitest/test_bug995943.xul
new file mode 100644
index 000000000..e9eebb736
--- /dev/null
+++ b/caps/tests/mochitest/test_bug995943.xul
@@ -0,0 +1,115 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=995943
+-->
+<window title="Mozilla Bug 995943"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=995943"
+ target="_blank">Mozilla Bug 995943</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ const Cu = Components.utils;
+ const Cc = Components.classes;
+ const Ci = Components.interfaces;
+ Cu.import("resource://gre/modules/Services.jsm");
+ function debug(msg) { info(msg); }
+
+ /** Test for CAPS file:// URI prefs. **/
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestCompleteLog();
+ if (navigator.userAgent.indexOf("Mac OS X 10.10") != -1)
+ SimpleTest.expectAssertions(5, 11); // See bug 1067022, 1307988
+ else if (Services.appinfo.OS == "WINNT")
+ SimpleTest.expectAssertions(0, 1); // See bug 1067022
+ else
+ SimpleTest.expectAssertions(0, 2); // See bug 1305241
+
+ var rootdir = Services.appinfo.OS == "WINNT" ? "file:///C:" : "file:///";
+
+ function checkLoadFileURI(domain, shouldLoad) {
+ debug("Invoking checkLoadFileURI with domain: " + domain + ", shouldLoad: " + shouldLoad);
+ return new Promise(function(resolve, reject) {
+ $('ifr').addEventListener('load', function l1() {
+ debug("Invoked l1 for " + domain);
+ $('ifr').removeEventListener('load', l1);
+ function l2() {
+ debug("Invoked l2 for " + domain);
+ $('ifr').removeEventListener('load', l2);
+ ok(shouldLoad, "Successfully loaded file:// URI for domain: " + domain);
+ resolve();
+ }
+ $('ifr').addEventListener('load', l2);
+ try {
+ window[0].wrappedJSObject.location = rootdir;
+ debug("Successfully navigated for " + domain);
+ } catch (e) {
+ ok(!shouldLoad && /denied|insecure/.test(e),
+ "Prevented loading of file:// URI for domain: " + domain + " - " + e);
+ $('ifr').removeEventListener('load', l2);
+ resolve();
+ }
+ });
+ let targetURI = domain + '/tests/js/xpconnect/tests/mochitest/file_empty.html';
+ debug("Navigating iframe to " + targetURI);
+ $('ifr').contentWindow.location = targetURI;
+ });
+ }
+
+ function pushPrefs(prefs) {
+ return new Promise(function(resolve) { SpecialPowers.pushPrefEnv({ set: prefs }, resolve); });
+ }
+
+ function popPrefs() {
+ return new Promise(function(resolve) { SpecialPowers.popPrefEnv(resolve); });
+ }
+
+ var gGoCount = 0;
+ function go() {
+ debug("Invoking go for window with id: " + window.getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID);
+ is(++gGoCount, 1, "Should only call go once!");
+ checkLoadFileURI('http://example.com', false).then(
+ pushPrefs.bind(null, [['capability.policy.policynames', ' somepolicy '],
+ ['capability.policy.somepolicy.checkloaduri.enabled', 'AlLAcCeSs'],
+ ['capability.policy.somepolicy.sites', 'http://example.com']]))
+ .then(checkLoadFileURI.bind(null, 'http://example.com', true))
+ .then(popPrefs)
+ .then(checkLoadFileURI.bind(null, 'http://example.com', false))
+ .then(
+ pushPrefs.bind(null, [['capability.policy.policynames', ',somepolicy, someotherpolicy, '],
+ ['capability.policy.somepolicy.checkloaduri.enabled', 'allaccess'],
+ ['capability.policy.someotherpolicy.checkloaduri.enabled', 'nope'],
+ ['capability.policy.somepolicy.sites', ' http://example.org test1.example.com https://test2.example.com '],
+ ['capability.policy.someotherpolicy.sites', 'http://example.net ']]))
+ .then(checkLoadFileURI.bind(null, 'http://example.org', true))
+ .then(checkLoadFileURI.bind(null, 'http://test2.example.com', false))
+ .then(checkLoadFileURI.bind(null, 'https://test2.example.com', true))
+ .then(checkLoadFileURI.bind(null, 'http://sub1.test2.example.com', false))
+ .then(checkLoadFileURI.bind(null, 'https://sub1.test2.example.com', true))
+ .then(checkLoadFileURI.bind(null, 'http://example.net', false))
+ .then(checkLoadFileURI.bind(null, 'http://test1.example.com', true))
+ .then(checkLoadFileURI.bind(null, 'https://test1.example.com', true))
+ .then(checkLoadFileURI.bind(null, 'http://sub1.test1.example.com', true))
+ .then(checkLoadFileURI.bind(null, 'https://sub1.test1.example.com', true))
+ .then(pushPrefs.bind(null, [['capability.policy.someotherpolicy.checkloaduri.enabled', 'allAccess']]))
+ .then(checkLoadFileURI.bind(null, 'http://example.net', true))
+ .then(popPrefs)
+ .then(popPrefs)
+ .then(checkLoadFileURI.bind(null, 'http://example.net', false))
+ .then(SimpleTest.finish.bind(SimpleTest));
+
+ }
+ addLoadEvent(go);
+
+ ]]>
+ </script>
+ <iframe id="ifr" type="content" />
+</window>
diff --git a/caps/tests/mochitest/test_disableScript.xul b/caps/tests/mochitest/test_disableScript.xul
new file mode 100644
index 000000000..cef5f401a
--- /dev/null
+++ b/caps/tests/mochitest/test_disableScript.xul
@@ -0,0 +1,339 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=840488
+-->
+<window title="Mozilla Bug 840488"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=840488"
+ target="_blank">Mozilla Bug 840488</a>
+ </body>
+
+ <iframe id="root" name="root" type="content"/>
+ <iframe id="chromeFrame" name="chromeFrame" type="content"/>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for all the different ways that script can be disabled for a given global. **/
+
+ SimpleTest.waitForExplicitFinish();
+ const Cu = Components.utils;
+ const Ci = Components.interfaces;
+ Cu.import("resource://gre/modules/Promise.jsm");
+ Cu.import("resource://gre/modules/Services.jsm");
+ const ssm = Services.scriptSecurityManager;
+ function makeURI(uri) { return Services.io.newURI(uri, null, null); }
+ const path = "/tests/caps/tests/mochitest/file_disableScript.html";
+ const uri = "http://www.example.com" + path;
+ var rootFrame = document.getElementById('root');
+ var chromeFrame = document.getElementById('chromeFrame');
+ navigateFrame(rootFrame, uri + "?name=rootframe").then(function() {
+ navigateFrame(chromeFrame, "file_disableScript.html").then(go);
+ });
+
+ function navigateFrame(ifr, src) {
+ let deferred = Promise.defer();
+ function onload() {
+ ifr.removeEventListener('load', onload);
+ deferred.resolve();
+ }
+ ifr.addEventListener('load', onload, false);
+ ifr.setAttribute('src', src);
+ return deferred.promise;
+ }
+
+ function navigateBack(ifr) {
+ let deferred = Promise.defer();
+
+ // pageshow events don't fire on the iframe element, so we need to use the
+ // chrome event handler for the docshell.
+ var browser = ifr.contentWindow
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell)
+ .chromeEventHandler;
+ function onpageshow(evt) {
+ info("Navigated back. Persisted: " + evt.persisted);
+ browser.removeEventListener('pageshow', onpageshow);
+ deferred.resolve();
+ }
+ browser.addEventListener('pageshow', onpageshow, false);
+ ifr.contentWindow.history.back();
+ return deferred.promise;
+ }
+
+ function addFrame(parentWin, name, expectOnload) {
+ let ifr = parentWin.document.createElement('iframe');
+ parentWin.document.body.appendChild(ifr);
+ ifr.setAttribute('name', name);
+ let deferred = Promise.defer();
+ // We need to append 'name' to avoid running afoul of recursive frame detection.
+ let frameURI = uri + "?name=" + name;
+ navigateFrame(ifr, frameURI).then(function() {
+ is(String(ifr.contentWindow.location), frameURI, "Successful load");
+ is(!!ifr.contentWindow.wrappedJSObject.gFiredOnload, expectOnload,
+ "onload should only fire when scripts are enabled");
+ deferred.resolve();
+ });
+ return deferred.promise;
+ }
+
+ function checkScriptEnabled(win, expectEnabled) {
+ win.wrappedJSObject.gFiredOnclick = false;
+ win.document.body.dispatchEvent(new win.Event('click'));
+ is(win.wrappedJSObject.gFiredOnclick, expectEnabled, "Checking script-enabled for " + win.name + " (" + win.location + ")");
+ }
+
+ function setScriptEnabledForDocShell(win, enabled) {
+ win.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDocShell)
+ .allowJavascript = enabled;
+ }
+
+ function testList(expectEnabled, win, list, idx) {
+ idx = idx || 0;
+ let deferred = Promise.defer();
+ let target = list[idx] + path;
+ info("Testing scriptability for: " + target + ". expecting " + expectEnabled);
+ navigateFrame(win.frameElement, target).then(function() {
+ checkScriptEnabled(win, expectEnabled);
+ if (idx == list.length - 1)
+ deferred.resolve();
+ else
+ testList(expectEnabled, win, list, idx + 1).then(function() { deferred.resolve(); });
+ });
+ return deferred.promise;
+ }
+
+ function testDomainPolicy(defaultScriptability, exceptions, superExceptions,
+ exempt, notExempt, set, superSet, win) {
+ // Populate our sets.
+ for (var e of exceptions)
+ set.add(makeURI(e));
+ for (var e of superExceptions)
+ superSet.add(makeURI(e));
+
+ return testList(defaultScriptability, win, notExempt).then(function() {
+ return testList(!defaultScriptability, win, exempt);
+ });
+ }
+
+ function setScriptEnabledForBrowser(enabled) {
+ var prefname = "javascript.enabled";
+ Services.prefs.setBoolPref(prefname, enabled);
+ }
+
+ function reloadFrame(frame) {
+ let deferred = Promise.defer();
+ frame.addEventListener('load', function onload() {
+ deferred.resolve();
+ frame.removeEventListener('load', onload);
+ }, false);
+ frame.contentWindow.location.reload(true);
+ return deferred.promise;
+ }
+
+ function go() {
+ var rootWin = rootFrame.contentWindow;
+ var chromeWin = chromeFrame.contentWindow;
+
+ // Test simple docshell enable/disable.
+ checkScriptEnabled(rootWin, true);
+ setScriptEnabledForDocShell(rootWin, false);
+ checkScriptEnabled(rootWin, false);
+ setScriptEnabledForDocShell(rootWin, true);
+ checkScriptEnabled(rootWin, true);
+
+ // Privileged frames are immune to docshell flags.
+ ok(ssm.isSystemPrincipal(chromeWin.document.nodePrincipal), "Sanity check for System Principal");
+ setScriptEnabledForDocShell(chromeWin, false);
+ checkScriptEnabled(chromeWin, true);
+ setScriptEnabledForDocShell(chromeWin, true);
+
+ // Play around with the docshell tree and make sure everything works as
+ // we expect.
+ addFrame(rootWin, 'parent', true).then(function() {
+ checkScriptEnabled(rootWin[0], true);
+ return addFrame(rootWin[0], 'childA', true);
+ }).then(function() {
+ checkScriptEnabled(rootWin[0][0], true);
+ setScriptEnabledForDocShell(rootWin[0], false);
+ checkScriptEnabled(rootWin, true);
+ checkScriptEnabled(rootWin[0], false);
+ checkScriptEnabled(rootWin[0][0], false);
+ return addFrame(rootWin[0], 'childB', false);
+ }).then(function() {
+ checkScriptEnabled(rootWin[0][1], false);
+ setScriptEnabledForDocShell(rootWin[0][0], false);
+ setScriptEnabledForDocShell(rootWin[0], true);
+ checkScriptEnabled(rootWin[0], true);
+ checkScriptEnabled(rootWin[0][0], false);
+ setScriptEnabledForDocShell(rootWin[0][0], true);
+
+ // Flags are inherited from the parent docshell at attach time. Note that
+ // the flag itself is inherited, regardless of whether or not scripts are
+ // currently allowed on the parent (which could depend on the parent's
+ // parent). Check that.
+ checkScriptEnabled(rootWin[0][1], false);
+ setScriptEnabledForDocShell(rootWin[0], false);
+ setScriptEnabledForDocShell(rootWin[0][1], true);
+ return addFrame(rootWin[0][1], 'grandchild', false);
+ }).then(function() {
+ checkScriptEnabled(rootWin[0], false);
+ checkScriptEnabled(rootWin[0][1], false);
+ checkScriptEnabled(rootWin[0][1][0], false);
+ setScriptEnabledForDocShell(rootWin[0], true);
+ checkScriptEnabled(rootWin[0], true);
+ checkScriptEnabled(rootWin[0][1], true);
+ checkScriptEnabled(rootWin[0][1][0], true);
+
+ // Try navigating two frames, then munging docshell scriptability, then
+ // pulling the frames out of the bfcache to make sure that flags are
+ // properly propagated to inactive inner windows. We do this both for an
+ // 'own' docshell, as well as for an ancestor docshell.
+ return navigateFrame(rootWin[0][0].frameElement, rootWin[0][0].location + '-navigated');
+ }).then(function() { return navigateFrame(rootWin[0][1][0].frameElement, rootWin[0][1][0].location + '-navigated'); })
+ .then(function() {
+ checkScriptEnabled(rootWin[0][0], true);
+ checkScriptEnabled(rootWin[0][1][0], true);
+ setScriptEnabledForDocShell(rootWin[0][0], false);
+ setScriptEnabledForDocShell(rootWin[0][1], false);
+ checkScriptEnabled(rootWin[0][0], false);
+ checkScriptEnabled(rootWin[0][1][0], false);
+ return navigateBack(rootWin[0][0].frameElement);
+ }).then(function() { return navigateBack(rootWin[0][1][0].frameElement); })
+ .then(function() {
+ checkScriptEnabled(rootWin[0][0], false);
+ checkScriptEnabled(rootWin[0][1][0], false);
+
+ // Disable JS via the global pref pref. This is only guaranteed to have an effect
+ // for subsequent loads.
+ setScriptEnabledForBrowser(false);
+ return reloadFrame(rootFrame);
+ }).then(function() {
+ checkScriptEnabled(rootWin, false);
+ checkScriptEnabled(chromeWin, true);
+ setScriptEnabledForBrowser(true);
+ return reloadFrame(rootFrame);
+ }).then(function() {
+ checkScriptEnabled(rootWin, true);
+
+ // Play around with dynamically blocking script for a given global.
+ // This takes effect immediately.
+ Cu.blockScriptForGlobal(rootWin);
+ Cu.blockScriptForGlobal(rootWin);
+ Cu.unblockScriptForGlobal(rootWin);
+ checkScriptEnabled(rootWin, false);
+ Cu.unblockScriptForGlobal(rootWin);
+ checkScriptEnabled(rootWin, true);
+ Cu.blockScriptForGlobal(rootWin);
+ try {
+ Cu.blockScriptForGlobal(chromeWin);
+ ok(false, "Should have thrown");
+ } catch (e) {
+ ok(/may not be disabled/.test(e),
+ "Shouldn't be able to programmatically block script for system globals");
+ }
+ return reloadFrame(rootFrame);
+ }).then(function() {
+ checkScriptEnabled(rootWin, true);
+
+ // Test system-wide domain policy. This only takes effect for subsequently-
+ // loaded globals.
+
+ // Check the basic semantics of the sets.
+ is(ssm.domainPolicyActive, false, "not enabled");
+ window.policy = ssm.activateDomainPolicy();
+ ok(policy instanceof Ci.nsIDomainPolicy, "Got a policy");
+ try {
+ ssm.activateDomainPolicy();
+ ok(false, "Should have thrown");
+ } catch (e) {
+ ok(true, "can't have two live domain policies");
+ }
+ var sbRef = policy.superBlacklist;
+ isnot(sbRef, null, "superBlacklist non-null");
+ ok(!sbRef.contains(makeURI('http://www.example.com')));
+ sbRef.add(makeURI('http://www.example.com/foopy'));
+ ok(sbRef.contains(makeURI('http://www.example.com')));
+ sbRef.remove(makeURI('http://www.example.com'));
+ ok(!sbRef.contains(makeURI('http://www.example.com')));
+ sbRef.add(makeURI('http://www.example.com/foopy/this.that/'));
+ ok(sbRef.contains(makeURI('http://www.example.com/baz')));
+ ok(!sbRef.contains(makeURI('https://www.example.com')));
+ ok(!sbRef.contains(makeURI('https://www.example.com:88')));
+ ok(!sbRef.contains(makeURI('http://foo.www.example.com')));
+ ok(sbRef.containsSuperDomain(makeURI('http://foo.www.example.com')));
+ ok(sbRef.containsSuperDomain(makeURI('http://foo.bar.www.example.com')));
+ ok(!sbRef.containsSuperDomain(makeURI('http://foo.bar.www.exxample.com')));
+ ok(!sbRef.containsSuperDomain(makeURI('http://example.com')));
+ ok(!sbRef.containsSuperDomain(makeURI('http://com/this.that/')));
+ ok(!sbRef.containsSuperDomain(makeURI('https://foo.www.example.com')));
+ ok(sbRef.contains(makeURI('http://www.example.com')));
+ policy.deactivate();
+ is(ssm.domainPolicyActive, false, "back to inactive");
+ ok(!sbRef.contains(makeURI('http://www.example.com')),
+ "Disabling domain policy clears the set");
+ policy = ssm.activateDomainPolicy();
+ ok(policy.superBlacklist);
+ isnot(sbRef, policy.superBlacklist, "Mint new sets each time!");
+ policy.deactivate();
+ is(policy.blacklist, null, "blacklist nulled out");
+ policy = ssm.activateDomainPolicy();
+ isnot(policy.blacklist, null, "non-null again");
+ isnot(policy.blacklist, sbRef, "freshly minted");
+ policy.deactivate();
+
+ //
+ // Now, create and apply a mock-policy. We check the same policy both as
+ // a blacklist and as a whitelist.
+ //
+
+ window.testPolicy = {
+ // The policy.
+ exceptions: ['http://test1.example.com', 'http://example.com'],
+ superExceptions: ['http://test2.example.org', 'https://test1.example.com'],
+
+ // The testcases.
+ exempt: ['http://test1.example.com', 'http://example.com',
+ 'http://test2.example.org', 'http://sub1.test2.example.org',
+ 'https://sub1.test1.example.com'],
+
+ notExempt: ['http://test2.example.com', 'http://sub1.test1.example.com',
+ 'http://www.example.com', 'https://test2.example.com',
+ 'https://example.com', 'http://test1.example.org'],
+ };
+
+ policy = ssm.activateDomainPolicy();
+ info("Testing Blacklist-style Domain Policy");
+ return testDomainPolicy(true, testPolicy.exceptions,
+ testPolicy.superExceptions, testPolicy.exempt,
+ testPolicy.notExempt, policy.blacklist,
+ policy.superBlacklist, rootWin);
+ }).then(function() {
+ policy.deactivate();
+ policy = ssm.activateDomainPolicy();
+ info("Testing Whitelist-style Domain Policy");
+ setScriptEnabledForBrowser(false);
+ return testDomainPolicy(false, testPolicy.exceptions,
+ testPolicy.superExceptions, testPolicy.exempt,
+ testPolicy.notExempt, policy.whitelist,
+ policy.superWhitelist, rootWin);
+ }).then(function() {
+ setScriptEnabledForBrowser(true);
+ policy.deactivate();
+
+ SimpleTest.finish();
+ });
+ }
+
+ ]]>
+ </script>
+</window>
diff --git a/caps/tests/mochitest/test_disallowInheritPrincipal.html b/caps/tests/mochitest/test_disallowInheritPrincipal.html
new file mode 100644
index 000000000..ec59bec3c
--- /dev/null
+++ b/caps/tests/mochitest/test_disallowInheritPrincipal.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=732413
+-->
+<head>
+ <title>Test for Bug 732413</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=732413">Mozilla Bug 732413</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 732413
+ Passing DISALLOW_INHERIT_PRINCIPAL flag should be effective even if
+ aPrincipal is the system principal.
+ **/
+
+const nsIScriptSecurityManager = SpecialPowers.Ci.nsIScriptSecurityManager;
+var secMan = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
+ .getService(nsIScriptSecurityManager);
+var sysPrincipal = secMan.getSystemPrincipal();
+isnot(sysPrincipal, undefined, "Should have a principal");
+isnot(sysPrincipal, null, "Should have a non-null principal");
+is(secMan.isSystemPrincipal(sysPrincipal), true,
+ "Should have system principal here");
+
+
+var ioService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"].
+ getService(SpecialPowers.Ci.nsIIOService);
+var inheritingURI = ioService.newURI("javascript:1+1", null, null);
+
+// First try a normal call to checkLoadURIWithPrincipal
+try {
+ secMan.checkLoadURIWithPrincipal(sysPrincipal, inheritingURI,
+ nsIScriptSecurityManager.STANDARD);
+ ok(true, "checkLoadURI allowed the load");
+} catch (e) {
+ ok(false, "checkLoadURI failed unexpectedly: " + e);
+}
+
+// Now call checkLoadURIWithPrincipal with DISALLOW_INHERIT_PRINCIPAL
+try {
+ secMan.checkLoadURIWithPrincipal(sysPrincipal, inheritingURI,
+ nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
+ ok(false, "checkLoadURI allowed the load unexpectedly");
+} catch (e) {
+ ok(true, "checkLoadURI prevented load of principal-inheriting URI");
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/caps/tests/mochitest/test_extensionURL.html b/caps/tests/mochitest/test_extensionURL.html
new file mode 100644
index 000000000..315c47321
--- /dev/null
+++ b/caps/tests/mochitest/test_extensionURL.html
@@ -0,0 +1,166 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1161831
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1161831</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1161831 **/
+ SimpleTest.waitForExplicitFinish();
+
+ var aps = SpecialPowers.Cc["@mozilla.org/addons/policy-service;1"]
+ .getService(SpecialPowers.Ci.nsIAddonPolicyService).wrappedJSObject;
+ var oldLoadCallback = aps.setExtensionURILoadCallback(null);
+ var oldMapCallback = aps.setExtensionURIToAddonIdCallback(null);
+ var resourceHandler = SpecialPowers.Services.io.getProtocolHandler("resource")
+ .QueryInterface(SpecialPowers.Ci.nsISubstitutingProtocolHandler);
+ var extensionHandler = SpecialPowers.Services.io.getProtocolHandler("moz-extension")
+ .QueryInterface(SpecialPowers.Ci.nsISubstitutingProtocolHandler);
+
+ SimpleTest.registerCleanupFunction(function() {
+ extensionHandler.setSubstitution('cherise', null);
+ extensionHandler.setSubstitution('liebchen', null);
+ aps.setExtensionURILoadCallback(oldLoadCallback);
+ aps.setExtensionURIToAddonIdCallback(oldMapCallback);
+ });
+
+ addLoadEvent(function() {
+
+ // First, get a file:// URI to something - open to suggestions on how to do
+ // this more easily.
+ var resURI = SpecialPowers.Services.io.newURI('resource://testing-common/resource_test_file.html', null, null);
+ var filePath = resourceHandler.resolveURI(resURI);
+ ok(filePath.startsWith('file://'), 'resource:// URI resolves where we expect: ' + filePath);
+ var fileURI = SpecialPowers.Services.io.newURI(filePath, null, null);
+
+ // Register a moz-extension:// URI.
+ extensionHandler.setSubstitution('cherise', fileURI);
+
+ // Alias the above.
+ extensionHandler.setSubstitution('liebchen', SpecialPowers.Services.io.newURI('moz-extension://cherise', null, null));
+
+ //
+ // Make sure that non-file:// URIs don't work.
+ //
+
+ // resource://
+ try {
+ extensionHandler.setSubstitution('interdit', resURI);
+ ok(false, "Should have thrown for mapping moz-extension to resource");
+ } catch (e) {
+ ok(true, "Threw correctly: " + e);
+ }
+
+ // chrome://
+ try {
+ var chromeURI = SpecialPowers.Services.io.newURI('chrome://global/content/mozilla.xhtml', null, null);
+ extensionHandler.setSubstitution('verboten', chromeURI);
+ ok(false, "Should have thrown for mapping moz-extension to chrome");
+ } catch (e) {
+ ok(true, "Threw correctly: " + e);
+ }
+
+ function navigateWithLocation(ifr, url) { ifr.contentWindow.location = url; }
+ function navigateWithSrc(ifr, url) { ifr.setAttribute('src', url); }
+ function navigateFromChromeWithLocation(ifr, url) { SpecialPowers.wrap(ifr).contentWindow.location = url; }
+ function navigateFromChromeWithWebNav(ifr, url) {
+ SpecialPowers.wrap(ifr).contentWindow
+ .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
+ .getInterface(SpecialPowers.Ci.nsIWebNavigation)
+ .loadURI(url, 0, null, null, null);
+ }
+
+
+ function setWhitelistCallback(rgxp) {
+ var cb = SpecialPowers.wrapCallback(function(uri) { return rgxp.test(uri.spec); });
+ aps.setExtensionURILoadCallback(cb);
+ }
+
+ aps.setExtensionURIToAddonIdCallback(SpecialPowers.wrapCallback(function (uri) { return 'imaginaryaddon-' + uri.host[0]; }));
+
+ function testLoad(url, navigate, shouldThrow) {
+ var ifr = document.createElement('iframe');
+ var p = new Promise(function(resolve, reject) {
+ ifr.onload = function() {
+ ok(true, 'Loaded ' + url);
+ var prin = SpecialPowers.wrap(ifr.contentWindow).document.nodePrincipal;
+ function stripTrailingSlash(s) { return s.replace(/\/$/, ''); };
+ is(stripTrailingSlash(prin.URI.spec), url, 'Principal uri is correct: ' + url);
+ function stripPath(s) { return s.replace(/(.*\/\/.+)\/.*/, '$1'); };
+ is(prin.originNoSuffix, stripPath(url), 'Principal origin is correct: ' + prin.originNoSuffix);
+ is(prin.originAttributes.addonId, 'imaginaryaddon-' + url[url.indexOf('/') + 2], 'addonId is correct');
+ if (/_blank/.test(url)) {
+ is(SpecialPowers.wrap(ifr.contentWindow).document.documentElement.innerHTML,
+ '<head></head><body></body>', 'blank document looks right');
+ } else {
+ is(SpecialPowers.wrap(ifr.contentWindow).document.title, 'resource test file',
+ 'document looks right');
+ }
+ ifr.remove();
+ resolve();
+ };
+ document.body.appendChild(ifr);
+
+ var threw = false;
+ try {
+ navigate(ifr, url);
+ } catch (e) {
+ ifr.remove();
+ threw = true;
+ ok(/denied|insecure/.test(e), "exception correct: " + e);
+ }
+ is(threw, !!shouldThrow, "Correct throwing behavior for: " + url);
+ !threw || resolve();
+ });
+
+ return p;
+ }
+
+ function testXHR(url, shouldError) {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.addEventListener("load", () => { ok(!shouldError, `XHR to ${url} should succeed`); resolve(); });
+ xhr.addEventListener("error", () => { ok(shouldError, `XHR to ${url} should fail`); resolve(); });
+ xhr.open("GET", url, true);
+ xhr.send();
+ });
+ }
+
+ //
+ // Perform some loads and make sure they work correctly.
+ //
+ testLoad.bind(null, 'moz-extension://cherise', navigateFromChromeWithLocation)()
+ .then(testLoad.bind(null, 'moz-extension://cherise', navigateFromChromeWithWebNav))
+ .then(testLoad.bind(null, 'moz-extension://cherise', navigateWithLocation, /* shouldThrow = */ true))
+ .then(testXHR.bind(null, 'moz-extension://cherise', /* shouldError = */ true))
+ .then(setWhitelistCallback.bind(null, /cherise/))
+ .then(testLoad.bind(null, 'moz-extension://cherise', navigateWithLocation))
+ .then(testXHR.bind(null, 'moz-extension://cherise'))
+ .then(testLoad.bind(null, 'moz-extension://liebchen', navigateWithLocation, /* shouldThrow = */ true))
+ .then(testXHR.bind(null, 'moz-extension://liebchen', /* shouldError = */ true))
+ .then(setWhitelistCallback.bind(null, /cherise|liebchen/))
+ .then(testLoad.bind(null, 'moz-extension://liebchen', navigateWithLocation))
+ .then(testLoad.bind(null, 'moz-extension://liebchen', navigateWithSrc))
+ .then(testLoad.bind(null, 'moz-extension://cherise', navigateWithSrc))
+ .then(testLoad.bind(null, 'moz-extension://cherise/_blank.html', navigateWithSrc))
+ .then(SimpleTest.finish.bind(SimpleTest),
+ function(e) { ok(false, "rejected promise: " + e); SimpleTest.finish() }
+ );
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1161831">Mozilla Bug 1161831</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/caps/tests/unit/test_origin.js b/caps/tests/unit/test_origin.js
new file mode 100644
index 000000000..0fa125b61
--- /dev/null
+++ b/caps/tests/unit/test_origin.js
@@ -0,0 +1,307 @@
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+var ssm = Services.scriptSecurityManager;
+function makeURI(uri) { return Services.io.newURI(uri, null, null); }
+
+function checkThrows(f) {
+ var threw = false;
+ try { f(); } catch (e) { threw = true }
+ do_check_true(threw);
+}
+
+function checkCrossOrigin(a, b) {
+ do_check_false(a.equals(b));
+ do_check_false(a.equalsConsideringDomain(b));
+ do_check_false(a.subsumes(b));
+ do_check_false(a.subsumesConsideringDomain(b));
+ do_check_false(b.subsumes(a));
+ do_check_false(b.subsumesConsideringDomain(a));
+}
+
+function checkOriginAttributes(prin, attrs, suffix) {
+ attrs = attrs || {};
+ do_check_eq(prin.originAttributes.appId, attrs.appId || 0);
+ do_check_eq(prin.originAttributes.inIsolatedMozBrowser, attrs.inIsolatedMozBrowser || false);
+ do_check_eq(prin.originSuffix, suffix || '');
+ do_check_eq(ChromeUtils.originAttributesToSuffix(attrs), suffix || '');
+ do_check_true(ChromeUtils.originAttributesMatchPattern(prin.originAttributes, attrs));
+ if (!prin.isNullPrincipal && !prin.origin.startsWith('[')) {
+ do_check_true(ssm.createCodebasePrincipalFromOrigin(prin.origin).equals(prin));
+ } else {
+ checkThrows(() => ssm.createCodebasePrincipalFromOrigin(prin.origin));
+ }
+}
+
+function checkSandboxOriginAttributes(arr, attrs, options) {
+ options = options || {};
+ var sandbox = Cu.Sandbox(arr, options);
+ checkOriginAttributes(Cu.getObjectPrincipal(sandbox), attrs,
+ ChromeUtils.originAttributesToSuffix(attrs));
+}
+
+// utility function useful for debugging
+function printAttrs(name, attrs) {
+ do_print(name + " {\n" +
+ "\tappId: " + attrs.appId + ",\n" +
+ "\tuserContextId: " + attrs.userContextId + ",\n" +
+ "\tinIsolatedMozBrowser: " + attrs.inIsolatedMozBrowser + ",\n" +
+ "\taddonId: '" + attrs.addonId + "',\n" +
+ "\tprivateBrowsingId: '" + attrs.privateBrowsingId + "',\n" +
+ "\tfirstPartyDomain: '" + attrs.firstPartyDomain + "'\n}");
+}
+
+
+function checkValues(attrs, values) {
+ values = values || {};
+ //printAttrs("attrs", attrs);
+ //printAttrs("values", values);
+ do_check_eq(attrs.appId, values.appId || 0);
+ do_check_eq(attrs.userContextId, values.userContextId || 0);
+ do_check_eq(attrs.inIsolatedMozBrowser, values.inIsolatedMozBrowser || false);
+ do_check_eq(attrs.addonId, values.addonId || '');
+ do_check_eq(attrs.privateBrowsingId, values.privateBrowsingId || '');
+ do_check_eq(attrs.firstPartyDomain, values.firstPartyDomain || '');
+}
+
+function run_test() {
+ // Attributeless origins.
+ do_check_eq(ssm.getSystemPrincipal().origin, '[System Principal]');
+ checkOriginAttributes(ssm.getSystemPrincipal());
+ var exampleOrg = ssm.createCodebasePrincipal(makeURI('http://example.org'), {});
+ do_check_eq(exampleOrg.origin, 'http://example.org');
+ checkOriginAttributes(exampleOrg);
+ var exampleCom = ssm.createCodebasePrincipal(makeURI('https://www.example.com:123'), {});
+ do_check_eq(exampleCom.origin, 'https://www.example.com:123');
+ checkOriginAttributes(exampleCom);
+ var nullPrin = Cu.getObjectPrincipal(new Cu.Sandbox(null));
+ do_check_true(/^moz-nullprincipal:\{([0-9]|[a-z]|\-){36}\}$/.test(nullPrin.origin));
+ checkOriginAttributes(nullPrin);
+ var ipv6Prin = ssm.createCodebasePrincipal(makeURI('https://[2001:db8::ff00:42:8329]:123'), {});
+ do_check_eq(ipv6Prin.origin, 'https://[2001:db8::ff00:42:8329]:123');
+ checkOriginAttributes(ipv6Prin);
+ var ipv6NPPrin = ssm.createCodebasePrincipal(makeURI('https://[2001:db8::ff00:42:8329]'), {});
+ do_check_eq(ipv6NPPrin.origin, 'https://[2001:db8::ff00:42:8329]');
+ checkOriginAttributes(ipv6NPPrin);
+ var ep = Cu.getObjectPrincipal(Cu.Sandbox([exampleCom, nullPrin, exampleOrg]));
+ checkOriginAttributes(ep);
+ checkCrossOrigin(exampleCom, exampleOrg);
+ checkCrossOrigin(exampleOrg, nullPrin);
+
+ // nsEP origins should be in lexical order.
+ do_check_eq(ep.origin, `[Expanded Principal [${exampleOrg.origin}, ${exampleCom.origin}, ${nullPrin.origin}]]`);
+
+ // Make sure createCodebasePrincipal does what the rest of gecko does.
+ do_check_true(exampleOrg.equals(Cu.getObjectPrincipal(new Cu.Sandbox('http://example.org'))));
+
+ //
+ // Test origin attributes.
+ //
+
+ // Just app.
+ var exampleOrg_app = ssm.createCodebasePrincipal(makeURI('http://example.org'), {appId: 42});
+ var nullPrin_app = ssm.createNullPrincipal({appId: 42});
+ checkOriginAttributes(exampleOrg_app, {appId: 42}, '^appId=42');
+ checkOriginAttributes(nullPrin_app, {appId: 42}, '^appId=42');
+ do_check_eq(exampleOrg_app.origin, 'http://example.org^appId=42');
+
+ // Just browser.
+ var exampleOrg_browser = ssm.createCodebasePrincipal(makeURI('http://example.org'), {inIsolatedMozBrowser: true});
+ var nullPrin_browser = ssm.createNullPrincipal({inIsolatedMozBrowser: true});
+ checkOriginAttributes(exampleOrg_browser, {inIsolatedMozBrowser: true}, '^inBrowser=1');
+ checkOriginAttributes(nullPrin_browser, {inIsolatedMozBrowser: true}, '^inBrowser=1');
+ do_check_eq(exampleOrg_browser.origin, 'http://example.org^inBrowser=1');
+
+ // App and browser.
+ var exampleOrg_appBrowser = ssm.createCodebasePrincipal(makeURI('http://example.org'), {inIsolatedMozBrowser: true, appId: 42});
+ var nullPrin_appBrowser = ssm.createNullPrincipal({inIsolatedMozBrowser: true, appId: 42});
+ checkOriginAttributes(exampleOrg_appBrowser, {appId: 42, inIsolatedMozBrowser: true}, '^appId=42&inBrowser=1');
+ checkOriginAttributes(nullPrin_appBrowser, {appId: 42, inIsolatedMozBrowser: true}, '^appId=42&inBrowser=1');
+ do_check_eq(exampleOrg_appBrowser.origin, 'http://example.org^appId=42&inBrowser=1');
+
+ // App and browser, different domain.
+ var exampleCom_appBrowser = ssm.createCodebasePrincipal(makeURI('https://www.example.com:123'), {appId: 42, inIsolatedMozBrowser: true});
+ checkOriginAttributes(exampleCom_appBrowser, {appId: 42, inIsolatedMozBrowser: true}, '^appId=42&inBrowser=1');
+ do_check_eq(exampleCom_appBrowser.origin, 'https://www.example.com:123^appId=42&inBrowser=1');
+
+ // Addon.
+ var exampleOrg_addon = ssm.createCodebasePrincipal(makeURI('http://example.org'), {addonId: 'dummy'});
+ checkOriginAttributes(exampleOrg_addon, { addonId: "dummy" }, '^addonId=dummy');
+ do_check_eq(exampleOrg_addon.origin, 'http://example.org^addonId=dummy');
+
+ // First party Uri
+ var exampleOrg_firstPartyDomain = ssm.createCodebasePrincipal(makeURI('http://example.org'), {firstPartyDomain: 'example.org'});
+ checkOriginAttributes(exampleOrg_firstPartyDomain, { firstPartyDomain: "example.org" }, '^firstPartyDomain=example.org');
+ do_check_eq(exampleOrg_firstPartyDomain.origin, 'http://example.org^firstPartyDomain=example.org');
+
+ // Make sure we don't crash when serializing principals with UNKNOWN_APP_ID.
+ try {
+ let binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].
+ createInstance(Ci.nsIObjectOutputStream);
+ let pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
+ pipe.init(false, false, 0, 0xffffffff, null);
+ binaryStream.setOutputStream(pipe.outputStream);
+ binaryStream.writeCompoundObject(simplePrin, Ci.nsISupports, true);
+ binaryStream.close();
+ } catch (e) {
+ do_check_true(true);
+ }
+
+
+ // Just userContext.
+ var exampleOrg_userContext = ssm.createCodebasePrincipal(makeURI('http://example.org'), {userContextId: 42});
+ checkOriginAttributes(exampleOrg_userContext, { userContextId: 42 }, '^userContextId=42');
+ do_check_eq(exampleOrg_userContext.origin, 'http://example.org^userContextId=42');
+
+ // UserContext and Addon.
+ var exampleOrg_userContextAddon = ssm.createCodebasePrincipal(makeURI('http://example.org'), {addonId: 'dummy', userContextId: 42});
+ var nullPrin_userContextAddon = ssm.createNullPrincipal({addonId: 'dummy', userContextId: 42});
+ checkOriginAttributes(exampleOrg_userContextAddon, {addonId: 'dummy', userContextId: 42}, '^addonId=dummy&userContextId=42');
+ checkOriginAttributes(nullPrin_userContextAddon, {addonId: 'dummy', userContextId: 42}, '^addonId=dummy&userContextId=42');
+ do_check_eq(exampleOrg_userContextAddon.origin, 'http://example.org^addonId=dummy&userContextId=42');
+
+ // UserContext and App.
+ var exampleOrg_userContextApp = ssm.createCodebasePrincipal(makeURI('http://example.org'), {appId: 24, userContextId: 42});
+ var nullPrin_userContextApp = ssm.createNullPrincipal({appId: 24, userContextId: 42});
+ checkOriginAttributes(exampleOrg_userContextApp, {appId: 24, userContextId: 42}, '^appId=24&userContextId=42');
+ checkOriginAttributes(nullPrin_userContextApp, {appId: 24, userContextId: 42}, '^appId=24&userContextId=42');
+ do_check_eq(exampleOrg_userContextApp.origin, 'http://example.org^appId=24&userContextId=42');
+
+ checkSandboxOriginAttributes(null, {});
+ checkSandboxOriginAttributes('http://example.org', {});
+ checkSandboxOriginAttributes('http://example.org', {}, {originAttributes: {}});
+ checkSandboxOriginAttributes('http://example.org', {appId: 42}, {originAttributes: {appId: 42}});
+ checkSandboxOriginAttributes(['http://example.org'], {});
+ checkSandboxOriginAttributes(['http://example.org'], {}, {originAttributes: {}});
+ checkSandboxOriginAttributes(['http://example.org'], {appId: 42}, {originAttributes: {appId: 42}});
+
+ // Check that all of the above are cross-origin.
+ checkCrossOrigin(exampleOrg_app, exampleOrg);
+ checkCrossOrigin(exampleOrg_app, nullPrin_app);
+ checkCrossOrigin(exampleOrg_browser, exampleOrg_app);
+ checkCrossOrigin(exampleOrg_browser, nullPrin_browser);
+ checkCrossOrigin(exampleOrg_appBrowser, exampleOrg_app);
+ checkCrossOrigin(exampleOrg_appBrowser, nullPrin_appBrowser);
+ checkCrossOrigin(exampleOrg_appBrowser, exampleCom_appBrowser);
+ checkCrossOrigin(exampleOrg_addon, exampleOrg);
+ checkCrossOrigin(exampleOrg_firstPartyDomain, exampleOrg);
+ checkCrossOrigin(exampleOrg_userContext, exampleOrg);
+ checkCrossOrigin(exampleOrg_userContextAddon, exampleOrg);
+ checkCrossOrigin(exampleOrg_userContext, exampleOrg_userContextAddon);
+ checkCrossOrigin(exampleOrg_userContext, exampleOrg_userContextApp);
+
+ // Check Principal kinds.
+ function checkKind(prin, kind) {
+ do_check_eq(prin.isNullPrincipal, kind == 'nullPrincipal');
+ do_check_eq(prin.isCodebasePrincipal, kind == 'codebasePrincipal');
+ do_check_eq(prin.isExpandedPrincipal, kind == 'expandedPrincipal');
+ do_check_eq(prin.isSystemPrincipal, kind == 'systemPrincipal');
+ }
+ checkKind(ssm.createNullPrincipal({}), 'nullPrincipal');
+ checkKind(ssm.createCodebasePrincipal(makeURI('http://www.example.com'), {}), 'codebasePrincipal');
+ checkKind(Cu.getObjectPrincipal(Cu.Sandbox([ssm.createCodebasePrincipal(makeURI('http://www.example.com'), {})])), 'expandedPrincipal');
+ checkKind(ssm.getSystemPrincipal(), 'systemPrincipal');
+
+ //
+ // Test Origin Attribute Manipulation
+ //
+
+ // check that we can create an empty origin attributes dict with default
+ // members and values.
+ var emptyAttrs = ChromeUtils.fillNonDefaultOriginAttributes({});
+ checkValues(emptyAttrs);
+
+ var uri = "http://example.org";
+ var tests = [
+ [ "", {} ],
+ [ "^appId=5", {appId: 5} ],
+ [ "^userContextId=3", {userContextId: 3} ],
+ [ "^addonId=fooBar", {addonId: "fooBar"} ],
+ [ "^inBrowser=1", {inIsolatedMozBrowser: true} ],
+ [ "^firstPartyDomain=example.org", {firstPartyDomain: "example.org"} ],
+ [ "^appId=3&inBrowser=1&userContextId=6",
+ {appId: 3, userContextId: 6, inIsolatedMozBrowser: true} ] ];
+
+ // check that we can create an origin attributes from an origin properly
+ tests.forEach(t => {
+ let attrs = ChromeUtils.createOriginAttributesFromOrigin(uri + t[0]);
+ checkValues(attrs, t[1]);
+ do_check_eq(ChromeUtils.originAttributesToSuffix(attrs), t[0]);
+ });
+
+ // check that we can create an origin attributes from a dict properly
+ tests.forEach(t => {
+ let attrs = ChromeUtils.fillNonDefaultOriginAttributes(t[1]);
+ checkValues(attrs, t[1]);
+ do_check_eq(ChromeUtils.originAttributesToSuffix(attrs), t[0]);
+ });
+
+ // each row in the set_tests array has these values:
+ // [0] - the suffix used to create an origin attribute from
+ // [1] - the expected result of creating an origin attribute from [0]
+ // [2] - the pattern to set on the origin attributes
+ // [3] - the expected result of setting [2] values on [1]
+ // [4] - the expected result of creating a suffix from [3]
+ var set_tests = [
+ [ "", {}, {appId: 5}, {appId: 5}, "^appId=5" ],
+ [ "^appId=5", {appId: 5}, {appId: 3}, {appId: 3}, "^appId=3" ],
+ [ "^appId=5", {appId: 5}, {userContextId: 3}, {appId: 5, userContextId: 3}, "^appId=5&userContextId=3" ],
+ [ "^appId=5", {appId: 5}, {appId: 3, userContextId: 7}, {appId: 3, userContextId: 7}, "^appId=3&userContextId=7" ] ];
+
+ // check that we can set origin attributes values properly
+ set_tests.forEach(t => {
+ let orig = ChromeUtils.createOriginAttributesFromOrigin(uri + t[0]);
+ checkValues(orig, t[1]);
+ let mod = orig;
+ for (var key in t[2]) {
+ mod[key] = t[2][key];
+ }
+ checkValues(mod, t[3]);
+ do_check_eq(ChromeUtils.originAttributesToSuffix(mod), t[4]);
+ });
+
+ // each row in the dflt_tests array has these values:
+ // [0] - the suffix used to create an origin attribute from
+ // [1] - the expected result of creating an origin attributes from [0]
+ // [2] - the expected result after setting userContextId to the default
+ // [3] - the expected result of creating a suffix from [2]
+ var dflt_tests = [
+ [ "", {}, {}, "" ],
+ [ "^userContextId=3", {userContextId: 3}, {}, "" ],
+ [ "^appId=5", {appId: 5}, {appId: 5}, "^appId=5" ],
+ [ "^appId=5&userContextId=3", {appId: 5, userContextId: 3}, {appId: 5}, "^appId=5" ] ];
+
+ // check that we can set the userContextId to default properly
+ dflt_tests.forEach(t => {
+ let orig = ChromeUtils.createOriginAttributesFromOrigin(uri + t[0]);
+ checkValues(orig, t[1]);
+ let mod = orig;
+ mod['userContextId'] = 0;
+ checkValues(mod, t[2]);
+ do_check_eq(ChromeUtils.originAttributesToSuffix(mod), t[3]);
+ });
+
+ // each row in the dflt2_tests array has these values:
+ // [0] - the suffix used to create an origin attribute from
+ // [1] - the expected result of creating an origin attributes from [0]
+ // [2] - the expected result after setting firstPartyUri to the default
+ // [3] - the expected result of creating a suffix from [2]
+ var dflt2_tests = [
+ [ "", {}, {}, "" ],
+ [ "^firstPartyDomain=foo.com", {firstPartyDomain: "foo.com"}, {}, "" ],
+ [ "^appId=5", {appId: 5}, {appId: 5}, "^appId=5" ],
+ [ "^appId=5&firstPartyDomain=foo.com", {appId: 5, firstPartyDomain: "foo.com"}, {appId: 5}, "^appId=5" ] ];
+
+ // check that we can set the userContextId to default properly
+ dflt2_tests.forEach(t => {
+ let orig = ChromeUtils.createOriginAttributesFromOrigin(uri + t[0]);
+ checkValues(orig, t[1]);
+ let mod = orig;
+ mod['firstPartyDomain'] = "";
+ checkValues(mod, t[2]);
+ do_check_eq(ChromeUtils.originAttributesToSuffix(mod), t[3]);
+ });
+
+}
diff --git a/caps/tests/unit/xpcshell.ini b/caps/tests/unit/xpcshell.ini
new file mode 100644
index 000000000..c06a6b4e9
--- /dev/null
+++ b/caps/tests/unit/xpcshell.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+head =
+tail =
+
+[test_origin.js]