diff options
Diffstat (limited to '')
-rw-r--r-- | toolkit/components/extensions/MatchPattern.h | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/toolkit/components/extensions/MatchPattern.h b/toolkit/components/extensions/MatchPattern.h new file mode 100644 index 0000000000..f0f752fa45 --- /dev/null +++ b/toolkit/components/extensions/MatchPattern.h @@ -0,0 +1,393 @@ +/* -*- 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; + + 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; +}; + +// 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 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 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 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 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 |