From dd2d07a12cfc7c0686daee37b766a6d4dd16f4cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Mon, 2 Oct 2017 15:32:07 +0200 Subject: [PATCH] Bug 1404897: Allow storing Servo selector lists in the document's selector cache. r=heycam MozReview-Commit-ID: GtIWGN2zEGT --HG-- extra : rebase_source : 42de988406724fd3dbb11134cb454bbfc927e3a6 --- dom/base/nsDocument.cpp | 30 +++++++++++++- dom/base/nsIDocument.h | 90 ++++++++++++++++++++++++++++++++++++++--- dom/base/nsINode.cpp | 16 ++++---- 3 files changed, 122 insertions(+), 14 deletions(-) diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index e1996f8598cc..9e0a50629e01 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -1406,13 +1406,39 @@ nsIDocument::SelectorCache::~SelectorCache() AgeAllGenerations(); } +void +nsIDocument::SelectorCache::SelectorList::Reset() +{ + if (mIsServo) { + if (mServo) { + Servo_SelectorList_Drop(mServo); + mServo = nullptr; + } + } else { + if (mGecko) { + delete mGecko; + mGecko = nullptr; + } + } +} + // CacheList takes ownership of aSelectorList. void nsIDocument::SelectorCache::CacheList(const nsAString& aSelector, - nsCSSSelectorList* aSelectorList) + mozilla::UniquePtr&& aSelectorList) { MOZ_ASSERT(NS_IsMainThread()); SelectorCacheKey* key = new SelectorCacheKey(aSelector); - mTable.Put(key->mKey, aSelectorList); + mTable.Put(key->mKey, SelectorList(Move(aSelectorList))); + AddObject(key); +} + +void nsIDocument::SelectorCache::CacheList( + const nsAString& aSelector, + UniquePtr&& aSelectorList) +{ + MOZ_ASSERT(NS_IsMainThread()); + SelectorCacheKey* key = new SelectorCacheKey(aSelector); + mTable.Put(key->mKey, SelectorList(Move(aSelectorList))); AddObject(key); } diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index eeffe405ca12..917dd3563441 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -37,6 +37,7 @@ #include "mozilla/LinkedList.h" #include "mozilla/NotNull.h" #include "mozilla/SegmentedVector.h" +#include "mozilla/ServoBindingTypes.h" #include "mozilla/StyleBackendType.h" #include "mozilla/StyleSheet.h" #include "mozilla/TimeStamp.h" @@ -1148,27 +1149,106 @@ public: : public nsExpirationTracker { public: + class SelectorList + { + public: + SelectorList() + : mIsServo(false) + , mGecko(nullptr) + {} + + SelectorList(SelectorList&& aOther) + { + *this = mozilla::Move(aOther); + } + + SelectorList& operator=(SelectorList&& aOther) + { + Reset(); + mIsServo = aOther.mIsServo; + if (mIsServo) { + mServo = aOther.mServo; + aOther.mServo = nullptr; + } else { + mGecko = aOther.mGecko; + aOther.mGecko = nullptr; + } + return *this; + } + + SelectorList(const SelectorList& aOther) = delete; + + explicit SelectorList(mozilla::UniquePtr&& aList) + : mIsServo(true) + , mServo(aList.release()) + {} + + explicit SelectorList(mozilla::UniquePtr&& aList) + : mIsServo(false) + , mGecko(aList.release()) + {} + + ~SelectorList() { + Reset(); + } + + bool IsServo() const { return mIsServo; } + bool IsGecko() const { return !IsServo(); } + + explicit operator bool() const + { + return IsServo() ? !!AsServo() : !!AsGecko(); + } + + nsCSSSelectorList* AsGecko() const + { + MOZ_ASSERT(IsGecko()); + return mGecko; + } + + RawServoSelectorList* AsServo() const + { + MOZ_ASSERT(IsServo()); + return mServo; + } + + private: + void Reset(); + + bool mIsServo; + + union { + nsCSSSelectorList* mGecko; + RawServoSelectorList* mServo; + }; + }; + explicit SelectorCache(nsIEventTarget* aEventTarget); // CacheList takes ownership of aSelectorList. - void CacheList(const nsAString& aSelector, nsCSSSelectorList* aSelectorList); + void CacheList(const nsAString& aSelector, + mozilla::UniquePtr&& aSelectorList); + void CacheList(const nsAString& aSelector, + mozilla::UniquePtr&& aSelectorList); virtual void NotifyExpired(SelectorCacheKey* aSelector) override; // We do not call MarkUsed because it would just slow down lookups and // because we're OK expiring things after a few seconds even if they're // being used. Returns whether we actually had an entry for aSelector. - // If we have an entry and *aList is null, that indicates that aSelector + // + // If we have an entry and the selector list returned has a null + // nsCSSSelectorList*/RawServoSelectorList*, that indicates that aSelector // has already been parsed and is not a syntactically valid selector. - bool GetList(const nsAString& aSelector, nsCSSSelectorList** aList) + SelectorList* GetList(const nsAString& aSelector) { - return mTable.Get(aSelector, aList); + return mTable.GetValue(aSelector); } ~SelectorCache(); private: - nsClassHashtable mTable; + nsDataHashtable mTable; }; SelectorCache& GetSelectorCache() { diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index 1674548fd26a..9d97f589b36d 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -2682,21 +2682,23 @@ nsINode::ParseSelectorList(const nsAString& aSelectorString, { nsIDocument* doc = OwnerDoc(); nsIDocument::SelectorCache& cache = doc->GetSelectorCache(); - nsCSSSelectorList* selectorList = nullptr; - bool haveCachedList = cache.GetList(aSelectorString, &selectorList); - if (haveCachedList) { - if (!selectorList) { + nsIDocument::SelectorCache::SelectorList* list = + cache.GetList(aSelectorString); + if (list) { + if (!*list) { // Invalid selector. aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR, NS_LITERAL_CSTRING("'") + NS_ConvertUTF16toUTF8(aSelectorString) + NS_LITERAL_CSTRING("' is not a valid selector") ); } - return selectorList; + MOZ_ASSERT(list->IsGecko(), "We haven't done anything with Servo yet"); + return list->AsGecko(); } nsCSSParser parser(doc->CSSLoader()); + nsCSSSelectorList* selectorList = nullptr; aRv = parser.ParseSelectorString(aSelectorString, doc->GetDocumentURI(), 0, // XXXbz get the line number! @@ -2714,7 +2716,7 @@ nsINode::ParseSelectorList(const nsAString& aSelectorString, NS_LITERAL_CSTRING("' is not a valid selector") ); - cache.CacheList(aSelectorString, nullptr); + cache.CacheList(aSelectorString, UniquePtr()); return nullptr; } @@ -2734,7 +2736,7 @@ nsINode::ParseSelectorList(const nsAString& aSelectorString, if (selectorList) { NS_ASSERTION(selectorList->mSelectors, "How can we not have any selectors?"); - cache.CacheList(aSelectorString, selectorList); + cache.CacheList(aSelectorString, UniquePtr(selectorList)); } else { // This is the "only pseudo-element selectors" case, which is // not common, so just don't worry about caching it. That way a