Skip to content

Commit a525f60

Browse files
committed
Bug 1693271 - Part 1: Use RustRegex for MatchGlob, r=kmag
This also involves making MatchGlob operate on UTF8String instead of DOMString, as the rust `regex` crate operates on utf-8 strings. This should have no functional impact on callers. Differential Revision: https://phabricator.services.mozilla.com/D158877
1 parent 5af90d1 commit a525f60

9 files changed

+56
-99
lines changed

dom/chrome-webidl/MatchGlob.webidl

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@
1111
[ChromeOnly, Exposed=Window]
1212
interface MatchGlob {
1313
[Throws]
14-
constructor(DOMString glob, optional boolean allowQuestion = true);
15-
14+
constructor(UTF8String glob, optional boolean allowQuestion = true);
15+
1616
/**
1717
* Returns true if the string matches the glob.
1818
*/
19-
boolean matches(DOMString string);
19+
boolean matches(UTF8String string);
2020

2121
/**
2222
* The glob string this MatchGlob represents.
2323
*/
2424
[Constant]
25-
readonly attribute DOMString glob;
25+
readonly attribute UTF8String glob;
2626
};

dom/chrome-webidl/WebExtensionContentScript.webidl

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ interface URI;
77
interface WindowProxy;
88

99
typedef (MatchPatternSet or sequence<DOMString>) MatchPatternSetOrStringSequence;
10-
typedef (MatchGlob or DOMString) MatchGlobOrString;
10+
typedef (MatchGlob or UTF8String) MatchGlobOrString;
1111

