forked from mirrors/gecko-dev
By looking a bit more into the pernosco session attached to this bug, I did notice that MatchPattern::Init is being called late in the xpcom shutdown (seems to be triggered by some pending Promise jobs related to loading the builtin search extension, which in that run got scheduled during the late shutdown). Under these conditions, AtomSet::Get does allocate again the static RefPtr (because the previous one was already destroyed as part of the xpcom shutdown), but it gets destroyed by ClearOnShutdown before the RefPtr gets to the caller: - https://searchfox.org/mozilla-central/rev/a8b75e4ba3f8ddf0e76b42681d0a7b7e78e67730/xpcom/base/ClearOnShutdown.cpp#19-24 This patch does: - check explicitly in MatchPattern::Init if we are get past the XPCOMShutdownFinal phase, and throws an explicit NS_ERROR_ILLEGAL_DURING_SHUTDOWN error if that is the case - change the signature for AtomSet::Get static method, to have an explicit non discardable nsresult as its result value, to track down other similar AtomSet::Get calls that may have to take this scenario into account (and to make it a bit more clear from the method signature that it can actually fail and the returned nsresult should be checked before actually using the refptr). As an additional side note: - I've looked into existing crash reports with this signature but I didn't find any (but I won't exclude that I may have not looked enough) - I've tried to reproduce conditions similar to the ones that I observed in the pernosco session (by cheating a bit and trying to force similar conditions with some temporary changes applied locally) but I wasn't able to trigger it in the exact same way Nevertheless by looking into some past bugzilla issues it looks that - it did already happen in the past that we would be still loading search extensions late in shutdown, (e.g. Bug 1620227) and so it doesn't seem totally surprising that there are other ways that we may be still in the progress. - we did fix some other crashes due to some C++ code being triggered by the js runtime late in shutdown (e.g. Bug 1663315 is one that I did notice in the middle of the bugs I looked), and so it doesn't seem that unexpected that we may still have a call to MatchPattern::Init that late in the shutdown. And so this seems to be a legit scenario to account for, even if I wasn't able to trigger it exactly like Grizzly triggered it during a fuzzy test run. Differential Revision: https://phabricator.services.mozilla.com/D112638
321 lines
9.1 KiB
C++
321 lines
9.1 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef mozilla_extensions_MatchPattern_h
|
|
#define mozilla_extensions_MatchPattern_h
|
|
|
|
#include <utility>
|
|
|
|
#include "mozilla/dom/BindingDeclarations.h"
|
|
#include "mozilla/dom/MatchPatternBinding.h"
|
|
#include "mozilla/extensions/MatchGlob.h"
|
|
|
|
#include "jspubtd.h"
|
|
|
|
#include "mozilla/ClearOnShutdown.h"
|
|
#include "mozilla/Likely.h"
|
|
#include "mozilla/Maybe.h"
|
|
#include "mozilla/RefCounted.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsCycleCollectionParticipant.h"
|
|
#include "nsTArray.h"
|
|
#include "nsAtom.h"
|
|
#include "nsICookie.h"
|
|
#include "nsISupports.h"
|
|
#include "nsIURI.h"
|
|
#include "nsWrapperCache.h"
|
|
|
|
namespace mozilla {
|
|
namespace extensions {
|
|
|
|
using dom::MatchPatternOptions;
|
|
|
|
// A sorted, binary-search-backed set of atoms, optimized for frequent lookups
|
|
// and infrequent updates.
|
|
class AtomSet final : public RefCounted<AtomSet> {
|
|
using ArrayType = AutoTArray<RefPtr<nsAtom>, 1>;
|
|
|
|
public:
|
|
MOZ_DECLARE_REFCOUNTED_TYPENAME(AtomSet)
|
|
|
|
explicit AtomSet(const nsTArray<nsString>& aElems);
|
|
|
|
explicit AtomSet(const char** aElems);
|
|
|
|
MOZ_IMPLICIT AtomSet(std::initializer_list<nsAtom*> aIL);
|
|
|
|
bool Contains(const nsAString& elem) const {
|
|
RefPtr<nsAtom> atom = NS_AtomizeMainThread(elem);
|
|
return Contains(atom);
|
|
}
|
|
|
|
bool Contains(const nsACString& aElem) const {
|
|
RefPtr<nsAtom> atom = NS_Atomize(aElem);
|
|
return Contains(atom);
|
|
}
|
|
|
|
bool Contains(const nsAtom* aAtom) const {
|
|
return mElems.ContainsSorted(aAtom);
|
|
}
|
|
|
|
bool Intersects(const AtomSet& aOther) const;
|
|
|
|
void Add(nsAtom* aElem);
|
|
void Remove(nsAtom* aElem);
|
|
|
|
void Add(const nsAString& aElem) {
|
|
RefPtr<nsAtom> atom = NS_AtomizeMainThread(aElem);
|
|
return Add(atom);
|
|
}
|
|
|
|
void Remove(const nsAString& aElem) {
|
|
RefPtr<nsAtom> atom = NS_AtomizeMainThread(aElem);
|
|
return Remove(atom);
|
|
}
|
|
|
|
// Returns a cached, statically-allocated matcher for the given set of
|
|
// literal strings.
|
|
template <const char** schemes>
|
|
[[nodiscard]] static nsresult Get(RefPtr<AtomSet>& aMatcherOut) {
|
|
static RefPtr<AtomSet> sMatcher;
|
|
|
|
if (MOZ_UNLIKELY(!sMatcher)) {
|
|
// If this static method is called late during the shutdown,
|
|
// ClearOnShutdown would be destroying the instance before the
|
|
// RefPtr gets to the caller, let's make sure that this method
|
|
// signature does make it more clear by returning an explicit
|
|
// not discardable nsresult.
|
|
if (PastShutdownPhase(ShutdownPhase::XPCOMShutdownFinal)) {
|
|
aMatcherOut = nullptr;
|
|
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
|
|
}
|
|
|
|
sMatcher = new AtomSet(schemes);
|
|
ClearOnShutdown(&sMatcher);
|
|
}
|
|
|
|
MOZ_ASSERT(sMatcher);
|
|
aMatcherOut = do_AddRef(sMatcher);
|
|
return NS_OK;
|
|
}
|
|
|
|
void Get(nsTArray<nsString>& aResult) const {
|
|
aResult.SetCapacity(mElems.Length());
|
|
|
|
for (const auto& atom : mElems) {
|
|
aResult.AppendElement(nsDependentAtomString(atom));
|
|
}
|
|
}
|
|
|
|
auto begin() const -> decltype(std::declval<const ArrayType>().begin()) {
|
|
return mElems.begin();
|
|
}
|
|
|
|
auto end() const -> decltype(std::declval<const ArrayType>().end()) {
|
|
return mElems.end();
|
|
}
|
|
|
|
private:
|
|
ArrayType mElems;
|
|
|
|
void SortAndUniquify();
|
|
};
|
|
|
|
// A helper class to lazily retrieve, transcode, and atomize certain URI
|
|
// properties the first time they're used, and cache the results, so that they
|
|
// can be used across multiple match operations.
|
|
class URLInfo final {
|
|
public:
|
|
MOZ_IMPLICIT URLInfo(nsIURI* aURI) : mURI(aURI) { mHost.SetIsVoid(true); }
|
|
|
|
URLInfo(nsIURI* aURI, bool aNoRef) : URLInfo(aURI) {
|
|
if (aNoRef) {
|
|
mURINoRef = mURI;
|
|
}
|
|
}
|
|
|
|
URLInfo(const URLInfo& aOther) : URLInfo(aOther.mURI.get()) {}
|
|
|
|
nsIURI* URI() const { return mURI; }
|
|
|
|
nsAtom* Scheme() const;
|
|
const nsCString& Host() const;
|
|
const nsAtom* HostAtom() const;
|
|
const nsString& Path() const;
|
|
const nsString& FilePath() const;
|
|
const nsString& Spec() const;
|
|
const nsCString& CSpec() const;
|
|
|
|
bool InheritsPrincipal() const;
|
|
|
|
private:
|
|
nsIURI* URINoRef() const;
|
|
|
|
nsCOMPtr<nsIURI> mURI;
|
|
mutable nsCOMPtr<nsIURI> mURINoRef;
|
|
|
|
mutable RefPtr<nsAtom> mScheme;
|
|
mutable nsCString mHost;
|
|
mutable RefPtr<nsAtom> mHostAtom;
|
|
|
|
mutable nsString mPath;
|
|
mutable nsString mFilePath;
|
|
mutable nsString mSpec;
|
|
mutable nsCString mCSpec;
|
|
|
|
mutable Maybe<bool> mInheritsPrincipal;
|
|
};
|
|
|
|
// Similar to URLInfo, but for cookies.
|
|
class MOZ_STACK_CLASS CookieInfo final {
|
|
public:
|
|
MOZ_IMPLICIT CookieInfo(nsICookie* aCookie) : mCookie(aCookie) {}
|
|
|
|
bool IsSecure() const;
|
|
bool IsDomain() const;
|
|
|
|
const nsCString& Host() const;
|
|
const nsCString& RawHost() const;
|
|
|
|
private:
|
|
nsCOMPtr<nsICookie> mCookie;
|
|
|
|
mutable Maybe<bool> mIsSecure;
|
|
mutable Maybe<bool> mIsDomain;
|
|
|
|
mutable nsCString mHost;
|
|
mutable nsCString mRawHost;
|
|
};
|
|
|
|
class MatchPattern final : public nsISupports, public nsWrapperCache {
|
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MatchPattern)
|
|
|
|
static already_AddRefed<MatchPattern> Constructor(
|
|
dom::GlobalObject& aGlobal, const nsAString& aPattern,
|
|
const MatchPatternOptions& aOptions, ErrorResult& aRv);
|
|
|
|
bool Matches(const nsAString& aURL, bool aExplicit, ErrorResult& aRv) const;
|
|
|
|
bool Matches(const URLInfo& aURL, bool aExplicit = false) const;
|
|
|
|
bool Matches(const URLInfo& aURL, bool aExplicit, ErrorResult& aRv) const {
|
|
return Matches(aURL, aExplicit);
|
|
}
|
|
|
|
bool MatchesCookie(const CookieInfo& aCookie) const;
|
|
|
|
bool MatchesDomain(const nsACString& aDomain) const;
|
|
|
|
bool Subsumes(const MatchPattern& aPattern) const;
|
|
|
|
bool SubsumesDomain(const MatchPattern& aPattern) const;
|
|
|
|
bool Overlaps(const MatchPattern& aPattern) const;
|
|
|
|
bool DomainIsWildcard() const { return mMatchSubdomain && mDomain.IsEmpty(); }
|
|
|
|
void GetPattern(nsAString& aPattern) const { aPattern = mPattern; }
|
|
|
|
nsISupports* GetParentObject() const { return mParent; }
|
|
|
|
virtual JSObject* WrapObject(JSContext* aCx,
|
|
JS::HandleObject aGivenProto) override;
|
|
|
|
protected:
|
|
virtual ~MatchPattern() = default;
|
|
|
|
private:
|
|
explicit MatchPattern(nsISupports* aParent) : mParent(aParent) {}
|
|
|
|
void Init(JSContext* aCx, const nsAString& aPattern, bool aIgnorePath,
|
|
bool aRestrictSchemes, ErrorResult& aRv);
|
|
|
|
nsCOMPtr<nsISupports> mParent;
|
|
|
|
// The normalized match pattern string that this object represents.
|
|
nsString mPattern;
|
|
|
|
// The set of atomized URI schemes that this pattern matches.
|
|
RefPtr<AtomSet> mSchemes;
|
|
|
|
// The domain that this matcher matches. If mMatchSubdomain is false, only
|
|
// matches the exact domain. If it's true, matches the domain or any
|
|
// subdomain.
|
|
//
|
|
// For instance, "*.foo.com" gives mDomain = "foo.com" and mMatchSubdomain =
|
|
// true, and matches "foo.com" or "bar.foo.com" but not "barfoo.com".
|
|
//
|
|
// While "foo.com" gives mDomain = "foo.com" and mMatchSubdomain = false,
|
|
// and matches "foo.com" but not "bar.foo.com".
|
|
nsCString mDomain;
|
|
bool mMatchSubdomain = false;
|
|
|
|
// The glob against which the URL path must match. If null, the path is
|
|
// ignored entirely. If non-null, the path must match this glob.
|
|
RefPtr<MatchGlob> mPath;
|
|
|
|
public:
|
|
// A quick way to check if a particular URL matches <all_urls> without
|
|
// actually instantiating a MatchPattern
|
|
static bool MatchesAllURLs(const URLInfo& aURL);
|
|
};
|
|
|
|
class MatchPatternSet final : public nsISupports, public nsWrapperCache {
|
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MatchPatternSet)
|
|
|
|
using ArrayType = nsTArray<RefPtr<MatchPattern>>;
|
|
|
|
static already_AddRefed<MatchPatternSet> Constructor(
|
|
dom::GlobalObject& aGlobal,
|
|
const nsTArray<dom::OwningStringOrMatchPattern>& aPatterns,
|
|
const MatchPatternOptions& aOptions, ErrorResult& aRv);
|
|
|
|
bool Matches(const nsAString& aURL, bool aExplicit, ErrorResult& aRv) const;
|
|
|
|
bool Matches(const URLInfo& aURL, bool aExplicit = false) const;
|
|
|
|
bool Matches(const URLInfo& aURL, bool aExplicit, ErrorResult& aRv) const {
|
|
return Matches(aURL, aExplicit);
|
|
}
|
|
|
|
bool MatchesCookie(const CookieInfo& aCookie) const;
|
|
|
|
bool Subsumes(const MatchPattern& aPattern) const;
|
|
|
|
bool SubsumesDomain(const MatchPattern& aPattern) const;
|
|
|
|
bool Overlaps(const MatchPattern& aPattern) const;
|
|
|
|
bool Overlaps(const MatchPatternSet& aPatternSet) const;
|
|
|
|
bool OverlapsAll(const MatchPatternSet& aPatternSet) const;
|
|
|
|
void GetPatterns(ArrayType& aPatterns) {
|
|
aPatterns.AppendElements(mPatterns);
|
|
}
|
|
|
|
nsISupports* GetParentObject() const { return mParent; }
|
|
|
|
virtual JSObject* WrapObject(JSContext* aCx,
|
|
JS::HandleObject aGivenProto) override;
|
|
|
|
protected:
|
|
virtual ~MatchPatternSet() = default;
|
|
|
|
private:
|
|
explicit MatchPatternSet(nsISupports* aParent, ArrayType&& aPatterns)
|
|
: mParent(aParent), mPatterns(std::forward<ArrayType>(aPatterns)) {}
|
|
|
|
nsCOMPtr<nsISupports> mParent;
|
|
|
|
ArrayType mPatterns;
|
|
};
|
|
|
|
} // namespace extensions
|
|
} // namespace mozilla
|
|
|
|
#endif // mozilla_extensions_MatchPattern_h
|