Bug 1907868 - Optimize nsQueryFrame for all final frame types and ScrollContainerFrame. r=tnikkel,layout-reviewers,TYLin

Differential Revision: https://phabricator.services.mozilla.com/D216682
This commit is contained in:
Emilio Cobos Álvarez 2024-07-17 21:51:45 +00:00
Родитель 5249c141dd
Коммит 0f21b97fe0
7 изменённых файлов: 83 добавлений и 56 удалений

Просмотреть файл

@ -1261,12 +1261,7 @@ nsMapRuleToAttributesFunc HTMLSelectElement::GetAttributeMappingFunction()
}
bool HTMLSelectElement::IsDisabledForEvents(WidgetEvent* aEvent) {
nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
nsIFrame* formFrame = nullptr;
if (formControlFrame) {
formFrame = do_QueryFrame(formControlFrame);
}
return IsElementDisabledForEvents(aEvent, formFrame);
return IsElementDisabledForEvents(aEvent, GetPrimaryFrame());
}
void HTMLSelectElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
@ -1479,10 +1474,8 @@ HTMLSelectElement::SubmitNamesValues(FormData* aFormData) {
}
void HTMLSelectElement::DispatchContentReset() {
if (nsIFormControlFrame* formControlFrame = GetFormControlFrame(false)) {
if (nsListControlFrame* listFrame = do_QueryFrame(formControlFrame)) {
listFrame->OnContentReset();
}
if (nsListControlFrame* listFrame = do_QueryFrame(GetPrimaryFrame())) {
listFrame->OnContentReset();
}
}
@ -1630,8 +1623,7 @@ void HTMLSelectElement::SetUserInteracted(bool aInteracted) {
void HTMLSelectElement::SetPreviewValue(const nsAString& aValue) {
mPreviewValue = aValue;
nsContentUtils::RemoveNewlines(mPreviewValue);
nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
nsComboboxControlFrame* comboFrame = do_QueryFrame(formControlFrame);
nsComboboxControlFrame* comboFrame = do_QueryFrame(GetPrimaryFrame());
if (comboFrame) {
comboFrame->RedisplaySelectedText();
}

Просмотреть файл

@ -100,8 +100,7 @@ ScrollbarActivity::HandleEvent(dom::Event* aEvent) {
// of our scroll frame) and we don't want those to activate us.
nsIFrame* scrollFrame = do_QueryFrame(mScrollableFrame);
MOZ_ASSERT(scrollFrame);
ScrollContainerFrame* scrollContainerFrame =
do_QueryFrame(mScrollableFrame);
ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(scrollFrame);
nsCOMPtr<nsIContent> targetContent =
do_QueryInterface(aEvent->GetOriginalTarget());
nsIFrame* targetFrame =

Просмотреть файл

@ -8124,8 +8124,7 @@ bool nsIFrame::IsImageFrameOrSubclass() const {
bool nsIFrame::IsScrollContainerOrSubclass() const {
const bool result = IsScrollContainerFrame() || IsListControlFrame();
MOZ_ASSERT(result ==
!!static_cast<const ScrollContainerFrame*>(do_QueryFrame(this)));
MOZ_ASSERT(result == !!QueryFrame(ScrollContainerFrame::kFrameIID));
return result;
}

Просмотреть файл

@ -12,8 +12,9 @@
#include "nscore.h"
#include "mozilla/Assertions.h"
// NOTE: the long lines in this file are intentional to make compiler error
// messages more readable.
namespace mozilla {
class ScrollContainerFrame;
}
#define NS_DECL_QUERYFRAME_TARGET(classname) \
static const nsQueryFrame::FrameIID kFrameIID = \
@ -85,18 +86,18 @@ class nsQueryFrame {
class nsIFrame;
template <class Source>
template <typename Source>
class do_QueryFrameHelper {
public:
explicit do_QueryFrameHelper(Source* s) : mRawPtr(s) {}
// The return and argument types here are arbitrarily selected so no
// corresponding member function exists.
typedef void (do_QueryFrameHelper::*MatchNullptr)(double, float);
using MatchNullptr = void (*)(double, float);
// Implicit constructor for nullptr, trick borrowed from already_AddRefed.
MOZ_IMPLICIT do_QueryFrameHelper(MatchNullptr aRawPtr) : mRawPtr(nullptr) {}
template <class Dest>
template <typename Dest>
operator Dest*() {
static_assert(std::is_same_v<std::remove_const_t<Dest>,
typename Dest::Has_NS_DECL_QUERYFRAME_TARGET>,
@ -104,40 +105,71 @@ class do_QueryFrameHelper {
if (!mRawPtr) {
return nullptr;
}
if (Dest* f = FastQueryFrame<Source, Dest>::QueryFrame(mRawPtr)) {
if constexpr (FastQueryFrame<Dest>::kSupported) {
static_assert(
std::is_base_of_v<nsIFrame, Source>,
"We only support fast do_QueryFrame() where the source must be a "
"derived class of nsIFrame. Consider a two-step do_QueryFrame() "
"(once to nsIFrame, another to the target) if absolutely needed.");
Dest* f = FastQueryFrame<Dest>::QueryFrame(mRawPtr);
MOZ_ASSERT(
f == reinterpret_cast<Dest*>(mRawPtr->QueryFrame(Dest::kFrameIID)),
"fast and slow paths should give the same result");
return f;
}
if constexpr (std::is_base_of_v<nsIFrame, Source> &&
std::is_base_of_v<nsIFrame, Dest>) {
// For non-final frames we can still optimize the virtual call some of the
// time.
if (nsQueryFrame::FrameIID(mRawPtr->mClass) == Dest::kFrameIID) {
auto* f = static_cast<Dest*>(mRawPtr);
MOZ_ASSERT(
f == reinterpret_cast<Dest*>(mRawPtr->QueryFrame(Dest::kFrameIID)),
"fast and slow paths should give the same result");
return f;
}
}
return reinterpret_cast<Dest*>(mRawPtr->QueryFrame(Dest::kFrameIID));
}
private:
// For non-nsIFrame types there is no fast-path.
template <class Src, class Dst, typename = void, typename = void>
template <typename Dest, typename = void>
struct FastQueryFrame {
static Dst* QueryFrame(Src* aFrame) { return nullptr; }
static constexpr bool kSupported = false;
};
// Specialization for any nsIFrame type to any nsIFrame type -- if the source
// instance's mClass matches kFrameIID of the destination type then
// downcasting is safe.
template <class Src, class Dst>
struct FastQueryFrame<
Src, Dst, std::enable_if_t<std::is_base_of<nsIFrame, Src>::value>,
std::enable_if_t<std::is_base_of<nsIFrame, Dst>::value>> {
static Dst* QueryFrame(Src* aFrame) {
return nsQueryFrame::FrameIID(aFrame->mClass) == Dst::kFrameIID
? reinterpret_cast<Dst*>(aFrame)
// For final classes we can check the class id.
template <typename Dest>
struct FastQueryFrame<Dest, std::enable_if_t<std::is_final_v<Dest>, void>> {
static constexpr bool kSupported = true;
template <typename Src>
static Dest* QueryFrame(Src* aPtr) {
return nsQueryFrame::FrameIID(aPtr->mClass) == Dest::kFrameIID
? static_cast<Dest*>(aPtr)
: nullptr;
}
};
#define IMPL_FAST_QUERYFRAME(Dest_, Check_) \
template <> \
struct FastQueryFrame<Dest_, void> { \
static constexpr bool kSupported = true; \
template <typename Src> \
static Dest_* QueryFrame(Src* aPtr) { \
return aPtr->Check_() ? static_cast<Dest_*>(aPtr) : nullptr; \
} \
}
IMPL_FAST_QUERYFRAME(mozilla::ScrollContainerFrame,
IsScrollContainerOrSubclass);
#undef IMPL_FAST_QUERYFRAME
Source* mRawPtr;
};
template <class T>
template <typename T>
inline do_QueryFrameHelper<T> do_QueryFrame(T* s) {
return do_QueryFrameHelper<T>(s);
}

Просмотреть файл

@ -39,7 +39,7 @@ NS_IMPL_FRAMEARENA_HELPERS(SVGClipPathFrame)
void SVGClipPathFrame::ApplyClipPath(gfxContext& aContext,
nsIFrame* aClippedFrame,
const gfxMatrix& aMatrix) {
ISVGDisplayableFrame* singleClipPathChild = nullptr;
nsIFrame* singleClipPathChild = nullptr;
DebugOnly<bool> trivial = IsTrivial(&singleClipPathChild);
MOZ_ASSERT(trivial, "Caller needs to use GetClipMask");
@ -297,7 +297,7 @@ bool SVGClipPathFrame::PointIsInsideClipPath(nsIFrame* aClippedFrame,
return false;
}
bool SVGClipPathFrame::IsTrivial(ISVGDisplayableFrame** aSingleChild) {
bool SVGClipPathFrame::IsTrivial(nsIFrame** aSingleChild) {
// If the clip path is clipped then it's non-trivial
if (SVGObserverUtils::GetAndObserveClipPath(this, nullptr) ==
SVGObserverUtils::eHasRefsAllValid) {
@ -308,25 +308,25 @@ bool SVGClipPathFrame::IsTrivial(ISVGDisplayableFrame** aSingleChild) {
*aSingleChild = nullptr;
}
ISVGDisplayableFrame* foundChild = nullptr;
nsIFrame* foundChild = nullptr;
for (auto* kid : mFrames) {
ISVGDisplayableFrame* svgChild = do_QueryFrame(kid);
if (svgChild) {
// We consider a non-trivial clipPath to be one containing
// either more than one svg child and/or a svg container
if (foundChild || svgChild->IsDisplayContainer()) {
return false;
}
// or where the child is itself clipped
if (SVGObserverUtils::GetAndObserveClipPath(kid, nullptr) ==
SVGObserverUtils::eHasRefsAllValid) {
return false;
}
foundChild = svgChild;
if (!svgChild) {
continue;
}
// We consider a non-trivial clipPath to be one containing
// either more than one svg child and/or a svg container
if (foundChild || svgChild->IsDisplayContainer()) {
return false;
}
// or where the child is itself clipped
if (SVGObserverUtils::GetAndObserveClipPath(kid, nullptr) ==
SVGObserverUtils::eHasRefsAllValid) {
return false;
}
foundChild = kid;
}
if (aSingleChild) {
*aSingleChild = foundChild;

Просмотреть файл

@ -109,7 +109,7 @@ class SVGClipPathFrame final : public SVGContainerFrame {
// Check if this clipPath is made up of more than one geometry object.
// If so, the clipping API in cairo isn't enough and we need to use
// mask based clipping.
bool IsTrivial(ISVGDisplayableFrame** aSingleChild = nullptr);
bool IsTrivial(nsIFrame** aSingleChild = nullptr);
// nsIFrame interface:
nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,

Просмотреть файл

@ -408,7 +408,12 @@ static bool UsesCustomScrollbarMediator(nsIFrame* scrollbarBox) {
if (nsScrollbarFrame* scrollbarFrame = do_QueryFrame(scrollbarBox)) {
if (nsIScrollbarMediator* mediator =
scrollbarFrame->GetScrollbarMediator()) {
ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(mediator);
// Note we can't queryframe from nsIScrollbarMediator to
// ScrollContainerFrame directly due to an optimization in the queryframe
// implementation for ScrollContainerFrame.
nsIFrame* mediatorAsFrame = do_QueryFrame(mediator);
ScrollContainerFrame* scrollContainerFrame =
do_QueryFrame(mediatorAsFrame);
// The scrollbar mediator is not the scroll container frame.
// That means this scroll container frame has a custom scrollbar mediator.
if (!scrollContainerFrame) {