tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ContentPrincipal.cpp (24019B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=2 sw=2 et tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "ContentPrincipal.h"
      8 
      9 #include "mozIThirdPartyUtil.h"
     10 #include "nsContentUtils.h"
     11 #include "nscore.h"
     12 #include "nsScriptSecurityManager.h"
     13 #include "nsString.h"
     14 #include "nsReadableUtils.h"
     15 #include "pratom.h"
     16 #include "nsIURI.h"
     17 #include "nsIURL.h"
     18 #include "nsIStandardURL.h"
     19 #include "nsIURIWithSpecialOrigin.h"
     20 #include "nsIURIMutator.h"
     21 #include "nsJSPrincipals.h"
     22 #include "nsIEffectiveTLDService.h"
     23 #include "nsIClassInfoImpl.h"
     24 #include "nsIObjectInputStream.h"
     25 #include "nsIObjectOutputStream.h"
     26 #include "nsIProtocolHandler.h"
     27 #include "nsError.h"
     28 #include "nsIContentSecurityPolicy.h"
     29 #include "nsNetCID.h"
     30 #include "js/RealmIterators.h"
     31 #include "js/Wrapper.h"
     32 
     33 #include "mozilla/dom/BlobURLProtocolHandler.h"
     34 #include "mozilla/dom/ScriptSettings.h"
     35 #include "mozilla/ClearOnShutdown.h"
     36 #include "mozilla/ExtensionPolicyService.h"
     37 #include "mozilla/Preferences.h"
     38 
     39 #include "nsSerializationHelper.h"
     40 
     41 #include "js/JSON.h"
     42 #include "ContentPrincipalJSONHandler.h"
     43 
     44 using namespace mozilla;
     45 
     46 NS_IMPL_CLASSINFO(ContentPrincipal, nullptr, 0, NS_PRINCIPAL_CID)
     47 NS_IMPL_QUERY_INTERFACE_CI(ContentPrincipal, nsIPrincipal)
     48 NS_IMPL_CI_INTERFACE_GETTER(ContentPrincipal, nsIPrincipal)
     49 
     50 ContentPrincipal::ContentPrincipal(nsIURI* aURI,
     51                                   const OriginAttributes& aOriginAttributes,
     52                                   const nsACString& aOriginNoSuffix,
     53                                   nsIURI* aInitialDomain)
     54    : BasePrincipal(eContentPrincipal, aOriginNoSuffix, aOriginAttributes),
     55      mURI(aURI),
     56      mDomain(aInitialDomain) {
     57  if (mDomain) {
     58    // We're just creating the principal, so no need to re-compute wrappers.
     59    SetHasExplicitDomain();
     60  }
     61 
     62 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
     63  // Assert that the URI we get here isn't any of the schemes that we know we
     64  // should not get here.  These schemes always either inherit their principal
     65  // or fall back to a null principal.  These are schemes which return
     66  // URI_INHERITS_SECURITY_CONTEXT from their protocol handler's
     67  // GetProtocolFlags function.
     68  bool hasFlag = false;
     69  MOZ_DIAGNOSTIC_ASSERT(
     70      NS_SUCCEEDED(NS_URIChainHasFlags(
     71          aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &hasFlag)) &&
     72      !hasFlag);
     73 #endif
     74 }
     75 
     76 ContentPrincipal::ContentPrincipal(ContentPrincipal* aOther,
     77                                   const OriginAttributes& aOriginAttributes)
     78    : BasePrincipal(aOther, aOriginAttributes),
     79      mURI(aOther->mURI),
     80      mDomain(aOther->mDomain),
     81      mAddon(aOther->mAddon) {}
     82 
     83 ContentPrincipal::~ContentPrincipal() = default;
     84 
     85 nsresult ContentPrincipal::GetScriptLocation(nsACString& aStr) {
     86  return mURI->GetSpec(aStr);
     87 }
     88 
     89 /* static */
     90 nsresult ContentPrincipal::GenerateOriginNoSuffixFromURI(
     91    nsIURI* aURI, nsACString& aOriginNoSuffix) {
     92  if (!aURI) {
     93    return NS_ERROR_FAILURE;
     94  }
     95 
     96  nsCOMPtr<nsIURI> origin = NS_GetInnermostURI(aURI);
     97  if (!origin) {
     98    return NS_ERROR_FAILURE;
     99  }
    100 
    101  MOZ_ASSERT(!NS_IsAboutBlankAllowQueryAndFragment(origin),
    102             "The inner URI for about:blank must be moz-safe-about:blank");
    103 
    104  // Handle non-strict file:// uris.
    105  if (!nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
    106      NS_URIIsLocalFile(origin)) {
    107    // If strict file origin policy is not in effect, all local files are
    108    // considered to be same-origin, so return a known dummy origin here.
    109    aOriginNoSuffix.AssignLiteral("file://UNIVERSAL_FILE_URI_ORIGIN");
    110    return NS_OK;
    111  }
    112 
    113  nsresult rv;
    114 // NB: This is only compiled for Thunderbird/Suite.
    115 #if IS_ORIGIN_IS_FULL_SPEC_DEFINED
    116  bool fullSpec = false;
    117  rv = NS_URIChainHasFlags(origin, nsIProtocolHandler::ORIGIN_IS_FULL_SPEC,
    118                           &fullSpec);
    119  NS_ENSURE_SUCCESS(rv, rv);
    120  if (fullSpec) {
    121    return origin->GetAsciiSpec(aOriginNoSuffix);
    122  }
    123 #endif
    124 
    125  // We want the invariant that prinA.origin == prinB.origin i.f.f.
    126  // prinA.equals(prinB). However, this requires that we impose certain
    127  // constraints on the behavior and origin semantics of principals, and in
    128  // particular, forbid creating origin strings for principals whose equality
    129  // constraints are not expressible as strings (i.e. object equality).
    130  // Moreover, we want to forbid URIs containing the magic "^" we use as a
    131  // separating character for origin attributes.
    132  //
    133  // These constraints can generally be achieved by restricting .origin to
    134  // nsIStandardURL-based URIs, but there are a few other URI schemes that we
    135  // need to handle.
    136  if (origin->SchemeIs("about") ||
    137      (origin->SchemeIs("moz-safe-about") &&
    138       // We generally consider two about:foo origins to be same-origin, but
    139       // about:blank is special since it can be generated from different
    140       // sources. We check for moz-safe-about:blank since origin is an
    141       // innermost URI.
    142       !StringBeginsWith(origin->GetSpecOrDefault(),
    143                         "moz-safe-about:blank"_ns))) {
    144    rv = origin->GetAsciiSpec(aOriginNoSuffix);
    145    NS_ENSURE_SUCCESS(rv, rv);
    146 
    147    int32_t pos = aOriginNoSuffix.FindChar('?');
    148    int32_t hashPos = aOriginNoSuffix.FindChar('#');
    149 
    150    if (hashPos != kNotFound && (pos == kNotFound || hashPos < pos)) {
    151      pos = hashPos;
    152    }
    153 
    154    if (pos != kNotFound) {
    155      aOriginNoSuffix.Truncate(pos);
    156    }
    157 
    158    // These URIs could technically contain a '^', but they never should.
    159    if (NS_WARN_IF(aOriginNoSuffix.FindChar('^', 0) != -1)) {
    160      aOriginNoSuffix.Truncate();
    161      return NS_ERROR_FAILURE;
    162    }
    163    return NS_OK;
    164  }
    165 
    166  // This URL can be a blobURL. In this case, we should use the 'parent'
    167  // principal instead.
    168  nsCOMPtr<nsIPrincipal> blobPrincipal;
    169  if (dom::BlobURLProtocolHandler::GetBlobURLPrincipal(
    170          origin, getter_AddRefs(blobPrincipal))) {
    171    MOZ_ASSERT(blobPrincipal);
    172    return blobPrincipal->GetOriginNoSuffix(aOriginNoSuffix);
    173  }
    174 
    175  // If we reached this branch, we can only create an origin if we have a
    176  // nsIStandardURL.  So, we query to a nsIStandardURL, and fail if we aren't
    177  // an instance of an nsIStandardURL nsIStandardURLs have the good property
    178  // of escaping the '^' character in their specs, which means that we can be
    179  // sure that the caret character (which is reserved for delimiting the end
    180  // of the spec, and the beginning of the origin attributes) is not present
    181  // in the origin string
    182  nsCOMPtr<nsIStandardURL> standardURL = do_QueryInterface(origin);
    183  if (!standardURL) {
    184    return NS_ERROR_FAILURE;
    185  }
    186 
    187  // See whether we have a useful hostPort. If we do, use that.
    188  nsAutoCString hostPort;
    189  if (!origin->SchemeIs("chrome")) {
    190    rv = origin->GetAsciiHostPort(hostPort);
    191    NS_ENSURE_SUCCESS(rv, rv);
    192  }
    193  if (!hostPort.IsEmpty()) {
    194    rv = origin->GetScheme(aOriginNoSuffix);
    195    NS_ENSURE_SUCCESS(rv, rv);
    196    aOriginNoSuffix.AppendLiteral("://");
    197    aOriginNoSuffix.Append(hostPort);
    198    return NS_OK;
    199  }
    200 
    201  rv = aURI->GetAsciiSpec(aOriginNoSuffix);
    202  NS_ENSURE_SUCCESS(rv, rv);
    203 
    204  // The origin, when taken from the spec, should not contain the ref part of
    205  // the URL.
    206 
    207  int32_t pos = aOriginNoSuffix.FindChar('?');
    208  int32_t hashPos = aOriginNoSuffix.FindChar('#');
    209 
    210  if (hashPos != kNotFound && (pos == kNotFound || hashPos < pos)) {
    211    pos = hashPos;
    212  }
    213 
    214  if (pos != kNotFound) {
    215    aOriginNoSuffix.Truncate(pos);
    216  }
    217 
    218  return NS_OK;
    219 }
    220 
    221 bool ContentPrincipal::SubsumesInternal(
    222    nsIPrincipal* aOther,
    223    BasePrincipal::DocumentDomainConsideration aConsideration) {
    224  MOZ_ASSERT(aOther);
    225 
    226  // For ContentPrincipal, Subsumes is equivalent to Equals.
    227  if (aOther == this) {
    228    return true;
    229  }
    230 
    231  // If either the subject or the object has changed its principal by
    232  // explicitly setting document.domain then the other must also have
    233  // done so in order to be considered the same origin. This prevents
    234  // DNS spoofing based on document.domain (154930)
    235  if (aConsideration == ConsiderDocumentDomain) {
    236    // Get .domain on each principal.
    237    nsCOMPtr<nsIURI> thisDomain, otherDomain;
    238    GetDomain(getter_AddRefs(thisDomain));
    239    aOther->GetDomain(getter_AddRefs(otherDomain));
    240 
    241    // If either has .domain set, we have equality i.f.f. the domains match.
    242    // Otherwise, we fall through to the non-document-domain-considering case.
    243    if (thisDomain || otherDomain) {
    244      bool isMatch =
    245          nsScriptSecurityManager::SecurityCompareURIs(thisDomain, otherDomain);
    246 #ifdef DEBUG
    247      if (isMatch) {
    248        nsAutoCString thisSiteOrigin, otherSiteOrigin;
    249        MOZ_ALWAYS_SUCCEEDS(GetSiteOrigin(thisSiteOrigin));
    250        MOZ_ALWAYS_SUCCEEDS(aOther->GetSiteOrigin(otherSiteOrigin));
    251        MOZ_ASSERT(
    252            thisSiteOrigin == otherSiteOrigin,
    253            "SubsumesConsideringDomain passed with mismatched siteOrigin!");
    254      }
    255 #endif
    256      return isMatch;
    257    }
    258  }
    259 
    260  // Do a fast check (including origin attributes) or a slow uri comparison.
    261  return FastEquals(aOther) || aOther->IsSameOrigin(mURI);
    262 }
    263 
    264 NS_IMETHODIMP
    265 ContentPrincipal::GetURI(nsIURI** aURI) {
    266  *aURI = do_AddRef(mURI).take();
    267  return NS_OK;
    268 }
    269 
    270 bool ContentPrincipal::MayLoadInternal(nsIURI* aURI) {
    271  MOZ_ASSERT(aURI);
    272 
    273 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
    274  nsCOMPtr<nsIURIWithSpecialOrigin> uriWithSpecialOrigin =
    275      do_QueryInterface(aURI);
    276  if (uriWithSpecialOrigin) {
    277    nsCOMPtr<nsIURI> origin;
    278    nsresult rv = uriWithSpecialOrigin->GetOrigin(getter_AddRefs(origin));
    279    if (NS_WARN_IF(NS_FAILED(rv))) {
    280      return false;
    281    }
    282    MOZ_ASSERT(origin);
    283    OriginAttributes attrs;
    284    RefPtr<BasePrincipal> principal =
    285        BasePrincipal::CreateContentPrincipal(origin, attrs);
    286    return nsIPrincipal::Subsumes(principal);
    287  }
    288 #endif
    289 
    290  nsCOMPtr<nsIPrincipal> blobPrincipal;
    291  if (dom::BlobURLProtocolHandler::GetBlobURLPrincipal(
    292          aURI, getter_AddRefs(blobPrincipal))) {
    293    MOZ_ASSERT(blobPrincipal);
    294    return nsIPrincipal::Subsumes(blobPrincipal);
    295  }
    296 
    297  // If this principal is associated with an addon, check whether that addon
    298  // has been given permission to load from this domain.
    299  if (AddonAllowsLoad(aURI)) {
    300    return true;
    301  }
    302 
    303  if (nsScriptSecurityManager::SecurityCompareURIs(mURI, aURI)) {
    304    return true;
    305  }
    306 
    307  return false;
    308 }
    309 
    310 NS_IMETHODIMP
    311 ContentPrincipal::GetDomain(nsIURI** aDomain) {
    312  if (!GetHasExplicitDomain()) {
    313    *aDomain = nullptr;
    314    return NS_OK;
    315  }
    316 
    317  mozilla::MutexAutoLock lock(mMutex);
    318  NS_ADDREF(*aDomain = mDomain);
    319  return NS_OK;
    320 }
    321 
    322 NS_IMETHODIMP
    323 ContentPrincipal::SetDomain(nsIURI* aDomain) {
    324  AssertIsOnMainThread();
    325  MOZ_ASSERT(aDomain);
    326 
    327  {
    328    mozilla::MutexAutoLock lock(mMutex);
    329    mDomain = aDomain;
    330    SetHasExplicitDomain();
    331  }
    332 
    333  // Set the changed-document-domain flag on compartments containing realms
    334  // using this principal.
    335  auto cb = [](JSContext*, void*, JS::Realm* aRealm,
    336               const JS::AutoRequireNoGC& nogc) {
    337    JS::Compartment* comp = JS::GetCompartmentForRealm(aRealm);
    338    xpc::SetCompartmentChangedDocumentDomain(comp);
    339  };
    340  JSPrincipals* principals =
    341      nsJSPrincipals::get(static_cast<nsIPrincipal*>(this));
    342 
    343  dom::AutoJSAPI jsapi;
    344  jsapi.Init();
    345  JS::IterateRealmsWithPrincipals(jsapi.cx(), principals, nullptr, cb);
    346 
    347  return NS_OK;
    348 }
    349 
    350 static nsresult GetSpecialBaseDomain(const nsCOMPtr<nsIURI>& aURI,
    351                                     bool* aHandled, nsACString& aBaseDomain) {
    352  *aHandled = false;
    353 
    354  // Special handling for a file URI.
    355  if (NS_URIIsLocalFile(aURI)) {
    356    // If strict file origin policy is not in effect, all local files are
    357    // considered to be same-origin, so return a known dummy domain here.
    358    if (!nsScriptSecurityManager::GetStrictFileOriginPolicy()) {
    359      *aHandled = true;
    360      aBaseDomain.AssignLiteral("UNIVERSAL_FILE_URI_ORIGIN");
    361      return NS_OK;
    362    }
    363 
    364    // Otherwise, we return the file path.
    365    nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
    366 
    367    if (url) {
    368      *aHandled = true;
    369      return url->GetFilePath(aBaseDomain);
    370    }
    371  }
    372 
    373  bool hasNoRelativeFlag;
    374  nsresult rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_NORELATIVE,
    375                                    &hasNoRelativeFlag);
    376  if (NS_WARN_IF(NS_FAILED(rv))) {
    377    return rv;
    378  }
    379 
    380  // In case of FTP we want to get base domain via TLD service even if FTP
    381  // protocol handler is disabled and the scheme is handled by external protocol
    382  // handler which returns URI_NORELATIVE flag.
    383  if (hasNoRelativeFlag && !aURI->SchemeIs("ftp")) {
    384    *aHandled = true;
    385    return aURI->GetSpec(aBaseDomain);
    386  }
    387 
    388  // For local resources we can't get a meaningful base domain.
    389  bool isUIResource = false;
    390  if (NS_SUCCEEDED(NS_URIChainHasFlags(
    391          aURI, nsIProtocolHandler::URI_IS_UI_RESOURCE, &isUIResource)) &&
    392      isUIResource) {
    393    *aHandled = true;
    394    return aURI->GetPrePath(aBaseDomain);
    395  }
    396 
    397  if (aURI->SchemeIs("indexeddb")) {
    398    *aHandled = true;
    399    return aURI->GetSpec(aBaseDomain);
    400  }
    401 
    402  return NS_OK;
    403 }
    404 
    405 NS_IMETHODIMP
    406 ContentPrincipal::GetBaseDomain(nsACString& aBaseDomain) {
    407  // Handle some special URIs first.
    408  bool handled;
    409  nsresult rv = GetSpecialBaseDomain(mURI, &handled, aBaseDomain);
    410  NS_ENSURE_SUCCESS(rv, rv);
    411 
    412  if (handled) {
    413    return NS_OK;
    414  }
    415 
    416  // For everything else, we ask the TLD service via the ThirdPartyUtil.
    417  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
    418      do_GetService(THIRDPARTYUTIL_CONTRACTID);
    419  if (!thirdPartyUtil) {
    420    return NS_ERROR_FAILURE;
    421  }
    422 
    423  return thirdPartyUtil->GetBaseDomain(mURI, aBaseDomain);
    424 }
    425 
    426 NS_IMETHODIMP
    427 ContentPrincipal::GetSiteOriginNoSuffix(nsACString& aSiteOrigin) {
    428  nsresult rv = GetOriginNoSuffix(aSiteOrigin);
    429  NS_ENSURE_SUCCESS(rv, rv);
    430 
    431  // The originNoSuffix is already normalized when being generated by
    432  // GenerateOriginNoSuffixFromURI. Avoid re-parsing the URI here.
    433  //
    434  // For all URIs which are not http(s), the site-origin matches the full
    435  // origin, so can immediately return.
    436  int32_t schemeEnd = aSiteOrigin.Find("://");
    437  if (schemeEnd == kNotFound) {
    438    return NS_OK;
    439  }
    440 
    441  // Check for a http(s) scheme. For all URIs which are not http(s), the
    442  // site-origin matches the full origin, so we can immediately return.
    443  nsDependentCSubstring scheme(aSiteOrigin, 0, schemeEnd);
    444  if (scheme != "http"_ns && scheme != "https"_ns) {
    445    return NS_OK;
    446  }
    447 
    448  // The site-origin never includes a port (:[0-9]*), so remove it.
    449  // NOTE: This avoids using RFindChar to avoid false-positives.
    450  const char* portStart = aSiteOrigin.EndReading() - 1;
    451  while ('0' <= *portStart && *portStart <= '9') {
    452    --portStart;
    453  }
    454  if (*portStart == ':') {
    455    aSiteOrigin.Truncate(portStart - aSiteOrigin.BeginReading());
    456  }
    457 
    458  nsCOMPtr<nsIEffectiveTLDService> tldService =
    459      do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
    460  if (!tldService) {
    461    return NS_ERROR_NOT_AVAILABLE;
    462  }
    463 
    464  // Get the base-domain from the host. We use the "fromHost" variant to avoid
    465  // unnecessary changes to the site-origin.
    466  nsAutoCString baseDomain;
    467  int32_t hostStart = schemeEnd + 3;
    468  nsDependentCSubstring host(aSiteOrigin, hostStart);
    469  rv = tldService->GetBaseDomainFromHost(host, 0, baseDomain);
    470  if (NS_FAILED(rv)) {
    471    // If this is an IP address or something like "localhost", there's nothing
    472    // else to be done.
    473    if (rv != NS_ERROR_HOST_IS_IP_ADDRESS &&
    474        rv != NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS &&
    475        rv != NS_ERROR_INVALID_ARG) {
    476      return rv;
    477    }
    478    return NS_OK;
    479  }
    480 
    481  // Replace the host in aSiteOrigin with baseDomain.
    482  if (baseDomain != host) {
    483    aSiteOrigin.Replace(hostStart, aSiteOrigin.Length() - hostStart,
    484                        baseDomain);
    485  }
    486  return NS_OK;
    487 }
    488 
    489 nsresult ContentPrincipal::GetSiteIdentifier(SiteIdentifier& aSite) {
    490  nsCString siteOrigin;
    491  nsresult rv = GetSiteOrigin(siteOrigin);
    492  NS_ENSURE_SUCCESS(rv, rv);
    493 
    494  RefPtr<BasePrincipal> principal = CreateContentPrincipal(siteOrigin);
    495  if (!principal) {
    496    NS_WARNING("could not instantiate content principal");
    497    return NS_ERROR_FAILURE;
    498  }
    499 
    500  aSite.Init(principal);
    501  return NS_OK;
    502 }
    503 
    504 RefPtr<extensions::WebExtensionPolicyCore> ContentPrincipal::AddonPolicyCore() {
    505  mozilla::MutexAutoLock lock(mMutex);
    506  if (!mAddon.isSome()) {
    507    NS_ENSURE_TRUE(mURI, nullptr);
    508 
    509    RefPtr<extensions::WebExtensionPolicyCore> core;
    510    if (mURI->SchemeIs("moz-extension")) {
    511      nsCString host;
    512      NS_ENSURE_SUCCESS(mURI->GetHost(host), nullptr);
    513      core = ExtensionPolicyService::GetCoreByHost(host);
    514    }
    515 
    516    mAddon.emplace(core);
    517  }
    518  return *mAddon;
    519 }
    520 
    521 NS_IMETHODIMP
    522 ContentPrincipal::GetAddonId(nsAString& aAddonId) {
    523  if (RefPtr<extensions::WebExtensionPolicyCore> policy = AddonPolicyCore()) {
    524    policy->Id()->ToString(aAddonId);
    525  } else {
    526    aAddonId.Truncate();
    527  }
    528  return NS_OK;
    529 }
    530 
    531 NS_IMETHODIMP
    532 ContentPrincipal::Deserializer::Read(nsIObjectInputStream* aStream) {
    533  MOZ_ASSERT(!mPrincipal);
    534 
    535  nsCOMPtr<nsISupports> supports;
    536  nsCOMPtr<nsIURI> principalURI;
    537  nsresult rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
    538  if (NS_FAILED(rv)) {
    539    return rv;
    540  }
    541 
    542  principalURI = do_QueryInterface(supports);
    543  // Enforce re-parsing about: URIs so that if they change, we continue to use
    544  // their new principals correctly.
    545  if (principalURI->SchemeIs("about")) {
    546    nsAutoCString spec;
    547    principalURI->GetSpec(spec);
    548    NS_ENSURE_SUCCESS(NS_NewURI(getter_AddRefs(principalURI), spec),
    549                      NS_ERROR_FAILURE);
    550  }
    551 
    552  nsCOMPtr<nsIURI> domain;
    553  rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
    554  if (NS_FAILED(rv)) {
    555    return rv;
    556  }
    557 
    558  domain = do_QueryInterface(supports);
    559 
    560  nsAutoCString suffix;
    561  rv = aStream->ReadCString(suffix);
    562  NS_ENSURE_SUCCESS(rv, rv);
    563 
    564  OriginAttributes attrs;
    565  bool ok = attrs.PopulateFromSuffix(suffix);
    566  NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
    567 
    568  // Since Bug 965637 we do not serialize the CSP within the
    569  // Principal anymore. Nevertheless there might still be
    570  // serialized Principals that do have a serialized CSP.
    571  // For now, we just read the CSP here but do not actually
    572  // consume it. Please note that we deliberately ignore
    573  // the return value to avoid CSP deserialization problems.
    574  // After Bug 1508939 we will have a new serialization for
    575  // Principals which allows us to update the code here.
    576  // Additionally, the format for serialized CSPs changed
    577  // within Bug 965637 which also can cause failures within
    578  // the CSP deserialization code.
    579  (void)NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
    580 
    581  nsAutoCString originNoSuffix;
    582  rv = GenerateOriginNoSuffixFromURI(principalURI, originNoSuffix);
    583  NS_ENSURE_SUCCESS(rv, rv);
    584 
    585  mPrincipal =
    586      new ContentPrincipal(principalURI, attrs, originNoSuffix, domain);
    587  return NS_OK;
    588 }
    589 
    590 nsresult ContentPrincipal::WriteJSONInnerProperties(JSONWriter& aWriter) {
    591  nsAutoCString principalURI;
    592  nsresult rv = mURI->GetSpec(principalURI);
    593  NS_ENSURE_SUCCESS(rv, rv);
    594 
    595  // We turn each int enum field into a JSON string key of the object, aWriter
    596  // is set up to be inside of the inner object that has stringified enum keys
    597  // An example inner object might be:
    598  //
    599  // eURI                   eSuffix
    600  //    |                           |
    601  //  {"0": "https://mozilla.com", "2": "^privateBrowsingId=1"}
    602  //    |                |          |         |
    603  //    -----------------------------         |
    604  //         |           |                    |
    605  //        Key          ----------------------
    606  //                                |
    607  //                              Value
    608  WriteJSONProperty<eURI>(aWriter, principalURI);
    609 
    610  if (GetHasExplicitDomain()) {
    611    nsAutoCString domainStr;
    612    {
    613      MutexAutoLock lock(mMutex);
    614      rv = mDomain->GetSpec(domainStr);
    615      NS_ENSURE_SUCCESS(rv, rv);
    616    }
    617    WriteJSONProperty<eDomain>(aWriter, domainStr);
    618  }
    619 
    620  nsAutoCString suffix;
    621  OriginAttributesRef().CreateSuffix(suffix);
    622  if (suffix.Length() > 0) {
    623    WriteJSONProperty<eSuffix>(aWriter, suffix);
    624  }
    625 
    626  return NS_OK;
    627 }
    628 
    629 bool ContentPrincipalJSONHandler::startObject() {
    630  switch (mState) {
    631    case State::Init:
    632      mState = State::StartObject;
    633      break;
    634    default:
    635      NS_WARNING("Unexpected object value");
    636      mState = State::Error;
    637      return false;
    638  }
    639 
    640  return true;
    641 }
    642 
    643 bool ContentPrincipalJSONHandler::propertyName(const JS::Latin1Char* name,
    644                                               size_t length) {
    645  switch (mState) {
    646    case State::StartObject:
    647    case State::AfterPropertyValue: {
    648      if (length != 1) {
    649        NS_WARNING(
    650            nsPrintfCString("Unexpected property name length: %zu", length)
    651                .get());
    652        mState = State::Error;
    653        return false;
    654      }
    655 
    656      char key = char(name[0]);
    657      switch (key) {
    658        case ContentPrincipal::URIKey:
    659          mState = State::URIKey;
    660          break;
    661        case ContentPrincipal::DomainKey:
    662          mState = State::DomainKey;
    663          break;
    664        case ContentPrincipal::SuffixKey:
    665          mState = State::SuffixKey;
    666          break;
    667        default:
    668          NS_WARNING(
    669              nsPrintfCString("Unexpected property name: '%c'", key).get());
    670          mState = State::Error;
    671          return false;
    672      }
    673      break;
    674    }
    675    default:
    676      NS_WARNING("Unexpected property name");
    677      mState = State::Error;
    678      return false;
    679  }
    680 
    681  return true;
    682 }
    683 
    684 bool ContentPrincipalJSONHandler::endObject() {
    685  switch (mState) {
    686    case State::AfterPropertyValue: {
    687      MOZ_ASSERT(mPrincipalURI);
    688      // NOTE: mDomain is optional.
    689 
    690      nsAutoCString originNoSuffix;
    691      nsresult rv = ContentPrincipal::GenerateOriginNoSuffixFromURI(
    692          mPrincipalURI, originNoSuffix);
    693      if (NS_FAILED(rv)) {
    694        mState = State::Error;
    695        return false;
    696      }
    697 
    698      mPrincipal =
    699          new ContentPrincipal(mPrincipalURI, mAttrs, originNoSuffix, mDomain);
    700      MOZ_ASSERT(mPrincipal);
    701 
    702      mState = State::EndObject;
    703      break;
    704    }
    705    default:
    706      NS_WARNING("Unexpected end of object");
    707      mState = State::Error;
    708      return false;
    709  }
    710 
    711  return true;
    712 }
    713 
    714 bool ContentPrincipalJSONHandler::stringValue(const JS::Latin1Char* str,
    715                                              size_t length) {
    716  switch (mState) {
    717    case State::URIKey: {
    718      nsDependentCSubstring spec(reinterpret_cast<const char*>(str), length);
    719 
    720      nsresult rv = NS_NewURI(getter_AddRefs(mPrincipalURI), spec);
    721      if (NS_FAILED(rv)) {
    722        mState = State::Error;
    723        return false;
    724      }
    725 
    726      {
    727        // Enforce re-parsing about: URIs so that if they change, we
    728        // continue to use their new principals correctly.
    729        if (mPrincipalURI->SchemeIs("about")) {
    730          nsAutoCString spec;
    731          mPrincipalURI->GetSpec(spec);
    732          rv = NS_NewURI(getter_AddRefs(mPrincipalURI), spec);
    733          if (NS_FAILED(rv)) {
    734            mState = State::Error;
    735            return false;
    736          }
    737        }
    738      }
    739 
    740      mState = State::AfterPropertyValue;
    741      break;
    742    }
    743    case State::DomainKey: {
    744      nsDependentCSubstring spec(reinterpret_cast<const char*>(str), length);
    745 
    746      nsresult rv = NS_NewURI(getter_AddRefs(mDomain), spec);
    747      if (NS_FAILED(rv)) {
    748        mState = State::Error;
    749        return false;
    750      }
    751 
    752      mState = State::AfterPropertyValue;
    753      break;
    754    }
    755    case State::SuffixKey: {
    756      nsDependentCSubstring attrs(reinterpret_cast<const char*>(str), length);
    757      if (!mAttrs.PopulateFromSuffix(attrs)) {
    758        mState = State::Error;
    759        return false;
    760      }
    761 
    762      mState = State::AfterPropertyValue;
    763      break;
    764    }
    765    default:
    766      NS_WARNING("Unexpected string value");
    767      mState = State::Error;
    768      return false;
    769  }
    770 
    771  return true;
    772 }