1212
[ChromeOnly, Exposed=Window]
1313
interface MozDocumentMatcher {

dom/chrome-webidl/WebExtensionPolicy.webidl

+2-2
Original file line numberDiff line numberDiff line change
@@ -176,14 +176,14 @@ interface WebExtensionPolicy {
176176
* URL root is listed as a web accessible path. Access checks on a path, such
177177
* as performed in nsScriptSecurityManager, use sourceMayAccessPath below.
178178
*/
179-
boolean isWebAccessiblePath(DOMString pathname);
179+
boolean isWebAccessiblePath(UTF8String pathname);
180180

181181
/**
182182
* Returns true if the given path relative to the extension's moz-extension:
183183
* URL root may be accessed by web content at sourceURI. For Manifest V2,
184184
* sourceURI is ignored and the path must merely be listed as web accessible.
185185
*/
186-
boolean sourceMayAccessPath(URI sourceURI, DOMString pathname);
186+
boolean sourceMayAccessPath(URI sourceURI, UTF8String pathname);
187187

188188
/**
189189
* Replaces localization placeholders in the given string with localized

dom/security/nsContentSecurityUtils.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ FilenameTypeAndDetails nsContentSecurityUtils::FilenameToFilenameType(
459459
sanitizedPathAndScheme.Append(u"can't get addon off main thread]"_ns);
460460
}
461461

462-
sanitizedPathAndScheme.Append(url.FilePath());
462+
AppendUTF8toUTF16(url.FilePath(), sanitizedPathAndScheme);
463463
return FilenameTypeAndDetails(kExtensionURI, Some(sanitizedPathAndScheme));
464464
}
465465

toolkit/components/extensions/MatchGlob.h

+11-12
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "jspubtd.h"
1313
#include "js/RootingAPI.h"
1414

15+
#include "mozilla/RustRegex.h"
1516
#include "nsCOMPtr.h"
1617
#include "nsCycleCollectionParticipant.h"
1718
#include "nsISupports.h"
@@ -25,18 +26,18 @@ class MatchPattern;
2526
class MatchGlob final : public nsISupports, public nsWrapperCache {
2627
public:
2728
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
28-
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MatchGlob)
29+
NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(MatchGlob)
2930

3031
static already_AddRefed<MatchGlob> Constructor(dom::GlobalObject& aGlobal,
31-
const nsAString& aGlob,
32+
const nsACString& aGlob,
3233
bool aAllowQuestion,
3334
ErrorResult& aRv);
3435

35-
bool Matches(const nsAString& aString) const;
36+
bool Matches(const nsACString& aString) const;
3637

3738
bool IsWildcard() const { return mIsPrefix && mPathLiteral.IsEmpty(); }
3839

39-
void GetGlob(nsAString& aGlob) const { aGlob = mGlob; }
40+
void GetGlob(nsACString& aGlob) const { aGlob = mGlob; }
4041

4142
nsISupports* GetParentObject() const { return mParent; }
4243

@@ -49,27 +50,25 @@ class MatchGlob final : public nsISupports, public nsWrapperCache {
4950
private:
5051
friend class MatchPattern;
5152

52-
explicit MatchGlob(nsISupports* aParent) : mParent(aParent) {}
53-
54-
void Init(JSContext* aCx, const nsAString& aGlob, bool aAllowQuestion,
55-
ErrorResult& aRv);
53+
explicit MatchGlob(nsISupports* aParent, const nsACString& aGlob,
54+
bool aAllowQuestion, ErrorResult& aRv);
5655

5756
nsCOMPtr<nsISupports> mParent;
5857

5958
// The original glob string that this glob object represents.
60-
nsString mGlob;
59+
const nsCString mGlob;
6160

6261
// The literal path string to match against. If this contains a non-void
6362
// value, the glob matches against this exact literal string, rather than
6463
// performng a pattern match. If mIsPrefix is true, the literal must appear
6564
// at the start of the matched string. If it is false, the the literal must
6665
// be exactly equal to the matched string.
67-
nsString mPathLiteral;
66+
nsCString mPathLiteral;
6867
bool mIsPrefix = false;
6968

7069
// The regular expression object which is equivalent to this glob pattern.
7170
// Used for matching if, and only if, mPathLiteral is non-void.
72-
JS::Heap<JSObject*> mRegExp;
71+
RustRegex mRegExp;
7372
};
7473

7574
class MatchGlobSet final : public CopyableTArray<RefPtr<MatchGlob>> {
@@ -84,7 +83,7 @@ class MatchGlobSet final : public CopyableTArray<RefPtr<MatchGlob>> {
8483
MOZ_IMPLICIT MatchGlobSet(std::initializer_list<RefPtr<MatchGlob>> aIL)
8584
: CopyableTArray(aIL) {}
8685

87-
bool Matches(const nsAString& aValue) const;
86+
bool Matches(const nsACString& aValue) const;
8887
};
8988

9089
} // namespace extensions

toolkit/components/extensions/MatchPattern.cpp

+23-66
Original file line numberDiff line numberDiff line change
@@ -123,24 +123,20 @@ const nsAtom* URLInfo::HostAtom() const {
123123
return mHostAtom;
124124
}
125125

126-
const nsString& URLInfo::FilePath() const {
126+
const nsCString& URLInfo::FilePath() const {
127127
if (mFilePath.IsEmpty()) {
128-
nsCString path;
129128
nsCOMPtr<nsIURL> url = do_QueryInterface(mURI);
130-
if (url && NS_SUCCEEDED(url->GetFilePath(path))) {
131-
AppendUTF8toUTF16(path, mFilePath);
132-
} else {
129+
if (!url || NS_FAILED(url->GetFilePath(mFilePath))) {
133130
mFilePath = Path();
134131
}
135132
}
136133
return mFilePath;
137134
}
138135

139-
const nsString& URLInfo::Path() const {
136+
const nsCString& URLInfo::Path() const {
140137
if (mPath.IsEmpty()) {
141-
nsCString path;
142-
if (NS_SUCCEEDED(URINoRef()->GetPathQueryRef(path))) {
143-
AppendUTF8toUTF16(path, mPath);
138+
if (NS_FAILED(URINoRef()->GetPathQueryRef(mPath))) {
139+
mPath.Truncate();
144140
}
145141
}
146142
return mPath;
@@ -359,14 +355,13 @@ void MatchPattern::Init(JSContext* aCx, const nsAString& aPattern,
359355
return;
360356
}
361357

362-
auto path = tail;
358+
NS_ConvertUTF16toUTF8 path(tail);
363359
if (path.IsEmpty()) {
364360
aRv.Throw(NS_ERROR_INVALID_ARG);
365361
return;
366362
}
367363

368-
mPath = new MatchGlob(this);
369-
mPath->Init(aCx, path, false, aRv);
364+
mPath = new MatchGlob(this, path, false, aRv);
370365
}
371366

372367
bool MatchPattern::MatchesDomain(const nsACString& aDomain) const {
@@ -622,27 +617,26 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(MatchPatternSet)
622617
* MatchGlob
623618
*****************************************************************************/
624619

625-
MatchGlob::~MatchGlob() { mozilla::DropJSObjects(this); }
620+
MatchGlob::~MatchGlob() = default;
626621

627622
/* static */
628623
already_AddRefed<MatchGlob> MatchGlob::Constructor(dom::GlobalObject& aGlobal,
629-
const nsAString& aGlob,
624+
const nsACString& aGlob,
630625
bool aAllowQuestion,
631626
ErrorResult& aRv) {
632-
RefPtr<MatchGlob> glob = new MatchGlob(aGlobal.GetAsSupports());
633-
glob->Init(aGlobal.Context(), aGlob, aAllowQuestion, aRv);
627+
RefPtr<MatchGlob> glob =
628+
new MatchGlob(aGlobal.GetAsSupports(), aGlob, aAllowQuestion, aRv);
634629
if (aRv.Failed()) {
635630
return nullptr;
636631
}
637632
return glob.forget();
638633
}
639634

640-
void MatchGlob::Init(JSContext* aCx, const nsAString& aGlob,
641-
bool aAllowQuestion, ErrorResult& aRv) {
642-
mGlob = aGlob;
643-
635+
MatchGlob::MatchGlob(nsISupports* aParent, const nsACString& aGlob,
636+
bool aAllowQuestion, ErrorResult& aRv)
637+
: mParent(aParent), mGlob(aGlob) {
644638
// Check for a literal match with no glob metacharacters.
645-
auto index = mGlob.FindCharInSet(aAllowQuestion ? u"*?" : u"*");
639+
auto index = mGlob.FindCharInSet(aAllowQuestion ? "*?" : "*");
646640
if (index < 0) {
647641
mPathLiteral = mGlob;
648642
return;
@@ -659,7 +653,7 @@ void MatchGlob::Init(JSContext* aCx, const nsAString& aGlob,
659653
// Fall back to the regexp slow path.
660654
constexpr auto metaChars = ".+*?^${}()|[]\\"_ns;
661655

662-
nsAutoString escaped;
656+
nsAutoCString escaped;
663657
escaped.Append('^');
664658

665659
// For any continuous string of * (and ? if aAllowQuestion) wildcards, only
@@ -688,37 +682,15 @@ void MatchGlob::Init(JSContext* aCx, const nsAString& aGlob,
688682

689683
escaped.Append('$');
690684

691-
// TODO: Switch to the Rust regexp crate, when Rust integration is easier.
692-
// It uses a much more efficient, linear time matching algorithm, and
693-
// doesn't require special casing for the literal and prefix cases.
694-
mRegExp = JS::NewUCRegExpObject(aCx, escaped.get(), escaped.Length(), 0);
695-
if (mRegExp) {
696-
mozilla::HoldJSObjects(this);
697-
} else {
698-
aRv.NoteJSContextException(aCx);
685+
mRegExp = RustRegex(escaped);
686+
if (!mRegExp) {
687+
aRv.ThrowTypeError("failed to compile regex for glob");
699688
}
700689
}
701690

702-
bool MatchGlob::Matches(const nsAString& aString) const {
691+
bool MatchGlob::Matches(const nsACString& aString) const {
703692
if (mRegExp) {
704-
AutoJSAPI jsapi;
705-
jsapi.Init();
706-
JSContext* cx = jsapi.cx();
707-
708-
JSAutoRealm ar(cx, mRegExp);
709-
710-
JS::Rooted<JSObject*> regexp(cx, mRegExp);
711-
JS::Rooted<JS::Value> result(cx);
712-
713-
nsString input(aString);
714-
715-
size_t index = 0;
716-
if (!JS::ExecuteRegExpNoStatics(cx, regexp, input.BeginWriting(),
717-
aString.Length(), &index, true, &result)) {
718-
return false;
719-
}
720-
721-
return result.isBoolean() && result.toBoolean();
693+
return mRegExp.IsMatch(aString);
722694
}
723695

724696
if (mIsPrefix) {
@@ -733,22 +705,7 @@ JSObject* MatchGlob::WrapObject(JSContext* aCx,
733705
return MatchGlob_Binding::Wrap(aCx, this, aGivenProto);
734706
}
735707

736-
NS_IMPL_CYCLE_COLLECTION_CLASS(MatchGlob)
737-
738-
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MatchGlob)
739-
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
740-
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
741-
tmp->mRegExp = nullptr;
742-
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
743-
744-
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MatchGlob)
745-
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
746-
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
747-
748-
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(MatchGlob)
749-
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
750-
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRegExp)
751-
NS_IMPL_CYCLE_COLLECTION_TRACE_END
708+
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MatchGlob, mParent)
752709

753710
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MatchGlob)
754711
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@@ -762,7 +719,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(MatchGlob)
762719
* MatchGlobSet
763720
*****************************************************************************/
764721

765-
bool MatchGlobSet::Matches(const nsAString& aValue) const {
722+
bool MatchGlobSet::Matches(const nsACString& aValue) const {
766723
for (auto& glob : *this) {
767724
if (glob->Matches(aValue)) {
768725
return true;

toolkit/components/extensions/MatchPattern.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@ class URLInfo final {
143143
nsAtom* Scheme() const;
144144
const nsCString& Host() const;
145145
const nsAtom* HostAtom() const;
146-
const nsString& Path() const;
147-
const nsString& FilePath() const;
146+
const nsCString& Path() const;
147+
const nsCString& FilePath() const;
148148
const nsString& Spec() const;
149149
const nsCString& CSpec() const;
150150

@@ -160,8 +160,8 @@ class URLInfo final {
160160
mutable nsCString mHost;
161161
mutable RefPtr<nsAtom> mHostAtom;
162162

163-
mutable nsString mPath;
164-
mutable nsString mFilePath;
163+
mutable nsCString mPath;
164+
mutable nsCString mFilePath;
165165
mutable nsString mSpec;
166166
mutable nsCString mCSpec;
167167

toolkit/components/extensions/WebExtensionPolicy.cpp

+6-5
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,15 @@ static nsISubstitutingProtocolHandler* Proto() {
7979
return sHandler;
8080
}
8181

82-
bool ParseGlobs(GlobalObject& aGlobal, Sequence<OwningMatchGlobOrString> aGlobs,
82+
bool ParseGlobs(GlobalObject& aGlobal,
83+
Sequence<OwningMatchGlobOrUTF8String> aGlobs,
8384
nsTArray<RefPtr<MatchGlob>>& aResult, ErrorResult& aRv) {
8485
for (auto& elem : aGlobs) {
8586
if (elem.IsMatchGlob()) {
8687
aResult.AppendElement(elem.GetAsMatchGlob());
8788
} else {
8889
RefPtr<MatchGlob> glob =
89-
MatchGlob::Constructor(aGlobal, elem.GetAsString(), true, aRv);
90+
MatchGlob::Constructor(aGlobal, elem.GetAsUTF8String(), true, aRv);
9091
if (aRv.Failed()) {
9192
return false;
9293
}
@@ -441,7 +442,7 @@ bool WebExtensionPolicy::BackgroundServiceWorkerEnabled(GlobalObject& aGlobal) {
441442
}
442443

443444
bool WebExtensionPolicy::SourceMayAccessPath(const URLInfo& aURI,
444-
const nsAString& aPath) const {
445+
const nsACString& aPath) const {
445446
if (aURI.Scheme() == nsGkAtoms::moz_extension &&
446447
mHostname.Equals(aURI.Host())) {
447448
// An extension can always access it's own paths.
@@ -824,11 +825,11 @@ bool MozDocumentMatcher::MatchesURI(const URLInfo& aURL,
824825
return false;
825826
}
826827

827-
if (!mIncludeGlobs.IsNull() && !mIncludeGlobs.Value().Matches(aURL.Spec())) {
828+
if (!mIncludeGlobs.IsNull() && !mIncludeGlobs.Value().Matches(aURL.CSpec())) {
828829
return false;
829830
}
830831

831-
if (!mExcludeGlobs.IsNull() && mExcludeGlobs.Value().Matches(aURL.Spec())) {
832+
if (!mExcludeGlobs.IsNull() && mExcludeGlobs.Value().Matches(aURL.CSpec())) {
832833
return false;
833834
}
834835

toolkit/components/extensions/WebExtensionPolicy.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ class WebAccessibleResource final : public nsISupports {
4646
const WebAccessibleResourceInit& aInit,
4747
ErrorResult& aRv);
4848

49-
bool IsWebAccessiblePath(const nsAString& aPath) const {
49+
bool IsWebAccessiblePath(const nsACString& aPath) const {
5050
return mWebAccessiblePaths.Matches(aPath);
5151
}
5252

53-
bool SourceMayAccessPath(const URLInfo& aURI, const nsAString& aPath) {
53+
bool SourceMayAccessPath(const URLInfo& aURI, const nsACString& aPath) {
5454
return mWebAccessiblePaths.Matches(aPath) &&
5555
(IsHostMatch(aURI) || IsExtensionMatch(aURI));
5656
}
@@ -115,7 +115,7 @@ class WebExtensionPolicy final : public nsISupports,
115115
bool aCheckRestricted = true,
116116
bool aAllowFilePermission = false) const;
117117

118-
bool IsWebAccessiblePath(const nsAString& aPath) const {
118+
bool IsWebAccessiblePath(const nsACString& aPath) const {
119119
for (const auto& resource : mWebAccessibleResources) {
120120
if (resource->IsWebAccessiblePath(aPath)) {
121121
return true;
@@ -124,7 +124,7 @@ class WebExtensionPolicy final : public nsISupports,
124124
return false;
125125
}
126126

127-
bool SourceMayAccessPath(const URLInfo& aURI, const nsAString& aPath) const;
127+
bool SourceMayAccessPath(const URLInfo& aURI, const nsACString& aPath) const;
128128

129129
bool HasPermission(const nsAtom* aPermission) const {
130130
return mPermissions->Contains(aPermission);

0 commit comments

Comments
 (0)