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