/* -*- 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 #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, 1>; NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AtomSet) explicit AtomSet(const nsTArray& aElems); MOZ_IMPLICIT AtomSet(std::initializer_list aIL); bool Contains(const nsAString& elem) const { RefPtr atom = NS_Atomize(elem); return Contains(atom); } bool Contains(const nsACString& aElem) const { RefPtr 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& aResult) const { aResult.SetCapacity(mElems.Length()); for (const auto& atom : mElems) { aResult.AppendElement(nsDependentAtomString(atom)); } } auto begin() const -> decltype(std::declval().begin()) { return mElems.begin(); } auto end() const -> decltype(std::declval().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 mURI; mutable nsCOMPtr mURINoRef; mutable RefPtr mScheme; mutable nsCString mHost; mutable RefPtr mHostAtom; mutable nsCString mPath; mutable nsCString mFilePath; mutable nsString mSpec; mutable nsCString mCSpec; mutable Maybe 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 mCookie; mutable Maybe mIsSecure; mutable Maybe 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 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 mPath; }; class MatchPattern final : public nsISupports, public nsWrapperCache { NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(MatchPattern) static already_AddRefed 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 aGivenProto) override; protected: virtual ~MatchPattern() = default; private: friend class MatchPatternSet; explicit MatchPattern(nsISupports* aParent, already_AddRefed aCore) : mParent(aParent), mCore(std::move(aCore)) {} void Init(JSContext* aCx, const nsAString& aPattern, bool aIgnorePath, bool aRestrictSchemes, ErrorResult& aRv); nsCOMPtr mParent; RefPtr mCore; public: // A quick way to check if a particular URL matches without // actually instantiating a MatchPattern static bool MatchesAllURLs(const URLInfo& aURL); }; class MatchPatternSetCore final { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MatchPatternSetCore) using ArrayType = nsTArray>; explicit MatchPatternSetCore(ArrayType&& aPatterns) : mPatterns(std::move(aPatterns)) {} static already_AddRefed Constructor( dom::GlobalObject& aGlobal, const nsTArray& 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>; static already_AddRefed Constructor( dom::GlobalObject& aGlobal, const nsTArray& 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 aGivenProto) override; protected: virtual ~MatchPatternSet() = default; private: explicit MatchPatternSet(nsISupports* aParent, already_AddRefed aCore) : mParent(aParent), mCore(std::move(aCore)) {} nsCOMPtr mParent; RefPtr mCore; mozilla::Maybe mPatternsCache; }; } // namespace extensions } // namespace mozilla #endif // mozilla_extensions_MatchPattern_h