Skip to content

Commit

Permalink
Bug 1861819 - Use JS::ParseJSONWithHandler in BasePrincipal. r=peterv
Browse files Browse the repository at this point in the history
This avoid intermediate object tree for "JSON to principal" case.

Differential Revision: https://phabricator.services.mozilla.com/D192146
  • Loading branch information
arai-a committed Jan 26, 2024
1 parent 081d863 commit 31ceef4
Show file tree
Hide file tree
Showing 16 changed files with 1,255 additions and 410 deletions.
369 changes: 178 additions & 191 deletions caps/BasePrincipal.cpp

Large diffs are not rendered by default.

13 changes: 9 additions & 4 deletions caps/BasePrincipal.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ class nsIChannel;
class nsIReferrerInfo;
class nsISupports;
class nsIURI;
namespace Json {
class Value;
}

namespace mozilla {

Expand Down Expand Up @@ -93,6 +90,15 @@ class BasePrincipal : public nsJSPrincipals {
eKindMax = eSystemPrincipal
};

static constexpr char NullPrincipalKey = '0';
static_assert(eNullPrincipal == 0);
static constexpr char ContentPrincipalKey = '1';
static_assert(eContentPrincipal == 1);
static constexpr char ExpandedPrincipalKey = '2';
static_assert(eExpandedPrincipal == 2);
static constexpr char SystemPrincipalKey = '3';
static_assert(eSystemPrincipal == 3);

template <typename T>
bool Is() const {
return mKind == T::Kind();
Expand Down Expand Up @@ -195,7 +201,6 @@ class BasePrincipal : public nsJSPrincipals {
nsresult WriteJSONProperties(JSONWriter& aWriter);

static already_AddRefed<BasePrincipal> FromJSON(const nsACString& aJSON);
static already_AddRefed<BasePrincipal> FromJSON(const Json::Value& aJSON);

// Method to write serializable fields which represent all of the fields to
// deserialize the principal.
Expand Down
196 changes: 140 additions & 56 deletions caps/ContentPrincipal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@

#include "nsSerializationHelper.h"

#include "js/JSON.h"
#include "ContentPrincipalJSONHandler.h"

using namespace mozilla;

NS_IMPL_CLASSINFO(ContentPrincipal, nullptr, 0, NS_PRINCIPAL_CID)
Expand Down Expand Up @@ -643,66 +646,147 @@ nsresult ContentPrincipal::WriteJSONInnerProperties(JSONWriter& aWriter) {
return NS_OK;
}

already_AddRefed<BasePrincipal> ContentPrincipal::FromProperties(
nsTArray<ContentPrincipal::KeyVal>& aFields) {
MOZ_ASSERT(aFields.Length() == eMax + 1, "Must have all the keys");
nsresult rv;
nsCOMPtr<nsIURI> principalURI;
nsCOMPtr<nsIURI> domain;
nsCOMPtr<nsIContentSecurityPolicy> csp;
OriginAttributes attrs;
bool ContentPrincipalJSONHandler::startObject() {
switch (mState) {
case State::Init:
mState = State::StartObject;
break;
default:
NS_WARNING("Unexpected object value");
mState = State::Error;
return false;
}

// The odd structure here is to make the code to not compile
// if all the switch enum cases haven't been codified
for (const auto& field : aFields) {
switch (field.key) {
case ContentPrincipal::eURI:
if (!field.valueWasSerialized) {
MOZ_ASSERT(
false,
"Content principals require a principal URI in serialized JSON");
return nullptr;
}
rv = NS_NewURI(getter_AddRefs(principalURI), field.value.get());
NS_ENSURE_SUCCESS(rv, nullptr);

{
// Enforce re-parsing about: URIs so that if they change, we
// continue to use their new principals correctly.
if (principalURI->SchemeIs("about")) {
nsAutoCString spec;
principalURI->GetSpec(spec);
if (NS_FAILED(NS_NewURI(getter_AddRefs(principalURI), spec))) {
return nullptr;
}
}
}
break;
case ContentPrincipal::eDomain:
if (field.valueWasSerialized) {
rv = NS_NewURI(getter_AddRefs(domain), field.value.get());
NS_ENSURE_SUCCESS(rv, nullptr);
}
break;
case ContentPrincipal::eSuffix:
if (field.valueWasSerialized) {
bool ok = attrs.PopulateFromSuffix(field.value);
if (!ok) {
return nullptr;
}
}
break;
return true;
}

bool ContentPrincipalJSONHandler::propertyName(const JS::Latin1Char* name,
size_t length) {
switch (mState) {
case State::StartObject:
case State::AfterPropertyValue: {
if (length != 1) {
NS_WARNING(
nsPrintfCString("Unexpected property name length: %zu", length)
.get());
mState = State::Error;
return false;
}

char key = char(name[0]);
switch (key) {
case ContentPrincipal::URIKey:
mState = State::URIKey;
break;
case ContentPrincipal::DomainKey:
mState = State::DomainKey;
break;
case ContentPrincipal::SuffixKey:
mState = State::SuffixKey;
break;
default:
NS_WARNING(
nsPrintfCString("Unexpected property name: '%c'", key).get());
mState = State::Error;
return false;
}
break;
}
default:
NS_WARNING("Unexpected property name");
mState = State::Error;
return false;
}
nsAutoCString originNoSuffix;
rv = ContentPrincipal::GenerateOriginNoSuffixFromURI(principalURI,
originNoSuffix);
if (NS_FAILED(rv)) {
return nullptr;

return true;
}

bool ContentPrincipalJSONHandler::endObject() {
switch (mState) {
case State::AfterPropertyValue: {
MOZ_ASSERT(mPrincipalURI);
// NOTE: mDomain is optional.

nsAutoCString originNoSuffix;
nsresult rv = ContentPrincipal::GenerateOriginNoSuffixFromURI(
mPrincipalURI, originNoSuffix);
if (NS_FAILED(rv)) {
mState = State::Error;
return false;
}

mPrincipal =
new ContentPrincipal(mPrincipalURI, mAttrs, originNoSuffix, mDomain);
MOZ_ASSERT(mPrincipal);

mState = State::EndObject;
break;
}
default:
NS_WARNING("Unexpected end of object");
mState = State::Error;
return false;
}

RefPtr<ContentPrincipal> principal =
new ContentPrincipal(principalURI, attrs, originNoSuffix, domain);
return true;
}

bool ContentPrincipalJSONHandler::stringValue(const JS::Latin1Char* str,
size_t length) {
switch (mState) {
case State::URIKey: {
nsDependentCSubstring spec(reinterpret_cast<const char*>(str), length);

nsresult rv = NS_NewURI(getter_AddRefs(mPrincipalURI), spec);
if (NS_FAILED(rv)) {
mState = State::Error;
return false;
}

{
// Enforce re-parsing about: URIs so that if they change, we
// continue to use their new principals correctly.
if (mPrincipalURI->SchemeIs("about")) {
nsAutoCString spec;
mPrincipalURI->GetSpec(spec);
rv = NS_NewURI(getter_AddRefs(mPrincipalURI), spec);
if (NS_FAILED(rv)) {
mState = State::Error;
return false;
}
}
}

mState = State::AfterPropertyValue;
break;
}
case State::DomainKey: {
nsDependentCSubstring spec(reinterpret_cast<const char*>(str), length);

nsresult rv = NS_NewURI(getter_AddRefs(mDomain), spec);
if (NS_FAILED(rv)) {
mState = State::Error;
return false;
}

mState = State::AfterPropertyValue;
break;
}
case State::SuffixKey: {
nsDependentCSubstring attrs(reinterpret_cast<const char*>(str), length);
if (!mAttrs.PopulateFromSuffix(attrs)) {
mState = State::Error;
return false;
}

mState = State::AfterPropertyValue;
break;
}
default:
NS_WARNING("Unexpected string value");
mState = State::Error;
return false;
}

return principal.forget();
return true;
}
9 changes: 6 additions & 3 deletions caps/ContentPrincipal.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,13 @@ class ContentPrincipal final : public BasePrincipal {
eSuffix,
eMax = eSuffix
};
typedef mozilla::BasePrincipal::KeyValT<SerializableKeys> KeyVal;

static already_AddRefed<BasePrincipal> FromProperties(
nsTArray<ContentPrincipal::KeyVal>& aFields);
static constexpr char URIKey = '0';
static_assert(eURI == 0);
static constexpr char DomainKey = '1';
static_assert(eDomain == 1);
static constexpr char SuffixKey = '2';
static_assert(eSuffix == 2);

class Deserializer : public BasePrincipal::Deserializer {
public:
Expand Down
94 changes: 94 additions & 0 deletions caps/ContentPrincipalJSONHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/* -*- 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 mozilla_ContentPrincipalJSONHandler_h
#define mozilla_ContentPrincipalJSONHandler_h

#include <stddef.h> // size_t
#include <stdint.h> // uint32_t

#include "js/TypeDecls.h" // JS::Latin1Char

#include "mozilla/AlreadyAddRefed.h" // already_AddRefed
#include "mozilla/RefPtr.h" // RefPtr

#include "nsCOMPtr.h" // nsCOMPtr
#include "nsDebug.h" // NS_WARNING
#include "nsIURI.h" // nsIURI

#include "ContentPrincipal.h"
#include "OriginAttributes.h"
#include "SharedJSONHandler.h"

namespace mozilla {

// JSON parse handler for an inner object for ContentPrincipal.
// Used by PrincipalJSONHandler or SubsumedPrincipalJSONHandler.
class ContentPrincipalJSONHandler : public PrincipalJSONHandlerShared {
enum class State {
Init,

// After the inner object's '{'.
StartObject,

// After the property key for eURI.
URIKey,

// After the property key for eDomain.
DomainKey,

// After the property key for eSuffix.
SuffixKey,

// After the property value for eURI, eDomain, or eSuffix.
AfterPropertyValue,

// After the inner object's '}'.
EndObject,

Error,
};

public:
ContentPrincipalJSONHandler() = default;
virtual ~ContentPrincipalJSONHandler() = default;

virtual bool startObject() override;

using PrincipalJSONHandlerShared::propertyName;
virtual bool propertyName(const JS::Latin1Char* name, size_t length) override;

virtual bool endObject() override;

virtual bool startArray() override {
NS_WARNING("Unexpected array value");
mState = State::Error;
return false;
}
virtual bool endArray() override {
NS_WARNING("Unexpected array value");
mState = State::Error;
return false;
}

using PrincipalJSONHandlerShared::stringValue;
virtual bool stringValue(const JS::Latin1Char* str, size_t length) override;

bool HasAccepted() const { return mState == State::EndObject; }

protected:
virtual void SetErrorState() override { mState = State::Error; }

private:
State mState = State::Init;

nsCOMPtr<nsIURI> mPrincipalURI;
nsCOMPtr<nsIURI> mDomain;
OriginAttributes mAttrs;
};

} // namespace mozilla

#endif // mozilla_ContentPrincipalJSONHandler_h
Loading

0 comments on commit 31ceef4

Please sign in to comment.