Bug 1351783 part 8 - Gather whether there are key listeners on the focused element. r?kats,smaug
This commit updates FocusTarget to collect whether there are key listeners
on the event target chain for the focused element. This is needed because we
cannot do async scrolling when this is the case.
MozReview-Commit-ID: FhSyF6ffZ4
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -1759,16 +1759,33 @@ EventListenerManager::TraceListeners(JST
mozilla::TraceScriptHolder(listener.mListener.GetWebIDLCallback(), aTrc);
}
// We might have eWrappedJSListener, but that is the legacy type for
// JS implemented event listeners, and trickier to handle here.
}
}
bool
+EventListenerManager::HasUntrustedOrNonSystemGroupKeyEventListeners()
+{
+ uint32_t count = mListeners.Length();
+ for (uint32_t i = 0; i < count; ++i) {
+ Listener* listener = &mListeners.ElementAt(i);
+ if (!listener->mFlags.mInSystemGroup &&
+ listener->mFlags.mAllowUntrustedEvents &&
+ (listener->mTypeAtom == nsGkAtoms::onkeydown ||
+ listener->mTypeAtom == nsGkAtoms::onkeypress ||
+ listener->mTypeAtom == nsGkAtoms::onkeyup)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
EventListenerManager::HasApzAwareListeners()
{
uint32_t count = mListeners.Length();
for (uint32_t i = 0; i < count; ++i) {
Listener* listener = &mListeners.ElementAt(i);
if (IsApzAwareListener(listener)) {
return true;
}
--- a/dom/events/EventListenerManager.h
+++ b/dom/events/EventListenerManager.h
@@ -463,16 +463,18 @@ public:
}
void MarkForCC();
void TraceListeners(JSTracer* aTrc);
dom::EventTarget* GetTarget() { return mTarget; }
+ bool HasUntrustedOrNonSystemGroupKeyEventListeners();
+
bool HasApzAwareListeners();
bool IsApzAwareListener(Listener* aListener);
bool IsApzAwareEvent(nsIAtom* aEvent);
protected:
void HandleEventInternal(nsPresContext* aPresContext,
WidgetEvent* aEvent,
nsIDOMEvent** aDOMEvent,
--- a/dom/events/EventTarget.cpp
+++ b/dom/events/EventTarget.cpp
@@ -53,16 +53,23 @@ EventTarget::SetEventHandler(const nsASt
void
EventTarget::SetEventHandler(nsIAtom* aType, const nsAString& aTypeString,
EventHandlerNonNull* aHandler)
{
GetOrCreateListenerManager()->SetEventHandler(aType, aTypeString, aHandler);
}
bool
+EventTarget::HasUntrustedOrNonSystemGroupKeyEventListeners() const
+{
+ EventListenerManager* elm = GetExistingListenerManager();
+ return elm && elm->HasUntrustedOrNonSystemGroupKeyEventListeners();
+}
+
+bool
EventTarget::IsApzAware() const
{
EventListenerManager* elm = GetExistingListenerManager();
return elm && elm->HasApzAwareListeners();
}
bool
EventTarget::DispatchEvent(Event& aEvent,
--- a/dom/events/EventTarget.h
+++ b/dom/events/EventTarget.h
@@ -94,16 +94,20 @@ public:
* Get the event listener manager, returning null if it does not already
* exist.
*/
virtual EventListenerManager* GetExistingListenerManager() const = 0;
// Called from AsyncEventDispatcher to notify it is running.
virtual void AsyncEventRunning(AsyncEventDispatcher* aEvent) {}
+ // Used by FocusTarget to determine whether this event target has listeners
+ // for untrusted or non system group key events.
+ bool HasUntrustedOrNonSystemGroupKeyEventListeners() const;
+
virtual bool IsApzAware() const;
protected:
EventHandlerNonNull* GetEventHandler(nsIAtom* aType,
const nsAString& aTypeString);
void SetEventHandler(nsIAtom* aType, const nsAString& aTypeString,
EventHandlerNonNull* aHandler);
};
--- a/gfx/layers/apz/src/FocusState.cpp
+++ b/gfx/layers/apz/src/FocusState.cpp
@@ -4,46 +4,51 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/layers/FocusState.h"
namespace mozilla {
namespace layers {
FocusState::FocusState()
- : mFocusLayersId(0)
+ : mFocusHasKeyEventListeners(false)
+ , mFocusLayersId(0)
, mFocusHorizontalTarget(FrameMetrics::NULL_SCROLL_ID)
, mFocusVerticalTarget(FrameMetrics::NULL_SCROLL_ID)
{
}
void
FocusState::Update(uint64_t aRootLayerTreeId,
uint64_t aOriginatingLayersId,
const FocusTarget& aState)
{
// Update the focus tree with the latest target
mFocusTree[aOriginatingLayersId] = aState;
// Reset our internal state so we can recalculate it
+ mFocusHasKeyEventListeners = false;
mFocusLayersId = aRootLayerTreeId;
mFocusHorizontalTarget = FrameMetrics::NULL_SCROLL_ID;
mFocusVerticalTarget = FrameMetrics::NULL_SCROLL_ID;
// To update the focus state for the entire APZCTreeManager, we need
// to traverse the focus tree to find the current leaf which is the global
// focus target we can use for async keyboard scrolling
while (true) {
auto currentNode = mFocusTree.find(mFocusLayersId);
if (currentNode == mFocusTree.end()) {
return;
}
const FocusTarget& target = currentNode->second;
+ // Accumulate event listener flags on the path to the focus target
+ mFocusHasKeyEventListeners |= target.mFocusHasKeyEventListeners;
+
switch (target.mType) {
case FocusTarget::eRefLayer: {
// Guard against infinite loops
MOZ_ASSERT(mFocusLayersId != target.mData.mRefLayerId);
if (mFocusLayersId == target.mData.mRefLayerId) {
return;
}
--- a/gfx/layers/apz/src/FocusState.h
+++ b/gfx/layers/apz/src/FocusState.h
@@ -91,16 +91,20 @@ public:
* Removes a focus target by its layer tree ID.
*/
void RemoveFocusTarget(uint64_t aLayersId);
private:
// The set of focus targets received indexed by their layer tree ID
std::unordered_map<uint64_t, FocusTarget> mFocusTree;
+ // A flag whether there is a key listener on the event target chain for the
+ // focused element
+ bool mFocusHasKeyEventListeners;
+
// The layer tree ID which contains the scrollable frame of the focused element
uint64_t mFocusLayersId;
// The scrollable layer corresponding to the scrollable frame that is used to
// scroll the focused element. This depends on the direction the user is
// scrolling.
FrameMetrics::ViewID mFocusHorizontalTarget;
FrameMetrics::ViewID mFocusVerticalTarget;
};
--- a/gfx/layers/apz/src/FocusTarget.cpp
+++ b/gfx/layers/apz/src/FocusTarget.cpp
@@ -1,16 +1,18 @@
/* -*- Mode: C++; tab-width: 8; 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/. */
#include "mozilla/layers/FocusTarget.h"
-#include "mozilla/dom/TabParent.h" // for TabParent
+#include "mozilla/dom/EventTarget.h" // for EventTarget
+#include "mozilla/dom/TabParent.h" // for TabParent
+#include "mozilla/EventDispatcher.h" // for EventDispatcher
#include "mozilla/layout/RenderFrameParent.h" // For RenderFrameParent
#include "nsIPresShell.h" // for nsIPresShell
#include "nsLayoutUtils.h" // for nsLayoutUtils
using namespace mozilla::dom;
using namespace mozilla::layout;
namespace mozilla {
@@ -33,32 +35,59 @@ GetRetargetEventPresShell(nsIPresShell*
if (!retargetEventDoc) {
return nullptr;
}
nsCOMPtr<nsIPresShell> presShell = retargetEventDoc->GetShell();
return presShell.forget();
}
-FocusTarget::FocusTarget() : mType(FocusTarget::eNone)
+static bool
+HasListenersForKeyEvents(nsIContent* aContent)
+{
+ if (!aContent) {
+ return false;
+ }
+
+ WidgetEvent event(true, eVoidEvent);
+ nsTArray<EventTarget*> targets;
+ nsresult rv = EventDispatcher::Dispatch(aContent, nullptr, &event, nullptr,
+ nullptr, nullptr, &targets);
+ NS_ENSURE_SUCCESS(rv, false);
+ for (size_t i = 0; i < targets.Length(); i++) {
+ if (targets[i]->HasUntrustedOrNonSystemGroupKeyEventListeners()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+FocusTarget::FocusTarget()
+ : mFocusHasKeyEventListeners(false)
+ , mType(FocusTarget::eNone)
{
}
FocusTarget::FocusTarget(nsIPresShell* aRootPresShell)
+ : mFocusHasKeyEventListeners(false)
{
MOZ_ASSERT(aRootPresShell);
MOZ_ASSERT(NS_IsMainThread());
// Key events can be retargeted to a child PresShell when there is an iframe
nsCOMPtr<nsIPresShell> presShell = GetRetargetEventPresShell(aRootPresShell);
// Get the content that should be scrolled for this PresShell, which is
// the current focused element or the current DOM selection
nsCOMPtr<nsIContent> scrollTarget = presShell->GetContentForScrolling();
+ // Collect event listener information so we can track what is potentially focus
+ // changing
+ mFocusHasKeyEventListeners = HasListenersForKeyEvents(scrollTarget);
+
// Check if the scroll target is a remote browser
if (TabParent* browserParent = TabParent::GetFrom(scrollTarget)) {
RenderFrameParent* rfp = browserParent->GetRenderFrame();
// The globally focused element for scrolling is in a remote layer tree
if (rfp) {
mType = FocusTarget::eRefLayer;
mData.mRefLayerId = rfp->GetLayersId();
--- a/gfx/layers/apz/src/FocusTarget.h
+++ b/gfx/layers/apz/src/FocusTarget.h
@@ -48,16 +48,20 @@ public:
FocusTarget();
/**
* Construct a focus target for the specified top level PresShell
*/
FocusTarget(nsIPresShell* aRootPresShell);
public:
+ // Whether there are keydown, keypress, or keyup event listeners
+ // in the event target chain of the focused element
+ bool mFocusHasKeyEventListeners;
+
FocusTargetType mType;
FocusTargetData mData;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_layers_FocusTarget_h