/* -*- 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_extensions_MatchPattern_h
#define mozilla_extensions_MatchPattern_h
#include <utility>
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/MatchPatternBinding.h"
#include "mozilla/extensions/MatchGlob.h"
#include "jspubtd.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Likely.h"
#include "mozilla/Maybe.h"
#include "mozilla/RefCounted.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsTArray.h"
#include "nsAtom.h"
#include "nsICookie.h"
#include "nsISupports.h"
#include "nsIURI.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace extensions {
using dom::MatchPatternOptions;
// A sorted, immutable, binary-search-backed set of atoms, optimized for
// frequent lookups.
class AtomSet final {
public:
using ArrayType = AutoTArray<RefPtr<nsAtom>, 1>;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AtomSet)
explicit AtomSet(
const nsTArray<nsString>& aElems);
MOZ_IMPLICIT AtomSet(std::initializer_list<nsAtom*> aIL);
bool Contains(
const nsAString& elem)
const {
RefPtr<nsAtom> atom = NS_Atomize(elem);
return Contains(atom);
}
bool Contains(
const nsACString& aElem)
const {
RefPtr<nsAtom> atom = NS_Atomize(aElem);
return Contains(atom);
}
bool Contains(
const nsAtom* aAtom)
const {
return mElems.ContainsSorted(aAtom);
}
bool Intersects(
const AtomSet& aOther)
const;
void Get(nsTArray<nsString>& aResult)
const {
aResult.SetCapacity(mElems.Length());
for (
const auto& atom : mElems) {
aResult.AppendElement(nsDependentAtomString(atom));
}
}
auto begin()
const -> decltype(std::declval<
const ArrayType>().begin()) {
return mElems.begin();
}
auto end()
const -> decltype(std::declval<
const ArrayType>().end()) {
return mElems.end();
}
private:
~AtomSet() =
default;
const ArrayType mElems;
};
// A helper class to lazily retrieve, transcode, and atomize certain URI
// properties the first time they're used, and cache the results, so that they
// can be used across multiple match operations.
class URLInfo final {
public:
MOZ_IMPLICIT URLInfo(nsIURI* aURI) : mURI(aURI) { mHost.SetIsVoid(
true); }
URLInfo(nsIURI* aURI,
bool aNoRef) : URLInfo(aURI) {
if (aNoRef) {
mURINoRef = mURI;
}
}
URLInfo(
const URLInfo& aOther) : URLInfo(aOther.mURI.get()) {}
nsIURI* URI()
const {
return mURI; }
nsAtom* Scheme()
const;
const nsCString& Host()
const;
const nsAtom* HostAtom()
const;
const nsCString& Path()
const;
const nsCString& FilePath()
const;
const nsString& Spec()
const;
const nsCString& CSpec()
const;
bool InheritsPrincipal()
const;
// Whether the URL is a meaningful representation of a document URL, even if
// that document were to have an opaque origin (null principal).
// These URLs are more verbose than the precursor (origin) of a null
// principal, and therefore preferred for matching purposes.
//
// NOTE: This method is main-thread only
bool IsNonOpaqueURL()
const;
private:
nsIURI* URINoRef()
const;
nsCOMPtr<nsIURI> mURI;
mutable nsCOMPtr<nsIURI> mURINoRef;
mutable RefPtr<nsAtom> mScheme;
mutable nsCString mHost;
mutable RefPtr<nsAtom> mHostAtom;
mutable nsCString mPath;
mutable nsCString mFilePath;
mutable nsString mSpec;
mutable nsCString mCSpec;
mutable Maybe<
bool> mInheritsPrincipal;
mutable Maybe<
bool> mIsNonOpaqueURL;
};
// Similar to URLInfo, but for cookies.
class MOZ_STACK_CLASS CookieInfo final {
public:
MOZ_IMPLICIT CookieInfo(nsICookie* aCookie) : mCookie(aCookie) {}
bool IsSecure()
const;
bool IsDomain()
const;
const nsCString& Host()
const;
const nsCString& RawHost()
const;
private:
nsCOMPtr<nsICookie> mCookie;
mutable Maybe<
bool> mIsSecure;
mutable Maybe<
bool> mIsDomain;
mutable nsCString mHost;
mutable nsCString mRawHost;
};
class MatchPatternCore final {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MatchPatternCore)
// NOTE: Must be constructed on the main thread!
MatchPatternCore(
const nsAString& aPattern,
bool aIgnorePath,
bool aRestrictSchemes, ErrorResult& aRv);
bool Matches(
const nsAString& aURL,
bool aExplicit, ErrorResult& aRv)
const;
bool Matches(
const URLInfo& aURL,
bool aExplicit =
false)
const;
bool MatchesAllWebUrls()
const;
// Helper for MatchPatternSetCore::MatchesAllWebUrls:
bool MatchesAllUrlsWithScheme(
const nsAtom* aScheme)
const;
bool MatchesCookie(
const CookieInfo& aCookie)
const;
bool MatchesDomain(
const nsACString& aDomain)
const;
bool Subsumes(
const MatchPatternCore& aPattern)
const;
bool SubsumesDomain(
const MatchPatternCore& aPattern)
const;
bool Overlaps(
const MatchPatternCore& aPattern)
const;
bool DomainIsWildcard()
const {
return mMatchSubdomain && mDomain.IsEmpty(); }
void GetPattern(nsAString& aPattern)
const { aPattern = mPattern; }
private:
~MatchPatternCore() =
default;
// The normalized match pattern string that this object represents.
nsString mPattern;
// The set of atomized URI schemes that this pattern matches.
RefPtr<AtomSet> mSchemes;
// The domain that this matcher matches. If mMatchSubdomain is false, only
// matches the exact domain. If it's true, matches the domain or any
// subdomain.
//
// For instance, "*.foo.com" gives mDomain = "foo.com" and mMatchSubdomain =
// true, and matches "foo.com" or "bar.foo.com" but not "barfoo.com".
//
// While "foo.com" gives mDomain = "foo.com" and mMatchSubdomain = false,
// and matches "foo.com" but not "bar.foo.com".
nsCString mDomain;
bool mMatchSubdomain =
false;
// The glob against which the URL path must match. If null, the path is
// ignored entirely. If non-null, the path must match this glob.
RefPtr<MatchGlobCore> mPath;
};
class MatchPattern final :
public nsISupports,
public nsWrapperCache {
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(MatchPattern)
static already_AddRefed<MatchPattern> Constructor(
dom::GlobalObject& aGlobal,
const nsAString& aPattern,
const MatchPatternOptions& aOptions, ErrorResult& aRv);
bool Matches(
const nsAString& aURL,
bool aExplicit, ErrorResult& aRv)
const {
return Core()->Matches(aURL, aExplicit, aRv);
}
bool MatchesAllWebUrls()
const {
return Core()->MatchesAllWebUrls(); }
bool Matches(
const URLInfo& aURL,
bool aExplicit =
false)
const {
return Core()->Matches(aURL, aExplicit);
}
bool Matches(
const URLInfo& aURL,
bool aExplicit, ErrorResult& aRv)
const {
return Matches(aURL, aExplicit);
}
bool MatchesCookie(
const CookieInfo& aCookie)
const {
return Core()->MatchesCookie(aCookie);
}
bool MatchesDomain(
const nsACString& aDomain)
const {
return Core()->MatchesDomain(aDomain);
}
bool Subsumes(
const MatchPattern& aPattern)
const {
return Core()->Subsumes(*aPattern.Core());
}
bool SubsumesDomain(
const MatchPattern& aPattern)
const {
return Core()->SubsumesDomain(*aPattern.Core());
}
bool Overlaps(
const MatchPattern& aPattern)
const {
return Core()->Overlaps(*aPattern.Core());
}
bool DomainIsWildcard()
const {
return Core()->DomainIsWildcard(); }
void GetPattern(nsAString& aPattern)
const { Core()->GetPattern(aPattern); }
MatchPatternCore* Core()
const {
return mCore; }
nsISupports* GetParentObject()
const {
return mParent; }
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
protected:
virtual ~MatchPattern() =
default;
private:
friend class MatchPatternSet;
explicit MatchPattern(nsISupports* aParent,
already_AddRefed<MatchPatternCore> aCore)
: mParent(aParent), mCore(std::move(aCore)) {}
void Init(JSContext* aCx,
const nsAString& aPattern,
bool aIgnorePath,
bool aRestrictSchemes, ErrorResult& aRv);
nsCOMPtr<nsISupports> mParent;
RefPtr<MatchPatternCore> mCore;
public:
// A quick way to check if a particular URL matches <all_urls> without
// actually instantiating a MatchPattern
static bool MatchesAllURLs(
const URLInfo& aURL);
};
class MatchPatternSetCore final {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MatchPatternSetCore)
using ArrayType = nsTArray<RefPtr<MatchPatternCore>>;
explicit MatchPatternSetCore(ArrayType&& aPatterns)
: mPatterns(std::move(aPatterns)) {}
static already_AddRefed<MatchPatternSet> Constructor(
dom::GlobalObject& aGlobal,
const nsTArray<dom::OwningStringOrMatchPattern>& aPatterns,
const MatchPatternOptions& aOptions, ErrorResult& aRv);
bool Matches(
const nsAString& aURL,
bool aExplicit, ErrorResult& aRv)
const;
bool Matches(
const URLInfo& aURL,
bool aExplicit =
false)
const;
bool MatchesAllWebUrls()
const;
bool MatchesCookie(
const CookieInfo& aCookie)
const;
bool Subsumes(
const MatchPatternCore& aPattern)
const;
bool SubsumesDomain(
const MatchPatternCore& aPattern)
const;
bool Overlaps(
const MatchPatternCore& aPattern)
const;
bool Overlaps(
const MatchPatternSetCore& aPatternSet)
const;
bool OverlapsAll(
const MatchPatternSetCore& aPatternSet)
const;
void GetPatterns(ArrayType& aPatterns) {
aPatterns.AppendElements(mPatterns);
}
private:
friend class MatchPatternSet;
~MatchPatternSetCore() =
default;
ArrayType mPatterns;
};
class MatchPatternSet final :
public nsISupports,
public nsWrapperCache {
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(MatchPatternSet)
using ArrayType = nsTArray<RefPtr<MatchPattern>>;
static already_AddRefed<MatchPatternSet> Constructor(
dom::GlobalObject& aGlobal,
const nsTArray<dom::OwningStringOrMatchPattern>& aPatterns,
const MatchPatternOptions& aOptions, ErrorResult& aRv);
bool Matches(
const nsAString& aURL,
bool aExplicit, ErrorResult& aRv)
const {
return Core()->Matches(aURL, aExplicit, aRv);
}
bool Matches(
const URLInfo& aURL,
bool aExplicit =
false)
const {
return Core()->Matches(aURL, aExplicit);
}
bool Matches(
const URLInfo& aURL,
bool aExplicit, ErrorResult& aRv)
const {
return Matches(aURL, aExplicit);
}
bool MatchesAllWebUrls()
const {
return Core()->MatchesAllWebUrls(); }
bool MatchesCookie(
const CookieInfo& aCookie)
const {
return Core()->MatchesCookie(aCookie);
}
bool Subsumes(
const MatchPattern& aPattern)
const {
return Core()->Subsumes(*aPattern.Core());
}
bool SubsumesDomain(
const MatchPattern& aPattern)
const {
return Core()->SubsumesDomain(*aPattern.Core());
}
bool Overlaps(
const MatchPattern& aPattern)
const {
return Core()->Overlaps(*aPattern.Core());
}
bool Overlaps(
const MatchPatternSet& aPatternSet)
const {
return Core()->Overlaps(*aPatternSet.Core());
}
bool OverlapsAll(
const MatchPatternSet& aPatternSet)
const {
return Core()->OverlapsAll(*aPatternSet.Core());
}
void GetPatterns(ArrayType& aPatterns);
MatchPatternSetCore* Core()
const {
return mCore; }
nsISupports* GetParentObject()
const {
return mParent; }
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
protected:
virtual ~MatchPatternSet() =
default;
private:
explicit MatchPatternSet(nsISupports* aParent,
already_AddRefed<MatchPatternSetCore> aCore)
: mParent(aParent), mCore(std::move(aCore)) {}
nsCOMPtr<nsISupports> mParent;
RefPtr<MatchPatternSetCore> mCore;
mozilla::Maybe<ArrayType> mPatternsCache;
};
}
// namespace extensions
}
// namespace mozilla
#endif // mozilla_extensions_MatchPattern_h