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