forked from mirrors/gecko-dev
		
	 f965305179
			
		
	
	
		f965305179
		
	
	
	
	
		
			
			Paths are all about: URIs have, so ignoring them doesn't make sense. MozReview-Commit-ID: ATwAeI7m61z --HG-- extra : rebase_source : ee50847aefe6b738c8a6d8c4cd60ab9e3a12c656
		
			
				
	
	
		
			817 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			817 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*-  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/. */
 | |
| 
 | |
| #include "mozilla/extensions/MatchPattern.h"
 | |
| #include "mozilla/extensions/MatchGlob.h"
 | |
| 
 | |
| #include "mozilla/dom/ScriptSettings.h"
 | |
| #include "mozilla/HoldDropJSObjects.h"
 | |
| #include "mozilla/Unused.h"
 | |
| 
 | |
| #include "nsGkAtoms.h"
 | |
| #include "nsIProtocolHandler.h"
 | |
| #include "nsIURL.h"
 | |
| #include "nsNetUtil.h"
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace extensions {
 | |
| 
 | |
| using namespace mozilla::dom;
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  * AtomSet
 | |
|  *****************************************************************************/
 | |
| 
 | |
| AtomSet::AtomSet(const nsTArray<nsString>& aElems)
 | |
| {
 | |
|   mElems.SetCapacity(aElems.Length());
 | |
| 
 | |
|   for (const auto& elem : aElems) {
 | |
|     mElems.AppendElement(NS_AtomizeMainThread(elem));
 | |
|   }
 | |
| 
 | |
|   SortAndUniquify();
 | |
| }
 | |
| 
 | |
| AtomSet::AtomSet(const char** aElems)
 | |
| {
 | |
|   for (const char** elemp = aElems; *elemp; elemp++) {
 | |
|     mElems.AppendElement(NS_Atomize(*elemp));
 | |
|   }
 | |
| 
 | |
|   SortAndUniquify();
 | |
| }
 | |
| 
 | |
| AtomSet::AtomSet(std::initializer_list<nsAtom*> aIL)
 | |
| {
 | |
|   mElems.SetCapacity(aIL.size());
 | |
| 
 | |
|   for (const auto& elem : aIL) {
 | |
|     mElems.AppendElement(elem);
 | |
|   }
 | |
| 
 | |
|   SortAndUniquify();
 | |
| }
 | |
| 
 | |
| void
 | |
| AtomSet::SortAndUniquify()
 | |
| {
 | |
|   mElems.Sort();
 | |
| 
 | |
|   nsAtom* prev = nullptr;
 | |
|   mElems.RemoveElementsBy([&prev] (const RefPtr<nsAtom>& aAtom) {
 | |
|     bool remove = aAtom == prev;
 | |
|     prev = aAtom;
 | |
|     return remove;
 | |
|   });
 | |
| 
 | |
|   mElems.Compact();
 | |
| }
 | |
| 
 | |
| bool
 | |
| AtomSet::Intersects(const AtomSet& aOther) const
 | |
| {
 | |
|   for (const auto& atom : *this) {
 | |
|     if (aOther.Contains(atom)) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   for (const auto& atom : aOther) {
 | |
|     if (Contains(atom)) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void
 | |
| AtomSet::Add(nsAtom* aAtom)
 | |
| {
 | |
|   auto index = mElems.IndexOfFirstElementGt(aAtom);
 | |
|   if (index == 0 || mElems[index - 1] != aAtom) {
 | |
|     mElems.InsertElementAt(index, aAtom);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| AtomSet::Remove(nsAtom* aAtom)
 | |
| {
 | |
|   auto index = mElems.BinaryIndexOf(aAtom);
 | |
|   if (index != ArrayType::NoIndex) {
 | |
|     mElems.RemoveElementAt(index);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  * URLInfo
 | |
|  *****************************************************************************/
 | |
| 
 | |
| nsAtom*
 | |
| URLInfo::Scheme() const
 | |
| {
 | |
|   if (!mScheme) {
 | |
|     nsCString scheme;
 | |
|     if (NS_SUCCEEDED(mURI->GetScheme(scheme))) {
 | |
|       mScheme = NS_AtomizeMainThread(NS_ConvertASCIItoUTF16(scheme));
 | |
|     }
 | |
|   }
 | |
|   return mScheme;
 | |
| }
 | |
| 
 | |
| const nsCString&
 | |
| URLInfo::Host() const
 | |
| {
 | |
|   if (mHost.IsVoid()) {
 | |
|     Unused << mURI->GetHost(mHost);
 | |
|   }
 | |
|   return mHost;
 | |
| }
 | |
| 
 | |
| const nsAtom*
 | |
| URLInfo::HostAtom() const
 | |
| {
 | |
|   if (!mHostAtom) {
 | |
|     mHostAtom = NS_Atomize(Host());
 | |
|   }
 | |
|   return mHostAtom;
 | |
| }
 | |
| 
 | |
| const nsString&
 | |
| URLInfo::FilePath() const
 | |
| {
 | |
|   if (mFilePath.IsEmpty()) {
 | |
|     nsCString path;
 | |
|     nsCOMPtr<nsIURL> url = do_QueryInterface(mURI);
 | |
|     if (url && NS_SUCCEEDED(url->GetFilePath(path))) {
 | |
|       AppendUTF8toUTF16(path, mFilePath);
 | |
|     } else {
 | |
|       mFilePath = Path();
 | |
|     }
 | |
|   }
 | |
|   return mFilePath;
 | |
| }
 | |
| 
 | |
| const nsString&
 | |
| URLInfo::Path() const
 | |
| {
 | |
|   if (mPath.IsEmpty()) {
 | |
|     nsCString path;
 | |
|     if (NS_SUCCEEDED(URINoRef()->GetPathQueryRef(path))) {
 | |
|       AppendUTF8toUTF16(path, mPath);
 | |
|     }
 | |
|   }
 | |
|   return mPath;
 | |
| }
 | |
| 
 | |
| const nsCString&
 | |
| URLInfo::CSpec() const
 | |
| {
 | |
|   if (mCSpec.IsEmpty()) {
 | |
|     Unused << URINoRef()->GetSpec(mCSpec);
 | |
|   }
 | |
|   return mCSpec;
 | |
| }
 | |
| 
 | |
| const nsString&
 | |
| URLInfo::Spec() const
 | |
| {
 | |
|   if (mSpec.IsEmpty()) {
 | |
|     AppendUTF8toUTF16(CSpec(), mSpec);
 | |
|   }
 | |
|   return mSpec;
 | |
| }
 | |
| 
 | |
| nsIURI*
 | |
| URLInfo::URINoRef() const
 | |
| {
 | |
|   if (!mURINoRef) {
 | |
|     if (NS_FAILED(mURI->CloneIgnoringRef(getter_AddRefs(mURINoRef)))) {
 | |
|       mURINoRef = mURI;
 | |
|     }
 | |
|   }
 | |
|   return mURINoRef;
 | |
| }
 | |
| 
 | |
| bool
 | |
| URLInfo::InheritsPrincipal() const
 | |
| {
 | |
|   if (!mInheritsPrincipal.isSome()) {
 | |
|     // For our purposes, about:blank and about:srcdoc are treated as URIs that
 | |
|     // inherit principals.
 | |
|     bool inherits = Spec().EqualsLiteral("about:blank") || Spec().EqualsLiteral("about:srcdoc");
 | |
| 
 | |
|     if (!inherits) {
 | |
|       nsresult rv = NS_URIChainHasFlags(mURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
 | |
|                                         &inherits);
 | |
|       Unused << NS_WARN_IF(NS_FAILED(rv));
 | |
|     }
 | |
| 
 | |
|     mInheritsPrincipal.emplace(inherits);
 | |
|   }
 | |
|   return mInheritsPrincipal.ref();
 | |
| }
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  * CookieInfo
 | |
|  *****************************************************************************/
 | |
| 
 | |
| bool
 | |
| CookieInfo::IsDomain() const
 | |
| {
 | |
|   if (mIsDomain.isNothing()) {
 | |
|     mIsDomain.emplace(false);
 | |
|     MOZ_ALWAYS_SUCCEEDS(mCookie->GetIsDomain(mIsDomain.ptr()));
 | |
|   }
 | |
|   return mIsDomain.ref();
 | |
| }
 | |
| 
 | |
| bool
 | |
| CookieInfo::IsSecure() const
 | |
| {
 | |
|   if (mIsSecure.isNothing()) {
 | |
|     mIsSecure.emplace(false);
 | |
|     MOZ_ALWAYS_SUCCEEDS(mCookie->GetIsSecure(mIsSecure.ptr()));
 | |
|   }
 | |
|   return mIsSecure.ref();
 | |
| }
 | |
| 
 | |
| const nsCString&
 | |
| CookieInfo::Host() const
 | |
| {
 | |
|   if (mHost.IsEmpty()) {
 | |
|     MOZ_ALWAYS_SUCCEEDS(mCookie->GetHost(mHost));
 | |
|   }
 | |
|   return mHost;
 | |
| }
 | |
| 
 | |
| const nsCString&
 | |
| CookieInfo::RawHost() const
 | |
| {
 | |
|   if (mRawHost.IsEmpty()) {
 | |
|     MOZ_ALWAYS_SUCCEEDS(mCookie->GetRawHost(mRawHost));
 | |
|   }
 | |
|   return mRawHost;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  * MatchPattern
 | |
|  *****************************************************************************/
 | |
| 
 | |
| const char* PERMITTED_SCHEMES[] = {"http", "https", "ws", "wss", "file", "ftp", "data", nullptr};
 | |
| 
 | |
| const char* WILDCARD_SCHEMES[] = {"http", "https", "ws", "wss", nullptr};
 | |
| 
 | |
| /* static */ already_AddRefed<MatchPattern>
 | |
| MatchPattern::Constructor(dom::GlobalObject& aGlobal,
 | |
|                           const nsAString& aPattern,
 | |
|                           const MatchPatternOptions& aOptions,
 | |
|                           ErrorResult& aRv)
 | |
| {
 | |
|   RefPtr<MatchPattern> pattern = new MatchPattern(aGlobal.GetAsSupports());
 | |
|   pattern->Init(aGlobal.Context(), aPattern, aOptions.mIgnorePath,
 | |
|                 aOptions.mRestrictSchemes, aRv);
 | |
|   if (aRv.Failed()) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   return pattern.forget();
 | |
| }
 | |
| 
 | |
| void
 | |
| MatchPattern::Init(JSContext* aCx, const nsAString& aPattern, bool aIgnorePath,
 | |
|                    bool aRestrictSchemes, ErrorResult& aRv)
 | |
| {
 | |
|   RefPtr<AtomSet> permittedSchemes = AtomSet::Get<PERMITTED_SCHEMES>();
 | |
| 
 | |
|   mPattern = aPattern;
 | |
| 
 | |
|   if (aPattern.EqualsLiteral("<all_urls>")) {
 | |
|     mSchemes = permittedSchemes;
 | |
|     mMatchSubdomain = true;
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // The portion of the URL we're currently examining.
 | |
|   uint32_t offset = 0;
 | |
|   auto tail = Substring(aPattern, offset);
 | |
| 
 | |
|   /***************************************************************************
 | |
|    * Scheme
 | |
|    ***************************************************************************/
 | |
|   int32_t index = aPattern.FindChar(':');
 | |
|   if (index <= 0) {
 | |
|     aRv.Throw(NS_ERROR_INVALID_ARG);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   RefPtr<nsAtom> scheme = NS_AtomizeMainThread(StringHead(aPattern, index));
 | |
|   if (scheme == nsGkAtoms::_asterisk) {
 | |
|     mSchemes = AtomSet::Get<WILDCARD_SCHEMES>();
 | |
|   } else if (!aRestrictSchemes ||
 | |
|              permittedSchemes->Contains(scheme) ||
 | |
|              scheme == nsGkAtoms::moz_extension) {
 | |
|     mSchemes = new AtomSet({scheme});
 | |
|   } else {
 | |
|     aRv.Throw(NS_ERROR_INVALID_ARG);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   /***************************************************************************
 | |
|    * Host
 | |
|    ***************************************************************************/
 | |
|   offset = index + 1;
 | |
|   tail.Rebind(aPattern, offset);
 | |
| 
 | |
|   if (scheme == nsGkAtoms::about) {
 | |
|     // about: URIs don't have hosts, so just treat the host as a wildcard and
 | |
|     // match on the path.
 | |
|     mMatchSubdomain = true;
 | |
|     // And so, ignorePath doesn't make sense for about: matchers.
 | |
|     aIgnorePath = false;
 | |
|   } else {
 | |
|     if (!StringHead(tail, 2).EqualsLiteral("//")) {
 | |
|       aRv.Throw(NS_ERROR_INVALID_ARG);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     offset += 2;
 | |
|     tail.Rebind(aPattern, offset);
 | |
|     index = tail.FindChar('/');
 | |
|     if (index < 0) {
 | |
|       index = tail.Length();
 | |
|     }
 | |
| 
 | |
|     auto host = StringHead(tail, index);
 | |
|     if (host.IsEmpty() && scheme != nsGkAtoms::file) {
 | |
|       aRv.Throw(NS_ERROR_INVALID_ARG);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     offset += index;
 | |
|     tail.Rebind(aPattern, offset);
 | |
| 
 | |
|     if (host.EqualsLiteral("*")) {
 | |
|       mMatchSubdomain = true;
 | |
|     } else if (StringHead(host, 2).EqualsLiteral("*.")) {
 | |
|       mDomain = NS_ConvertUTF16toUTF8(Substring(host, 2));
 | |
|       mMatchSubdomain = true;
 | |
|     } else {
 | |
|       mDomain = NS_ConvertUTF16toUTF8(host);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /***************************************************************************
 | |
|    * Path
 | |
|    ***************************************************************************/
 | |
|   if (aIgnorePath) {
 | |
|     mPattern.Truncate(offset);
 | |
|     mPattern.AppendLiteral("/*");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   auto path = tail;
 | |
|   if (path.IsEmpty()) {
 | |
|     aRv.Throw(NS_ERROR_INVALID_ARG);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   mPath = new MatchGlob(this);
 | |
|   mPath->Init(aCx, path, false, aRv);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool
 | |
| MatchPattern::MatchesDomain(const nsACString& aDomain) const
 | |
| {
 | |
|   if (DomainIsWildcard() || mDomain == aDomain) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (mMatchSubdomain) {
 | |
|     int64_t offset = (int64_t)aDomain.Length() - mDomain.Length();
 | |
|     if (offset > 0 && aDomain[offset - 1] == '.' &&
 | |
|         Substring(aDomain, offset) == mDomain) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| MatchPattern::Matches(const nsAString& aURL, bool aExplicit, ErrorResult& aRv) const
 | |
| {
 | |
|   nsCOMPtr<nsIURI> uri;
 | |
|   nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr, nullptr);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aRv.Throw(rv);
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return Matches(uri.get(), aExplicit);
 | |
| }
 | |
| 
 | |
| bool
 | |
| MatchPattern::Matches(const URLInfo& aURL, bool aExplicit) const
 | |
| {
 | |
|   if (aExplicit && mMatchSubdomain) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (!mSchemes->Contains(aURL.Scheme())) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (!DomainIsWildcard() && !MatchesDomain(aURL.Host())) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (mPath && !mPath->IsWildcard() && !mPath->Matches(aURL.Path())) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool
 | |
| MatchPattern::MatchesCookie(const CookieInfo& aCookie) const
 | |
| {
 | |
|   if (!mSchemes->Contains(nsGkAtoms::https) &&
 | |
|       (aCookie.IsSecure() || !mSchemes->Contains(nsGkAtoms::http))) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (MatchesDomain(aCookie.RawHost())) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (!aCookie.IsDomain()) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // Things get tricker for domain cookies. The extension needs to be able
 | |
|   // to read any cookies that could be read by any host it has permissions
 | |
|   // for. This means that our normal host matching checks won't work,
 | |
|   // since the pattern "*://*.foo.example.com/" doesn't match ".example.com",
 | |
|   // but it does match "bar.foo.example.com", which can read cookies
 | |
|   // with the domain ".example.com".
 | |
|   //
 | |
|   // So, instead, we need to manually check our filters, and accept any
 | |
|   // with hosts that end with our cookie's host.
 | |
| 
 | |
|   auto& host = aCookie.Host();
 | |
|   return StringTail(mDomain, host.Length()) == host;
 | |
| }
 | |
| 
 | |
| bool
 | |
| MatchPattern::SubsumesDomain(const MatchPattern& aPattern) const
 | |
| {
 | |
|   if (!mMatchSubdomain && aPattern.mMatchSubdomain && aPattern.mDomain == mDomain) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return MatchesDomain(aPattern.mDomain);
 | |
| }
 | |
| 
 | |
| bool
 | |
| MatchPattern::Subsumes(const MatchPattern& aPattern) const
 | |
| {
 | |
|   for (auto& scheme : *aPattern.mSchemes) {
 | |
|     if (!mSchemes->Contains(scheme)) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return SubsumesDomain(aPattern);
 | |
| }
 | |
| 
 | |
| bool
 | |
| MatchPattern::Overlaps(const MatchPattern& aPattern) const
 | |
| {
 | |
|   if (!mSchemes->Intersects(*aPattern.mSchemes)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return SubsumesDomain(aPattern) || aPattern.SubsumesDomain(*this);
 | |
| }
 | |
| 
 | |
| 
 | |
| JSObject*
 | |
| MatchPattern::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
 | |
| {
 | |
|   return MatchPatternBinding::Wrap(aCx, this, aGivenProto);
 | |
| }
 | |
| 
 | |
| /* static */ bool
 | |
| MatchPattern::MatchesAllURLs(const URLInfo& aURL)
 | |
| {
 | |
|   RefPtr<AtomSet> permittedSchemes = AtomSet::Get<PERMITTED_SCHEMES>();
 | |
|   return permittedSchemes->Contains(aURL.Scheme());
 | |
| }
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MatchPattern, mPath, mParent)
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MatchPattern)
 | |
|   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
 | |
|   NS_INTERFACE_MAP_ENTRY(nsISupports)
 | |
| NS_INTERFACE_MAP_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTING_ADDREF(MatchPattern)
 | |
| NS_IMPL_CYCLE_COLLECTING_RELEASE(MatchPattern)
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  * MatchPatternSet
 | |
|  *****************************************************************************/
 | |
| 
 | |
| /* static */ already_AddRefed<MatchPatternSet>
 | |
| MatchPatternSet::Constructor(dom::GlobalObject& aGlobal,
 | |
|                              const nsTArray<dom::OwningStringOrMatchPattern>& aPatterns,
 | |
|                              const MatchPatternOptions& aOptions,
 | |
|                              ErrorResult& aRv)
 | |
| {
 | |
|   ArrayType patterns;
 | |
| 
 | |
|   for (auto& elem : aPatterns) {
 | |
|     if (elem.IsMatchPattern()) {
 | |
|       patterns.AppendElement(elem.GetAsMatchPattern());
 | |
|     } else {
 | |
|       RefPtr<MatchPattern> pattern = MatchPattern::Constructor(
 | |
|         aGlobal, elem.GetAsString(), aOptions, aRv);
 | |
| 
 | |
|       if (!pattern) {
 | |
|         return nullptr;
 | |
|       }
 | |
|       patterns.AppendElement(std::move(pattern));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   RefPtr<MatchPatternSet> patternSet = new MatchPatternSet(aGlobal.GetAsSupports(),
 | |
|                                                            std::move(patterns));
 | |
|   return patternSet.forget();
 | |
| }
 | |
| 
 | |
| 
 | |
| bool
 | |
| MatchPatternSet::Matches(const nsAString& aURL, bool aExplicit, ErrorResult& aRv) const
 | |
| {
 | |
|   nsCOMPtr<nsIURI> uri;
 | |
|   nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr, nullptr);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aRv.Throw(rv);
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return Matches(uri.get(), aExplicit);
 | |
| }
 | |
| 
 | |
| bool
 | |
| MatchPatternSet::Matches(const URLInfo& aURL, bool aExplicit) const
 | |
| {
 | |
|   for (const auto& pattern : mPatterns) {
 | |
|     if (pattern->Matches(aURL, aExplicit)) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| MatchPatternSet::MatchesCookie(const CookieInfo& aCookie) const
 | |
| {
 | |
|   for (const auto& pattern : mPatterns) {
 | |
|     if (pattern->MatchesCookie(aCookie)) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| MatchPatternSet::Subsumes(const MatchPattern& aPattern) const
 | |
| {
 | |
|   for (const auto& pattern : mPatterns) {
 | |
|     if (pattern->Subsumes(aPattern)) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| MatchPatternSet::Overlaps(const MatchPatternSet& aPatternSet) const
 | |
| {
 | |
|   for (const auto& pattern : aPatternSet.mPatterns) {
 | |
|     if (Overlaps(*pattern)) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| MatchPatternSet::Overlaps(const MatchPattern& aPattern) const
 | |
| {
 | |
|   for (const auto& pattern : mPatterns) {
 | |
|     if (pattern->Overlaps(aPattern)) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool
 | |
| MatchPatternSet::OverlapsAll(const MatchPatternSet& aPatternSet) const
 | |
| {
 | |
|   for (const auto& pattern : aPatternSet.mPatterns) {
 | |
|     if (!Overlaps(*pattern)) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
|   return aPatternSet.mPatterns.Length() > 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| JSObject*
 | |
| MatchPatternSet::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
 | |
| {
 | |
|   return MatchPatternSetBinding::Wrap(aCx, this, aGivenProto);
 | |
| }
 | |
| 
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MatchPatternSet, mPatterns, mParent)
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MatchPatternSet)
 | |
|   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
 | |
|   NS_INTERFACE_MAP_ENTRY(nsISupports)
 | |
| NS_INTERFACE_MAP_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTING_ADDREF(MatchPatternSet)
 | |
| NS_IMPL_CYCLE_COLLECTING_RELEASE(MatchPatternSet)
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  * MatchGlob
 | |
|  *****************************************************************************/
 | |
| 
 | |
| MatchGlob::~MatchGlob()
 | |
| {
 | |
|   mozilla::DropJSObjects(this);
 | |
| }
 | |
| 
 | |
| /* static */ already_AddRefed<MatchGlob>
 | |
| MatchGlob::Constructor(dom::GlobalObject& aGlobal,
 | |
|                        const nsAString& aGlob,
 | |
|                        bool aAllowQuestion,
 | |
|                        ErrorResult& aRv)
 | |
| {
 | |
|   RefPtr<MatchGlob> glob = new MatchGlob(aGlobal.GetAsSupports());
 | |
|   glob->Init(aGlobal.Context(), aGlob, aAllowQuestion, aRv);
 | |
|   if (aRv.Failed()) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   return glob.forget();
 | |
| }
 | |
| 
 | |
| void
 | |
| MatchGlob::Init(JSContext* aCx, const nsAString& aGlob, bool aAllowQuestion, ErrorResult& aRv)
 | |
| {
 | |
|   mGlob = aGlob;
 | |
| 
 | |
|   // Check for a literal match with no glob metacharacters.
 | |
|   auto index = mGlob.FindCharInSet(aAllowQuestion ? "*?" : "*");
 | |
|   if (index < 0) {
 | |
|     mPathLiteral = mGlob;
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Check for a prefix match, where the only glob metacharacter is a "*"
 | |
|   // at the end of the string.
 | |
|   if (index == (int32_t)mGlob.Length() - 1 && mGlob[index] == '*') {
 | |
|     mPathLiteral = StringHead(mGlob, index);
 | |
|     mIsPrefix = true;
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Fall back to the regexp slow path.
 | |
|   NS_NAMED_LITERAL_CSTRING(metaChars, ".+*?^${}()|[]\\");
 | |
| 
 | |
|   nsAutoString escaped;
 | |
|   escaped.Append('^');
 | |
| 
 | |
|   for (uint32_t i = 0; i < mGlob.Length(); i++) {
 | |
|     auto c = mGlob[i];
 | |
|     if (c == '*') {
 | |
|       escaped.AppendLiteral(".*");
 | |
|     } else if (c == '?' && aAllowQuestion) {
 | |
|       escaped.Append('.');
 | |
|     } else {
 | |
|       if (metaChars.Contains(c)) {
 | |
|         escaped.Append('\\');
 | |
|       }
 | |
|       escaped.Append(c);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   escaped.Append('$');
 | |
| 
 | |
|   // TODO: Switch to the Rust regexp crate, when Rust integration is easier.
 | |
|   // It uses a much more efficient, linear time matching algorithm, and
 | |
|   // doesn't require special casing for the literal and prefix cases.
 | |
|   mRegExp = JS_NewUCRegExpObject(aCx, escaped.get(), escaped.Length(), 0);
 | |
|   if (mRegExp) {
 | |
|     mozilla::HoldJSObjects(this);
 | |
|   } else {
 | |
|     aRv.NoteJSContextException(aCx);
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool
 | |
| MatchGlob::Matches(const nsAString& aString) const
 | |
| {
 | |
|   if (mRegExp) {
 | |
|     AutoJSAPI jsapi;
 | |
|     jsapi.Init();
 | |
|     JSContext* cx = jsapi.cx();
 | |
| 
 | |
|     JSAutoRealm ar(cx, mRegExp);
 | |
| 
 | |
|     JS::RootedObject regexp(cx, mRegExp);
 | |
|     JS::RootedValue result(cx);
 | |
| 
 | |
|     nsString input(aString);
 | |
| 
 | |
|     size_t index = 0;
 | |
|     if (!JS_ExecuteRegExpNoStatics(cx, regexp, input.BeginWriting(), aString.Length(),
 | |
|                                    &index, true, &result)) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     return result.isBoolean() && result.toBoolean();
 | |
|   }
 | |
| 
 | |
|   if (mIsPrefix) {
 | |
|     return mPathLiteral == StringHead(aString, mPathLiteral.Length());
 | |
|   }
 | |
| 
 | |
|   return mPathLiteral == aString;
 | |
| }
 | |
| 
 | |
| 
 | |
| JSObject*
 | |
| MatchGlob::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
 | |
| {
 | |
|   return MatchGlobBinding::Wrap(aCx, this, aGivenProto);
 | |
| }
 | |
| 
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_CLASS(MatchGlob)
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MatchGlob)
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
 | |
|   tmp->mRegExp = nullptr;
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MatchGlob)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(MatchGlob)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRegExp)
 | |
| NS_IMPL_CYCLE_COLLECTION_TRACE_END
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MatchGlob)
 | |
|   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
 | |
|   NS_INTERFACE_MAP_ENTRY(nsISupports)
 | |
| NS_INTERFACE_MAP_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTING_ADDREF(MatchGlob)
 | |
| NS_IMPL_CYCLE_COLLECTING_RELEASE(MatchGlob)
 | |
| 
 | |
| 
 | |
| /*****************************************************************************
 | |
|  * MatchGlobSet
 | |
|  *****************************************************************************/
 | |
| 
 | |
| bool
 | |
| MatchGlobSet::Matches(const nsAString& aValue) const
 | |
| {
 | |
|   for (auto& glob : *this) {
 | |
|     if (glob->Matches(aValue)) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| } // namespace extensions
 | |
| } // namespace mozilla
 | |
| 
 |