mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
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
This commit is contained in:
parent
5af90d1cbe
commit
a525f604ba
@ -11,16 +11,16 @@
|
||||
[ChromeOnly, Exposed=Window]
|
||||
interface MatchGlob {
|
||||
[Throws]
|
||||
constructor(DOMString glob, optional boolean allowQuestion = true);
|
||||
|
||||
constructor(UTF8String glob, optional boolean allowQuestion = true);
|
||||
|
||||
/**
|
||||
* Returns true if the string matches the glob.
|
||||
*/
|
||||
boolean matches(DOMString string);
|
||||
boolean matches(UTF8String string);
|
||||
|
||||
/**
|
||||
* The glob string this MatchGlob represents.
|
||||
*/
|
||||
[Constant]
|
||||
readonly attribute DOMString glob;
|
||||
readonly attribute UTF8String glob;
|
||||
};
|
||||
|
@ -7,7 +7,7 @@ interface URI;
|
||||
interface WindowProxy;
|
||||
|
||||
typedef (MatchPatternSet or sequence<DOMString>) MatchPatternSetOrStringSequence;
|
||||
typedef (MatchGlob or DOMString) MatchGlobOrString;
|
||||
typedef (MatchGlob or UTF8String) MatchGlobOrString;
|
||||
|
||||
[ChromeOnly, Exposed=Window]
|
||||
interface MozDocumentMatcher {
|
||||
|
@ -176,14 +176,14 @@ interface WebExtensionPolicy {
|
||||
* URL root is listed as a web accessible path. Access checks on a path, such
|
||||
* as performed in nsScriptSecurityManager, use sourceMayAccessPath below.
|
||||
*/
|
||||
boolean isWebAccessiblePath(DOMString pathname);
|
||||
boolean isWebAccessiblePath(UTF8String pathname);
|
||||
|
||||
/**
|
||||
* Returns true if the given path relative to the extension's moz-extension:
|
||||
* URL root may be accessed by web content at sourceURI. For Manifest V2,
|
||||
* sourceURI is ignored and the path must merely be listed as web accessible.
|
||||
*/
|
||||
boolean sourceMayAccessPath(URI sourceURI, DOMString pathname);
|
||||
boolean sourceMayAccessPath(URI sourceURI, UTF8String pathname);
|
||||
|
||||
/**
|
||||
* Replaces localization placeholders in the given string with localized
|
||||
|
@ -459,7 +459,7 @@ FilenameTypeAndDetails nsContentSecurityUtils::FilenameToFilenameType(
|
||||
sanitizedPathAndScheme.Append(u"can't get addon off main thread]"_ns);
|
||||
}
|
||||
|
||||
sanitizedPathAndScheme.Append(url.FilePath());
|
||||
AppendUTF8toUTF16(url.FilePath(), sanitizedPathAndScheme);
|
||||
return FilenameTypeAndDetails(kExtensionURI, Some(sanitizedPathAndScheme));
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "jspubtd.h"
|
||||
#include "js/RootingAPI.h"
|
||||
|
||||
#include "mozilla/RustRegex.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsISupports.h"
|
||||
@ -25,18 +26,18 @@ class MatchPattern;
|
||||
class MatchGlob final : public nsISupports, public nsWrapperCache {
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MatchGlob)
|
||||
NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(MatchGlob)
|
||||
|
||||
static already_AddRefed<MatchGlob> Constructor(dom::GlobalObject& aGlobal,
|
||||
const nsAString& aGlob,
|
||||
const nsACString& aGlob,
|
||||
bool aAllowQuestion,
|
||||
ErrorResult& aRv);
|
||||
|
||||
bool Matches(const nsAString& aString) const;
|
||||
bool Matches(const nsACString& aString) const;
|
||||
|
||||
bool IsWildcard() const { return mIsPrefix && mPathLiteral.IsEmpty(); }
|
||||
|
||||
void GetGlob(nsAString& aGlob) const { aGlob = mGlob; }
|
||||
void GetGlob(nsACString& aGlob) const { aGlob = mGlob; }
|
||||
|
||||
nsISupports* GetParentObject() const { return mParent; }
|
||||
|
||||
@ -49,27 +50,25 @@ class MatchGlob final : public nsISupports, public nsWrapperCache {
|
||||
private:
|
||||
friend class MatchPattern;
|
||||
|
||||
explicit MatchGlob(nsISupports* aParent) : mParent(aParent) {}
|
||||
|
||||
void Init(JSContext* aCx, const nsAString& aGlob, bool aAllowQuestion,
|
||||
ErrorResult& aRv);
|
||||
explicit MatchGlob(nsISupports* aParent, const nsACString& aGlob,
|
||||
bool aAllowQuestion, ErrorResult& aRv);
|
||||
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
|
||||
// The original glob string that this glob object represents.
|
||||
nsString mGlob;
|
||||
const nsCString mGlob;
|
||||
|
||||
// The literal path string to match against. If this contains a non-void
|
||||
// value, the glob matches against this exact literal string, rather than
|
||||
// performng a pattern match. If mIsPrefix is true, the literal must appear
|
||||
// at the start of the matched string. If it is false, the the literal must
|
||||
// be exactly equal to the matched string.
|
||||
nsString mPathLiteral;
|
||||
nsCString mPathLiteral;
|
||||
bool mIsPrefix = false;
|
||||
|
||||
// The regular expression object which is equivalent to this glob pattern.
|
||||
// Used for matching if, and only if, mPathLiteral is non-void.
|
||||
JS::Heap<JSObject*> mRegExp;
|
||||
RustRegex mRegExp;
|
||||
};
|
||||
|
||||
class MatchGlobSet final : public CopyableTArray<RefPtr<MatchGlob>> {
|
||||
@ -84,7 +83,7 @@ class MatchGlobSet final : public CopyableTArray<RefPtr<MatchGlob>> {
|
||||
MOZ_IMPLICIT MatchGlobSet(std::initializer_list<RefPtr<MatchGlob>> aIL)
|
||||
: CopyableTArray(aIL) {}
|
||||
|
||||
bool Matches(const nsAString& aValue) const;
|
||||
bool Matches(const nsACString& aValue) const;
|
||||
};
|
||||
|
||||
} // namespace extensions
|
||||
|
@ -123,24 +123,20 @@ const nsAtom* URLInfo::HostAtom() const {
|
||||
return mHostAtom;
|
||||
}
|
||||
|
||||
const nsString& URLInfo::FilePath() const {
|
||||
const nsCString& URLInfo::FilePath() const {
|
||||
if (mFilePath.IsEmpty()) {
|
||||
nsCString path;
|
||||
nsCOMPtr<nsIURL> url = do_QueryInterface(mURI);
|
||||
if (url && NS_SUCCEEDED(url->GetFilePath(path))) {
|
||||
AppendUTF8toUTF16(path, mFilePath);
|
||||
} else {
|
||||
if (!url || NS_FAILED(url->GetFilePath(mFilePath))) {
|
||||
mFilePath = Path();
|
||||
}
|
||||
}
|
||||
return mFilePath;
|
||||
}
|
||||
|
||||
const nsString& URLInfo::Path() const {
|
||||
const nsCString& URLInfo::Path() const {
|
||||
if (mPath.IsEmpty()) {
|
||||
nsCString path;
|
||||
if (NS_SUCCEEDED(URINoRef()->GetPathQueryRef(path))) {
|
||||
AppendUTF8toUTF16(path, mPath);
|
||||
if (NS_FAILED(URINoRef()->GetPathQueryRef(mPath))) {
|
||||
mPath.Truncate();
|
||||
}
|
||||
}
|
||||
return mPath;
|
||||
@ -359,14 +355,13 @@ void MatchPattern::Init(JSContext* aCx, const nsAString& aPattern,
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = tail;
|
||||
NS_ConvertUTF16toUTF8 path(tail);
|
||||
if (path.IsEmpty()) {
|
||||
aRv.Throw(NS_ERROR_INVALID_ARG);
|
||||
return;
|
||||
}
|
||||
|
||||
mPath = new MatchGlob(this);
|
||||
mPath->Init(aCx, path, false, aRv);
|
||||
mPath = new MatchGlob(this, path, false, aRv);
|
||||
}
|
||||
|
||||
bool MatchPattern::MatchesDomain(const nsACString& aDomain) const {
|
||||
@ -622,27 +617,26 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(MatchPatternSet)
|
||||
* MatchGlob
|
||||
*****************************************************************************/
|
||||
|
||||
MatchGlob::~MatchGlob() { mozilla::DropJSObjects(this); }
|
||||
MatchGlob::~MatchGlob() = default;
|
||||
|
||||
/* static */
|
||||
already_AddRefed<MatchGlob> MatchGlob::Constructor(dom::GlobalObject& aGlobal,
|
||||
const nsAString& aGlob,
|
||||
const nsACString& aGlob,
|
||||
bool aAllowQuestion,
|
||||
ErrorResult& aRv) {
|
||||
RefPtr<MatchGlob> glob = new MatchGlob(aGlobal.GetAsSupports());
|
||||
glob->Init(aGlobal.Context(), aGlob, aAllowQuestion, aRv);
|
||||
RefPtr<MatchGlob> glob =
|
||||
new MatchGlob(aGlobal.GetAsSupports(), aGlob, aAllowQuestion, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
return glob.forget();
|
||||
}
|
||||
|
||||
void MatchGlob::Init(JSContext* aCx, const nsAString& aGlob,
|
||||
bool aAllowQuestion, ErrorResult& aRv) {
|
||||
mGlob = aGlob;
|
||||
|
||||
MatchGlob::MatchGlob(nsISupports* aParent, const nsACString& aGlob,
|
||||
bool aAllowQuestion, ErrorResult& aRv)
|
||||
: mParent(aParent), mGlob(aGlob) {
|
||||
// Check for a literal match with no glob metacharacters.
|
||||
auto index = mGlob.FindCharInSet(aAllowQuestion ? u"*?" : u"*");
|
||||
auto index = mGlob.FindCharInSet(aAllowQuestion ? "*?" : "*");
|
||||
if (index < 0) {
|
||||
mPathLiteral = mGlob;
|
||||
return;
|
||||
@ -659,7 +653,7 @@ void MatchGlob::Init(JSContext* aCx, const nsAString& aGlob,
|
||||
// Fall back to the regexp slow path.
|
||||
constexpr auto metaChars = ".+*?^${}()|[]\\"_ns;
|
||||
|
||||
nsAutoString escaped;
|
||||
nsAutoCString escaped;
|
||||
escaped.Append('^');
|
||||
|
||||
// For any continuous string of * (and ? if aAllowQuestion) wildcards, only
|
||||
@ -688,37 +682,15 @@ void MatchGlob::Init(JSContext* aCx, const nsAString& aGlob,
|
||||
|
||||
escaped.Append('$');
|
||||
|
||||
// TODO: Switch to the Rust regexp crate, when Rust integration is easier.
|
||||
// It uses a much more efficient, linear time matching algorithm, and
|
||||
// doesn't require special casing for the literal and prefix cases.
|
||||
mRegExp = JS::NewUCRegExpObject(aCx, escaped.get(), escaped.Length(), 0);
|
||||
if (mRegExp) {
|
||||
mozilla::HoldJSObjects(this);
|
||||
} else {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
mRegExp = RustRegex(escaped);
|
||||
if (!mRegExp) {
|
||||
aRv.ThrowTypeError("failed to compile regex for glob");
|
||||
}
|
||||
}
|
||||
|
||||
bool MatchGlob::Matches(const nsAString& aString) const {
|
||||
bool MatchGlob::Matches(const nsACString& aString) const {
|
||||
if (mRegExp) {
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
JSAutoRealm ar(cx, mRegExp);
|
||||
|
||||
JS::Rooted<JSObject*> regexp(cx, mRegExp);
|
||||
JS::Rooted<JS::Value> result(cx);
|
||||
|
||||
nsString input(aString);
|
||||
|
||||
size_t index = 0;
|
||||
if (!JS::ExecuteRegExpNoStatics(cx, regexp, input.BeginWriting(),
|
||||
aString.Length(), &index, true, &result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return result.isBoolean() && result.toBoolean();
|
||||
return mRegExp.IsMatch(aString);
|
||||
}
|
||||
|
||||
if (mIsPrefix) {
|
||||
@ -733,22 +705,7 @@ JSObject* MatchGlob::WrapObject(JSContext* aCx,
|
||||
return MatchGlob_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(MatchGlob)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MatchGlob)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
|
||||
tmp->mRegExp = nullptr;
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MatchGlob)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(MatchGlob)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRegExp)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MatchGlob, mParent)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MatchGlob)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
@ -762,7 +719,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(MatchGlob)
|
||||
* MatchGlobSet
|
||||
*****************************************************************************/
|
||||
|
||||
bool MatchGlobSet::Matches(const nsAString& aValue) const {
|
||||
bool MatchGlobSet::Matches(const nsACString& aValue) const {
|
||||
for (auto& glob : *this) {
|
||||
if (glob->Matches(aValue)) {
|
||||
return true;
|
||||
|
@ -143,8 +143,8 @@ class URLInfo final {
|
||||
nsAtom* Scheme() const;
|
||||
const nsCString& Host() const;
|
||||
const nsAtom* HostAtom() const;
|
||||
const nsString& Path() const;
|
||||
const nsString& FilePath() const;
|
||||
const nsCString& Path() const;
|
||||
const nsCString& FilePath() const;
|
||||
const nsString& Spec() const;
|
||||
const nsCString& CSpec() const;
|
||||
|
||||
@ -160,8 +160,8 @@ class URLInfo final {
|
||||
mutable nsCString mHost;
|
||||
mutable RefPtr<nsAtom> mHostAtom;
|
||||
|
||||
mutable nsString mPath;
|
||||
mutable nsString mFilePath;
|
||||
mutable nsCString mPath;
|
||||
mutable nsCString mFilePath;
|
||||
mutable nsString mSpec;
|
||||
mutable nsCString mCSpec;
|
||||
|
||||
|
@ -79,14 +79,15 @@ static nsISubstitutingProtocolHandler* Proto() {
|
||||
return sHandler;
|
||||
}
|
||||
|
||||
bool ParseGlobs(GlobalObject& aGlobal, Sequence<OwningMatchGlobOrString> aGlobs,
|
||||
bool ParseGlobs(GlobalObject& aGlobal,
|
||||
Sequence<OwningMatchGlobOrUTF8String> aGlobs,
|
||||
nsTArray<RefPtr<MatchGlob>>& aResult, ErrorResult& aRv) {
|
||||
for (auto& elem : aGlobs) {
|
||||
if (elem.IsMatchGlob()) {
|
||||
aResult.AppendElement(elem.GetAsMatchGlob());
|
||||
} else {
|
||||
RefPtr<MatchGlob> glob =
|
||||
MatchGlob::Constructor(aGlobal, elem.GetAsString(), true, aRv);
|
||||
MatchGlob::Constructor(aGlobal, elem.GetAsUTF8String(), true, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return false;
|
||||
}
|
||||
@ -441,7 +442,7 @@ bool WebExtensionPolicy::BackgroundServiceWorkerEnabled(GlobalObject& aGlobal) {
|
||||
}
|
||||
|
||||
bool WebExtensionPolicy::SourceMayAccessPath(const URLInfo& aURI,
|
||||
const nsAString& aPath) const {
|
||||
const nsACString& aPath) const {
|
||||
if (aURI.Scheme() == nsGkAtoms::moz_extension &&
|
||||
mHostname.Equals(aURI.Host())) {
|
||||
// An extension can always access it's own paths.
|
||||
@ -824,11 +825,11 @@ bool MozDocumentMatcher::MatchesURI(const URLInfo& aURL,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mIncludeGlobs.IsNull() && !mIncludeGlobs.Value().Matches(aURL.Spec())) {
|
||||
if (!mIncludeGlobs.IsNull() && !mIncludeGlobs.Value().Matches(aURL.CSpec())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mExcludeGlobs.IsNull() && mExcludeGlobs.Value().Matches(aURL.Spec())) {
|
||||
if (!mExcludeGlobs.IsNull() && mExcludeGlobs.Value().Matches(aURL.CSpec())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -46,11 +46,11 @@ class WebAccessibleResource final : public nsISupports {
|
||||
const WebAccessibleResourceInit& aInit,
|
||||
ErrorResult& aRv);
|
||||
|
||||
bool IsWebAccessiblePath(const nsAString& aPath) const {
|
||||
bool IsWebAccessiblePath(const nsACString& aPath) const {
|
||||
return mWebAccessiblePaths.Matches(aPath);
|
||||
}
|
||||
|
||||
bool SourceMayAccessPath(const URLInfo& aURI, const nsAString& aPath) {
|
||||
bool SourceMayAccessPath(const URLInfo& aURI, const nsACString& aPath) {
|
||||
return mWebAccessiblePaths.Matches(aPath) &&
|
||||
(IsHostMatch(aURI) || IsExtensionMatch(aURI));
|
||||
}
|
||||
@ -115,7 +115,7 @@ class WebExtensionPolicy final : public nsISupports,
|
||||
bool aCheckRestricted = true,
|
||||
bool aAllowFilePermission = false) const;
|
||||
|
||||
bool IsWebAccessiblePath(const nsAString& aPath) const {
|
||||
bool IsWebAccessiblePath(const nsACString& aPath) const {
|
||||
for (const auto& resource : mWebAccessibleResources) {
|
||||
if (resource->IsWebAccessiblePath(aPath)) {
|
||||
return true;
|
||||
@ -124,7 +124,7 @@ class WebExtensionPolicy final : public nsISupports,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SourceMayAccessPath(const URLInfo& aURI, const nsAString& aPath) const;
|
||||
bool SourceMayAccessPath(const URLInfo& aURI, const nsACString& aPath) const;
|
||||
|
||||
bool HasPermission(const nsAtom* aPermission) const {
|
||||
return mPermissions->Contains(aPermission);
|
||||
|
Loading…
Reference in New Issue
Block a user