LCOV - code coverage report
Current view: top level - toolkit/components/extensions - MatchPattern.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 345 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 64 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
       4             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "mozilla/extensions/MatchPattern.h"
       7             : #include "mozilla/extensions/MatchGlob.h"
       8             : 
       9             : #include "mozilla/dom/ScriptSettings.h"
      10             : #include "mozilla/HoldDropJSObjects.h"
      11             : #include "mozilla/Unused.h"
      12             : 
      13             : #include "nsGkAtoms.h"
      14             : #include "nsIProtocolHandler.h"
      15             : #include "nsIURL.h"
      16             : #include "nsNetUtil.h"
      17             : 
      18             : namespace mozilla {
      19             : namespace extensions {
      20             : 
      21             : using namespace mozilla::dom;
      22             : 
      23             : 
      24             : /*****************************************************************************
      25             :  * AtomSet
      26             :  *****************************************************************************/
      27             : 
      28           0 : AtomSet::AtomSet(const nsTArray<nsString>& aElems)
      29             : {
      30           0 :   mElems.SetCapacity(aElems.Length());
      31             : 
      32           0 :   for (const auto& elem : aElems) {
      33           0 :     mElems.AppendElement(NS_AtomizeMainThread(elem));
      34             :   }
      35             : 
      36           0 :   SortAndUniquify();
      37           0 : }
      38             : 
      39           0 : AtomSet::AtomSet(const char** aElems)
      40             : {
      41           0 :   for (const char** elemp = aElems; *elemp; elemp++) {
      42           0 :     mElems.AppendElement(NS_Atomize(*elemp));
      43             :   }
      44             : 
      45           0 :   SortAndUniquify();
      46           0 : }
      47             : 
      48           0 : AtomSet::AtomSet(std::initializer_list<nsIAtom*> aIL)
      49             : {
      50           0 :   mElems.SetCapacity(aIL.size());
      51             : 
      52           0 :   for (const auto& elem : aIL) {
      53           0 :     mElems.AppendElement(elem);
      54             :   }
      55             : 
      56           0 :   SortAndUniquify();
      57           0 : }
      58             : 
      59             : void
      60           0 : AtomSet::SortAndUniquify()
      61             : {
      62           0 :   mElems.Sort();
      63             : 
      64           0 :   nsIAtom* prev = nullptr;
      65           0 :   mElems.RemoveElementsBy([&prev] (const RefPtr<nsIAtom>& aAtom) {
      66           0 :     bool remove = aAtom == prev;
      67           0 :     prev = aAtom;
      68           0 :     return remove;
      69           0 :   });
      70             : 
      71           0 :   mElems.Compact();
      72           0 : }
      73             : 
      74             : bool
      75           0 : AtomSet::Intersects(const AtomSet& aOther) const
      76             : {
      77           0 :   for (const auto& atom : *this) {
      78           0 :     if (aOther.Contains(atom)) {
      79           0 :       return true;
      80             :     }
      81             :   }
      82           0 :   for (const auto& atom : aOther) {
      83           0 :     if (Contains(atom)) {
      84           0 :       return true;
      85             :     }
      86             :   }
      87           0 :   return false;
      88             : }
      89             : 
      90             : void
      91           0 : AtomSet::Add(nsIAtom* aAtom)
      92             : {
      93           0 :   auto index = mElems.IndexOfFirstElementGt(aAtom);
      94           0 :   if (index == 0 || mElems[index - 1] != aAtom) {
      95           0 :     mElems.InsertElementAt(index, aAtom);
      96             :   }
      97           0 : }
      98             : 
      99             : void
     100           0 : AtomSet::Remove(nsIAtom* aAtom)
     101             : {
     102           0 :   auto index = mElems.BinaryIndexOf(aAtom);
     103           0 :   if (index != mElems.NoIndex) {
     104           0 :     mElems.RemoveElementAt(index);
     105             :   }
     106           0 : }
     107             : 
     108             : 
     109             : /*****************************************************************************
     110             :  * URLInfo
     111             :  *****************************************************************************/
     112             : 
     113             : nsIAtom*
     114           0 : URLInfo::Scheme() const
     115             : {
     116           0 :   if (!mScheme) {
     117           0 :     nsCString scheme;
     118           0 :     if (NS_SUCCEEDED(mURI->GetScheme(scheme))) {
     119           0 :       mScheme = NS_AtomizeMainThread(NS_ConvertASCIItoUTF16(scheme));
     120             :     }
     121             :   }
     122           0 :   return mScheme;
     123             : }
     124             : 
     125             : const nsCString&
     126           0 : URLInfo::Host() const
     127             : {
     128           0 :   if (mHost.IsVoid()) {
     129           0 :     Unused << mURI->GetHost(mHost);
     130             :   }
     131           0 :   return mHost;
     132             : }
     133             : 
     134             : const nsString&
     135           0 : URLInfo::FilePath() const
     136             : {
     137           0 :   if (mFilePath.IsEmpty()) {
     138           0 :     nsCString path;
     139           0 :     nsCOMPtr<nsIURL> url = do_QueryInterface(mURI);
     140           0 :     if (url && NS_SUCCEEDED(url->GetFilePath(path))) {
     141           0 :       AppendUTF8toUTF16(path, mFilePath);
     142             :     } else {
     143           0 :       mFilePath = Path();
     144             :     }
     145             :   }
     146           0 :   return mFilePath;
     147             : }
     148             : 
     149             : const nsString&
     150           0 : URLInfo::Path() const
     151             : {
     152           0 :   if (mPath.IsEmpty()) {
     153           0 :     nsCString path;
     154           0 :     if (NS_SUCCEEDED(URINoRef()->GetPath(path))) {
     155           0 :       AppendUTF8toUTF16(path, mPath);
     156             :     }
     157             :   }
     158           0 :   return mPath;
     159             : }
     160             : 
     161             : const nsString&
     162           0 : URLInfo::Spec() const
     163             : {
     164           0 :   if (mSpec.IsEmpty()) {
     165           0 :     nsCString spec;
     166           0 :     if (NS_SUCCEEDED(URINoRef()->GetSpec(spec))) {
     167           0 :       AppendUTF8toUTF16(spec, mSpec);
     168             :     }
     169             :   }
     170           0 :   return mSpec;
     171             : }
     172             : 
     173             : nsIURI*
     174           0 : URLInfo::URINoRef() const
     175             : {
     176           0 :   if (!mURINoRef) {
     177           0 :     if (NS_FAILED(mURI->CloneIgnoringRef(getter_AddRefs(mURINoRef)))) {
     178           0 :       mURINoRef = mURI;
     179             :     }
     180             :   }
     181           0 :   return mURINoRef;
     182             : }
     183             : 
     184             : bool
     185           0 : URLInfo::InheritsPrincipal() const
     186             : {
     187           0 :   if (!mInheritsPrincipal.isSome()) {
     188             :     // For our purposes, about:blank and about:srcdoc are treated as URIs that
     189             :     // inherit principals.
     190           0 :     bool inherits = Spec().EqualsLiteral("about:blank") || Spec().EqualsLiteral("about:srcdoc");
     191             : 
     192           0 :     if (!inherits) {
     193           0 :       nsresult rv = NS_URIChainHasFlags(mURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
     194           0 :                                         &inherits);
     195           0 :       Unused << NS_WARN_IF(NS_FAILED(rv));
     196             :     }
     197             : 
     198           0 :     mInheritsPrincipal.emplace(inherits);
     199             :   }
     200           0 :   return mInheritsPrincipal.ref();
     201             : }
     202             : 
     203             : 
     204             : /*****************************************************************************
     205             :  * CookieInfo
     206             :  *****************************************************************************/
     207             : 
     208             : bool
     209           0 : CookieInfo::IsDomain() const
     210             : {
     211           0 :   if (mIsDomain.isNothing()) {
     212           0 :     mIsDomain.emplace(false);
     213           0 :     MOZ_ALWAYS_SUCCEEDS(mCookie->GetIsDomain(mIsDomain.ptr()));
     214             :   }
     215           0 :   return mIsDomain.ref();
     216             : }
     217             : 
     218             : bool
     219           0 : CookieInfo::IsSecure() const
     220             : {
     221           0 :   if (mIsSecure.isNothing()) {
     222           0 :     mIsSecure.emplace(false);
     223           0 :     MOZ_ALWAYS_SUCCEEDS(mCookie->GetIsSecure(mIsSecure.ptr()));
     224             :   }
     225           0 :   return mIsSecure.ref();
     226             : }
     227             : 
     228             : const nsCString&
     229           0 : CookieInfo::Host() const
     230             : {
     231           0 :   if (mHost.IsEmpty()) {
     232           0 :     MOZ_ALWAYS_SUCCEEDS(mCookie->GetHost(mHost));
     233             :   }
     234           0 :   return mHost;
     235             : }
     236             : 
     237             : const nsCString&
     238           0 : CookieInfo::RawHost() const
     239             : {
     240           0 :   if (mRawHost.IsEmpty()) {
     241           0 :     MOZ_ALWAYS_SUCCEEDS(mCookie->GetRawHost(mRawHost));
     242             :   }
     243           0 :   return mRawHost;
     244             : }
     245             : 
     246             : 
     247             : /*****************************************************************************
     248             :  * MatchPattern
     249             :  *****************************************************************************/
     250             : 
     251             : const char* PERMITTED_SCHEMES[] = {"http", "https", "ws", "wss", "file", "ftp", "data", nullptr};
     252             : 
     253             : const char* WILDCARD_SCHEMES[] = {"http", "https", "ws", "wss", nullptr};
     254             : 
     255             : /* static */ already_AddRefed<MatchPattern>
     256           0 : MatchPattern::Constructor(dom::GlobalObject& aGlobal,
     257             :                           const nsAString& aPattern,
     258             :                           const MatchPatternOptions& aOptions,
     259             :                           ErrorResult& aRv)
     260             : {
     261           0 :   RefPtr<MatchPattern> pattern = new MatchPattern(aGlobal.GetAsSupports());
     262           0 :   pattern->Init(aGlobal.Context(), aPattern, aOptions.mIgnorePath, aRv);
     263           0 :   if (aRv.Failed()) {
     264           0 :     return nullptr;
     265             :   }
     266           0 :   return pattern.forget();
     267             : }
     268             : 
     269             : void
     270           0 : MatchPattern::Init(JSContext* aCx, const nsAString& aPattern, bool aIgnorePath, ErrorResult& aRv)
     271             : {
     272           0 :   RefPtr<AtomSet> permittedSchemes = AtomSet::Get<PERMITTED_SCHEMES>();
     273             : 
     274           0 :   mPattern = aPattern;
     275             : 
     276           0 :   if (aPattern.EqualsLiteral("<all_urls>")) {
     277           0 :     mSchemes = permittedSchemes;
     278           0 :     mMatchSubdomain = true;
     279           0 :     return;
     280             :   }
     281             : 
     282             :   // The portion of the URL we're currently examining.
     283           0 :   uint32_t offset = 0;
     284           0 :   auto tail = Substring(aPattern, offset);
     285             : 
     286             :   /***************************************************************************
     287             :    * Scheme
     288             :    ***************************************************************************/
     289           0 :   int32_t index = aPattern.FindChar(':');
     290           0 :   if (index <= 0) {
     291           0 :     aRv.Throw(NS_ERROR_INVALID_ARG);
     292           0 :     return;
     293             :   }
     294             : 
     295           0 :   nsCOMPtr<nsIAtom> scheme = NS_AtomizeMainThread(StringHead(aPattern, index));
     296           0 :   if (scheme == nsGkAtoms::_asterisk) {
     297           0 :     mSchemes = AtomSet::Get<WILDCARD_SCHEMES>();
     298           0 :   } else if (permittedSchemes->Contains(scheme) || scheme == nsGkAtoms::moz_extension) {
     299           0 :     mSchemes = new AtomSet({scheme});
     300             :   } else {
     301           0 :     aRv.Throw(NS_ERROR_INVALID_ARG);
     302           0 :     return;
     303             :   }
     304             : 
     305             :   /***************************************************************************
     306             :    * Host
     307             :    ***************************************************************************/
     308           0 :   offset = index + 1;
     309           0 :   tail.Rebind(aPattern, offset);
     310             : 
     311           0 :   if (!StringHead(tail, 2).EqualsLiteral("//")) {
     312           0 :     aRv.Throw(NS_ERROR_INVALID_ARG);
     313           0 :     return;
     314             :   }
     315             : 
     316           0 :   offset += 2;
     317           0 :   tail.Rebind(aPattern, offset);
     318           0 :   index = tail.FindChar('/');
     319           0 :   if (index < 0) {
     320           0 :     index = tail.Length();
     321             :   }
     322             : 
     323           0 :   auto host = StringHead(tail, index);
     324           0 :   if (host.IsEmpty() && scheme != nsGkAtoms::file) {
     325           0 :     aRv.Throw(NS_ERROR_INVALID_ARG);
     326           0 :     return;
     327             :   }
     328             : 
     329           0 :   offset += index;
     330           0 :   tail.Rebind(aPattern, offset);
     331             : 
     332           0 :   if (host.EqualsLiteral("*")) {
     333           0 :     mMatchSubdomain = true;
     334           0 :   } else if (StringHead(host, 2).EqualsLiteral("*.")) {
     335           0 :     mDomain = NS_ConvertUTF16toUTF8(Substring(host, 2));
     336           0 :     mMatchSubdomain = true;
     337             :   } else {
     338           0 :     mDomain = NS_ConvertUTF16toUTF8(host);
     339             :   }
     340             : 
     341             :   /***************************************************************************
     342             :    * Path
     343             :    ***************************************************************************/
     344           0 :   if (aIgnorePath) {
     345           0 :     mPattern.Truncate(offset);
     346           0 :     mPattern.AppendLiteral("/*");
     347           0 :     return;
     348             :   }
     349             : 
     350           0 :   auto path = tail;
     351           0 :   if (path.IsEmpty()) {
     352           0 :     aRv.Throw(NS_ERROR_INVALID_ARG);
     353           0 :     return;
     354             :   }
     355             : 
     356           0 :   mPath = new MatchGlob(this);
     357           0 :   mPath->Init(aCx, path, false, aRv);
     358             : }
     359             : 
     360             : 
     361             : bool
     362           0 : MatchPattern::MatchesDomain(const nsACString& aDomain) const
     363             : {
     364           0 :   if (DomainIsWildcard() || mDomain == aDomain) {
     365           0 :     return true;
     366             :   }
     367             : 
     368           0 :   if (mMatchSubdomain) {
     369           0 :     int64_t offset = (int64_t)aDomain.Length() - mDomain.Length();
     370           0 :     if (offset > 0 && aDomain[offset - 1] == '.' &&
     371           0 :         Substring(aDomain, offset) == mDomain) {
     372           0 :       return true;
     373             :     }
     374             :   }
     375             : 
     376           0 :   return false;
     377             : }
     378             : 
     379             : bool
     380           0 : MatchPattern::Matches(const URLInfo& aURL, bool aExplicit) const
     381             : {
     382           0 :   if (aExplicit && mMatchSubdomain) {
     383           0 :     return false;
     384             :   }
     385             : 
     386           0 :   if (!mSchemes->Contains(aURL.Scheme())) {
     387           0 :     return false;
     388             :   }
     389             : 
     390           0 :   if (!DomainIsWildcard() && !MatchesDomain(aURL.Host())) {
     391           0 :     return false;
     392             :   }
     393             : 
     394           0 :   if (mPath && !mPath->IsWildcard() && !mPath->Matches(aURL.Path())) {
     395           0 :     return false;
     396             :   }
     397             : 
     398           0 :   return true;
     399             : }
     400             : 
     401             : bool
     402           0 : MatchPattern::MatchesCookie(const CookieInfo& aCookie) const
     403             : {
     404           0 :   if (!mSchemes->Contains(nsGkAtoms::https) &&
     405           0 :       (aCookie.IsSecure() || !mSchemes->Contains(nsGkAtoms::http))) {
     406           0 :     return false;
     407             :   }
     408             : 
     409           0 :   if (MatchesDomain(aCookie.RawHost())) {
     410           0 :     return true;
     411             :   }
     412             : 
     413           0 :   if (!aCookie.IsDomain()) {
     414           0 :     return false;
     415             :   }
     416             : 
     417             :   // Things get tricker for domain cookies. The extension needs to be able
     418             :   // to read any cookies that could be read by any host it has permissions
     419             :   // for. This means that our normal host matching checks won't work,
     420             :   // since the pattern "*://*.foo.example.com/" doesn't match ".example.com",
     421             :   // but it does match "bar.foo.example.com", which can read cookies
     422             :   // with the domain ".example.com".
     423             :   //
     424             :   // So, instead, we need to manually check our filters, and accept any
     425             :   // with hosts that end with our cookie's host.
     426             : 
     427           0 :   auto& host = aCookie.Host();
     428           0 :   return StringTail(mDomain, host.Length()) == host;
     429             : }
     430             : 
     431             : bool
     432           0 : MatchPattern::SubsumesDomain(const MatchPattern& aPattern) const
     433             : {
     434           0 :   if (!mMatchSubdomain && aPattern.mMatchSubdomain && aPattern.mDomain == mDomain) {
     435           0 :     return false;
     436             :   }
     437             : 
     438           0 :   return MatchesDomain(aPattern.mDomain);
     439             : }
     440             : 
     441             : bool
     442           0 : MatchPattern::Subsumes(const MatchPattern& aPattern) const
     443             : {
     444           0 :   for (auto& scheme : *aPattern.mSchemes) {
     445           0 :     if (!mSchemes->Contains(scheme)) {
     446           0 :       return false;
     447             :     }
     448             :   }
     449             : 
     450           0 :   return SubsumesDomain(aPattern);
     451             : }
     452             : 
     453             : bool
     454           0 : MatchPattern::Overlaps(const MatchPattern& aPattern) const
     455             : {
     456           0 :   if (!mSchemes->Intersects(*aPattern.mSchemes)) {
     457           0 :     return false;
     458             :   }
     459             : 
     460           0 :   return SubsumesDomain(aPattern) || aPattern.SubsumesDomain(*this);
     461             : }
     462             : 
     463             : 
     464             : JSObject*
     465           0 : MatchPattern::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
     466             : {
     467           0 :   return MatchPatternBinding::Wrap(aCx, this, aGivenProto);
     468             : }
     469             : 
     470             : 
     471           0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MatchPattern, mPath, mParent)
     472             : 
     473           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MatchPattern)
     474           0 :   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     475           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
     476           0 : NS_INTERFACE_MAP_END
     477             : 
     478           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(MatchPattern)
     479           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(MatchPattern)
     480             : 
     481             : 
     482             : /*****************************************************************************
     483             :  * MatchPatternSet
     484             :  *****************************************************************************/
     485             : 
     486             : /* static */ already_AddRefed<MatchPatternSet>
     487           0 : MatchPatternSet::Constructor(dom::GlobalObject& aGlobal,
     488             :                              const nsTArray<dom::OwningStringOrMatchPattern>& aPatterns,
     489             :                              const MatchPatternOptions& aOptions,
     490             :                              ErrorResult& aRv)
     491             : {
     492           0 :   ArrayType patterns;
     493             : 
     494           0 :   for (auto& elem : aPatterns) {
     495           0 :     if (elem.IsMatchPattern()) {
     496           0 :       patterns.AppendElement(elem.GetAsMatchPattern());
     497             :     } else {
     498           0 :       RefPtr<MatchPattern> pattern = MatchPattern::Constructor(
     499           0 :         aGlobal, elem.GetAsString(), aOptions, aRv);
     500             : 
     501           0 :       if (!pattern) {
     502           0 :         return nullptr;
     503             :       }
     504           0 :       patterns.AppendElement(Move(pattern));
     505             :     }
     506             :   }
     507             : 
     508           0 :   RefPtr<MatchPatternSet> patternSet = new MatchPatternSet(aGlobal.GetAsSupports(),
     509           0 :                                                            Move(patterns));
     510           0 :   return patternSet.forget();
     511             : }
     512             : 
     513             : 
     514             : bool
     515           0 : MatchPatternSet::Matches(const URLInfo& aURL, bool aExplicit) const
     516             : {
     517           0 :   for (const auto& pattern : mPatterns) {
     518           0 :     if (pattern->Matches(aURL, aExplicit)) {
     519           0 :       return true;
     520             :     }
     521             :   }
     522           0 :   return false;
     523             : }
     524             : 
     525             : bool
     526           0 : MatchPatternSet::MatchesCookie(const CookieInfo& aCookie) const
     527             : {
     528           0 :   for (const auto& pattern : mPatterns) {
     529           0 :     if (pattern->MatchesCookie(aCookie)) {
     530           0 :       return true;
     531             :     }
     532             :   }
     533           0 :   return false;
     534             : }
     535             : 
     536             : bool
     537           0 : MatchPatternSet::Subsumes(const MatchPattern& aPattern) const
     538             : {
     539           0 :   for (const auto& pattern : mPatterns) {
     540           0 :     if (pattern->Subsumes(aPattern)) {
     541           0 :       return true;
     542             :     }
     543             :   }
     544           0 :   return false;
     545             : }
     546             : 
     547             : bool
     548           0 : MatchPatternSet::Overlaps(const MatchPatternSet& aPatternSet) const
     549             : {
     550           0 :   for (const auto& pattern : aPatternSet.mPatterns) {
     551           0 :     if (Overlaps(*pattern)) {
     552           0 :       return true;
     553             :     }
     554             :   }
     555           0 :   return false;
     556             : }
     557             : 
     558             : bool
     559           0 : MatchPatternSet::Overlaps(const MatchPattern& aPattern) const
     560             : {
     561           0 :   for (const auto& pattern : mPatterns) {
     562           0 :     if (pattern->Overlaps(aPattern)) {
     563           0 :       return true;
     564             :     }
     565             :   }
     566           0 :   return false;
     567             : }
     568             : 
     569             : 
     570             : bool
     571           0 : MatchPatternSet::OverlapsAll(const MatchPatternSet& aPatternSet) const
     572             : {
     573           0 :   for (const auto& pattern : aPatternSet.mPatterns) {
     574           0 :     if (!Overlaps(*pattern)) {
     575           0 :       return false;
     576             :     }
     577             :   }
     578           0 :   return aPatternSet.mPatterns.Length() > 0;
     579             : }
     580             : 
     581             : 
     582             : JSObject*
     583           0 : MatchPatternSet::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
     584             : {
     585           0 :   return MatchPatternSetBinding::Wrap(aCx, this, aGivenProto);
     586             : }
     587             : 
     588             : 
     589           0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MatchPatternSet, mPatterns, mParent)
     590             : 
     591           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MatchPatternSet)
     592           0 :   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     593           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
     594           0 : NS_INTERFACE_MAP_END
     595             : 
     596           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(MatchPatternSet)
     597           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(MatchPatternSet)
     598             : 
     599             : 
     600             : /*****************************************************************************
     601             :  * MatchGlob
     602             :  *****************************************************************************/
     603             : 
     604           0 : MatchGlob::~MatchGlob()
     605             : {
     606           0 :   mozilla::DropJSObjects(this);
     607           0 : }
     608             : 
     609             : /* static */ already_AddRefed<MatchGlob>
     610           0 : MatchGlob::Constructor(dom::GlobalObject& aGlobal,
     611             :                        const nsAString& aGlob,
     612             :                        bool aAllowQuestion,
     613             :                        ErrorResult& aRv)
     614             : {
     615           0 :   RefPtr<MatchGlob> glob = new MatchGlob(aGlobal.GetAsSupports());
     616           0 :   glob->Init(aGlobal.Context(), aGlob, aAllowQuestion, aRv);
     617           0 :   if (aRv.Failed()) {
     618           0 :     return nullptr;
     619             :   }
     620           0 :   return glob.forget();
     621             : }
     622             : 
     623             : void
     624           0 : MatchGlob::Init(JSContext* aCx, const nsAString& aGlob, bool aAllowQuestion, ErrorResult& aRv)
     625             : {
     626           0 :   mGlob = aGlob;
     627             : 
     628             :   // Check for a literal match with no glob metacharacters.
     629           0 :   auto index = mGlob.FindCharInSet(aAllowQuestion ? "*?" : "*");
     630           0 :   if (index < 0) {
     631           0 :     mPathLiteral = mGlob;
     632           0 :     return;
     633             :   }
     634             : 
     635             :   // Check for a prefix match, where the only glob metacharacter is a "*"
     636             :   // at the end of the string.
     637           0 :   if (index == (int32_t)mGlob.Length() - 1 && mGlob[index] == '*') {
     638           0 :     mPathLiteral = StringHead(mGlob, index);
     639           0 :     mIsPrefix = true;
     640           0 :     return;
     641             :   }
     642             : 
     643             :   // Fall back to the regexp slow path.
     644           0 :   NS_NAMED_LITERAL_CSTRING(metaChars, ".+*?^${}()|[]\\");
     645             : 
     646           0 :   nsAutoString escaped;
     647           0 :   escaped.Append('^');
     648             : 
     649           0 :   for (uint32_t i = 0; i < mGlob.Length(); i++) {
     650           0 :     auto c = mGlob[i];
     651           0 :     if (c == '*') {
     652           0 :       escaped.AppendLiteral(".*");
     653           0 :     } else if (c == '?' && aAllowQuestion) {
     654           0 :       escaped.Append('.');
     655             :     } else {
     656           0 :       if (metaChars.Contains(c)) {
     657           0 :         escaped.Append('\\');
     658             :       }
     659           0 :       escaped.Append(c);
     660             :     }
     661             :   }
     662             : 
     663           0 :   escaped.Append('$');
     664             : 
     665             :   // TODO: Switch to the Rust regexp crate, when Rust integration is easier.
     666             :   // It uses a much more efficient, linear time matching algorithm, and
     667             :   // doesn't require special casing for the literal and prefix cases.
     668           0 :   mRegExp = JS_NewUCRegExpObject(aCx, escaped.get(), escaped.Length(), 0);
     669           0 :   if (mRegExp) {
     670           0 :     mozilla::HoldJSObjects(this);
     671             :   } else {
     672           0 :     aRv.NoteJSContextException(aCx);
     673             :   }
     674             : }
     675             : 
     676             : bool
     677           0 : MatchGlob::Matches(const nsAString& aString) const
     678             : {
     679           0 :   if (mRegExp) {
     680           0 :     AutoJSAPI jsapi;
     681           0 :     jsapi.Init();
     682           0 :     JSContext* cx = jsapi.cx();
     683             : 
     684           0 :     JSAutoCompartment ac(cx, mRegExp);
     685             : 
     686           0 :     JS::RootedObject regexp(cx, mRegExp);
     687           0 :     JS::RootedValue result(cx);
     688             : 
     689           0 :     nsString input(aString);
     690             : 
     691           0 :     size_t index = 0;
     692           0 :     if (!JS_ExecuteRegExpNoStatics(cx, regexp, input.BeginWriting(), aString.Length(),
     693             :                                    &index, true, &result)) {
     694           0 :       return false;
     695             :     }
     696             : 
     697           0 :     return result.isBoolean() && result.toBoolean();
     698             :   }
     699             : 
     700           0 :   if (mIsPrefix) {
     701           0 :     return mPathLiteral == StringHead(aString, mPathLiteral.Length());
     702             :   }
     703             : 
     704           0 :   return mPathLiteral == aString;
     705             : }
     706             : 
     707             : 
     708             : JSObject*
     709           0 : MatchGlob::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
     710             : {
     711           0 :   return MatchGlobBinding::Wrap(aCx, this, aGivenProto);
     712             : }
     713             : 
     714             : 
     715             : NS_IMPL_CYCLE_COLLECTION_CLASS(MatchGlob)
     716             : 
     717           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MatchGlob)
     718           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
     719           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
     720           0 :   tmp->mRegExp = nullptr;
     721           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     722             : 
     723           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MatchGlob)
     724           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
     725           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     726             : 
     727           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(MatchGlob)
     728           0 :   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
     729           0 :   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRegExp)
     730           0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
     731             : 
     732           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MatchGlob)
     733           0 :   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     734           0 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
     735           0 : NS_INTERFACE_MAP_END
     736             : 
     737           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(MatchGlob)
     738           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(MatchGlob)
     739             : 
     740             : 
     741             : /*****************************************************************************
     742             :  * MatchGlobSet
     743             :  *****************************************************************************/
     744             : 
     745             : bool
     746           0 : MatchGlobSet::Matches(const nsAString& aValue) const
     747             : {
     748           0 :   for (auto& glob : *this) {
     749           0 :     if (glob->Matches(aValue)) {
     750           0 :       return true;
     751             :     }
     752             :   }
     753           0 :   return false;
     754             : }
     755             : 
     756             : } // namespace extensions
     757             : } // namespace mozilla
     758             : 

Generated by: LCOV version 1.13