Bug 1376551 - Don't lose APZ focus state when the mouse moves and there are no listeners. r?kats
MozReview-Commit-ID: 8hjoFBcOHuZ
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1000,16 +1000,21 @@ APZCTreeManager::ReceiveInputEvent(Input
result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId);
break;
} case MOUSE_INPUT: {
MouseInput& mouseInput = aEvent.AsMouseInput();
mouseInput.mHandledByAPZ = true;
mCurrentMousePosition = mouseInput.mOrigin;
+ if (mouseInput.mType == MouseInput::MOUSE_MOVE &&
+ mFocusState.CanIgnoreMouseMoveEvents()) {
+ focusSetter.MarkAsNonFocusChanging();
+ }
+
bool startsDrag = DragTracker::StartsDrag(mouseInput);
if (startsDrag) {
// If this is the start of a drag we need to unambiguously know if it's
// going to land on a scrollbar or not. We can't apply an untransform
// here without knowing that, so we need to ensure the untransform is
// a no-op.
FlushRepaintsToClearScreenToGeckoTransform();
}
--- a/gfx/layers/apz/src/FocusState.cpp
+++ b/gfx/layers/apz/src/FocusState.cpp
@@ -10,16 +10,17 @@
namespace mozilla {
namespace layers {
FocusState::FocusState()
: mLastAPZProcessedEvent(1)
, mLastContentProcessedEvent(0)
, mFocusHasKeyEventListeners(false)
+ , mHasPointerMovementEventListeners(false)
, mFocusLayersId(0)
, mFocusHorizontalTarget(FrameMetrics::NULL_SCROLL_ID)
, mFocusVerticalTarget(FrameMetrics::NULL_SCROLL_ID)
{
}
bool
FocusState::IsCurrent() const
@@ -49,20 +50,28 @@ FocusState::Update(uint64_t aRootLayerTr
static_cast<int>(aState.mType),
aState.mSequenceNumber);
// Update the focus tree with the latest target
mFocusTree[aOriginatingLayersId] = aState;
// Reset our internal state so we can recalculate it
mFocusHasKeyEventListeners = false;
+ mHasPointerMovementEventListeners = false;
mFocusLayersId = aRootLayerTreeId;
mFocusHorizontalTarget = FrameMetrics::NULL_SCROLL_ID;
mFocusVerticalTarget = FrameMetrics::NULL_SCROLL_ID;
+ // Accumulate whether there are listeners triggered for mouse movement
+ // anywhere in the document
+ for (const auto& currentNode : mFocusTree) {
+ const FocusTarget& target = currentNode.second;
+ mHasPointerMovementEventListeners |= target.mHasPointerMovementEventListeners;
+ }
+
// 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()) {
FS_LOG("Setting target to nil (cannot find lt=%" PRIu64 ")\n",
mFocusLayersId);
--- a/gfx/layers/apz/src/FocusState.h
+++ b/gfx/layers/apz/src/FocusState.h
@@ -135,16 +135,25 @@ public:
* Gets whether it is safe to not increment the focus sequence number for an
* unmatched keyboard event.
*/
bool CanIgnoreKeyboardShortcutMisses() const
{
return IsCurrent() && !mFocusHasKeyEventListeners;
}
+ /**
+ * Gets whether it is safe to not increment the focus sequence number for
+ * mousemove inputs.
+ */
+ bool CanIgnoreMouseMoveEvents() const
+ {
+ return IsCurrent() && !mHasPointerMovementEventListeners;
+ }
+
private:
// The set of focus targets received indexed by their layer tree ID
std::unordered_map<uint64_t, FocusTarget> mFocusTree;
// The focus sequence number of the last potentially focus changing event
// processed by APZ. This number starts at one and increases monotonically.
// We don't worry about wrap around here because at a pace of 100 increments/sec,
// it would take 5.85*10^9 years before we would wrap around. This number will
@@ -152,16 +161,19 @@ private:
// on input events.
uint64_t mLastAPZProcessedEvent;
// The focus sequence number last received in a focus update.
uint64_t mLastContentProcessedEvent;
// A flag whether there is a key listener on the event target chain for the
// focused element
bool mFocusHasKeyEventListeners;
+ // A flag whether there event listeners that are triggered by pointer
+ // movement.
+ bool mHasPointerMovementEventListeners;
// 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
@@ -75,24 +75,26 @@ static bool
IsEditableNode(nsINode* aNode)
{
return aNode && aNode->IsEditable();
}
FocusTarget::FocusTarget()
: mSequenceNumber(0)
, mFocusHasKeyEventListeners(false)
+ , mHasPointerMovementEventListeners(false)
, mType(FocusTarget::eNone)
{
}
FocusTarget::FocusTarget(nsIPresShell* aRootPresShell,
uint64_t aFocusSequenceNumber)
: mSequenceNumber(aFocusSequenceNumber)
, mFocusHasKeyEventListeners(false)
+ , mHasPointerMovementEventListeners(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);
if (!presShell) {
@@ -118,61 +120,71 @@ FocusTarget::FocusTarget(nsIPresShell* a
nsCOMPtr<nsIContent> focusedContent = presShell->GetFocusedContentInOurWindow();
// Check if there are key event listeners that could prevent default or change
// the focus or selection of the page.
mFocusHasKeyEventListeners =
HasListenersForKeyEvents(focusedContent ? focusedContent.get()
: document->GetUnfocusedKeyEventTarget());
- // Check if the focused element is content editable or if the document
+ // Check if there are mouse movement event listeners that could change the
+ // focus or selection of the page.
+ if (nsCOMPtr<nsPIDOMWindowInner> window = document->GetInnerWindow()) {
+ mHasPointerMovementEventListeners = window->HasPointerMovementEventListeners();
+ }
+
+ // Check if the key event target is content editable or if the document
// is in design mode.
if (IsEditableNode(focusedContent) ||
IsEditableNode(document)) {
- FT_LOG("Creating nil target with seq=%" PRIu64 ", kl=%d (disabling for editable node)\n",
+ FT_LOG("Creating nil target with seq=%" PRIu64 ", kl=%d, pl=%d (disabling for editable node)\n",
aFocusSequenceNumber,
- static_cast<int>(mFocusHasKeyEventListeners));
+ mFocusHasKeyEventListeners,
+ mHasPointerMovementEventListeners);
mType = FocusTarget::eNone;
return;
}
// Check if the focused element is a remote browser
if (TabParent* browserParent = TabParent::GetFrom(focusedContent)) {
RenderFrameParent* rfp = browserParent->GetRenderFrame();
// The globally focused element for scrolling is in a remote layer tree
if (rfp) {
- FT_LOG("Creating reflayer target with seq=%" PRIu64 ", kl=%d, lt=%" PRIu64 "\n",
+ FT_LOG("Creating reflayer target with seq=%" PRIu64 ", kl=%d, pl=%d, lt=%" PRIu64 "\n",
aFocusSequenceNumber,
mFocusHasKeyEventListeners,
+ mHasPointerMovementEventListeners,
rfp->GetLayersId());
mType = FocusTarget::eRefLayer;
mData.mRefLayerId = rfp->GetLayersId();
return;
}
- FT_LOG("Creating nil target with seq=%" PRIu64 ", kl=%d (remote browser missing layers id)\n",
+ FT_LOG("Creating nil target with seq=%" PRIu64 ", kl=%d, pl=%d (remote browser missing layers id)\n",
aFocusSequenceNumber,
- mFocusHasKeyEventListeners);
+ mFocusHasKeyEventListeners,
+ mHasPointerMovementEventListeners);
mType = FocusTarget::eNone;
return;
}
// The content to scroll is either the focused element or the focus node of
// the selection. It's difficult to determine if an element is an interactive
// element requiring async keyboard scrolling to be disabled. So we only
// allow async key scrolling based on the selection, which doesn't have
// this problem and is more common.
if (focusedContent) {
- FT_LOG("Creating nil target with seq=%" PRIu64 ", kl=%d (disabling for focusing an element)\n",
+ FT_LOG("Creating nil target with seq=%" PRIu64 ", kl=%d, pl=%d (disabling for focusing an element)\n",
aFocusSequenceNumber,
- mFocusHasKeyEventListeners);
+ mFocusHasKeyEventListeners,
+ mHasPointerMovementEventListeners);
mType = FocusTarget::eNone;
return;
}
nsCOMPtr<nsIContent> selectedContent = presShell->GetSelectedContentForScrolling();
// Gather the scrollable frames that would be scrolled in each direction
@@ -187,28 +199,30 @@ FocusTarget::FocusTarget(nsIPresShell* a
// We might have the globally focused element for scrolling. Gather a ViewID for
// the horizontal and vertical scroll targets of this element.
mType = FocusTarget::eScrollLayer;
mData.mScrollTargets.mHorizontal =
nsLayoutUtils::FindIDForScrollableFrame(horizontal);
mData.mScrollTargets.mVertical =
nsLayoutUtils::FindIDForScrollableFrame(vertical);
- FT_LOG("Creating scroll target with seq=%" PRIu64 ", kl=%d, h=%" PRIu64 ", v=%" PRIu64 "\n",
+ FT_LOG("Creating scroll target with seq=%" PRIu64 ", kl=%d, pl=%d, h=%" PRIu64 ", v=%" PRIu64 "\n",
aFocusSequenceNumber,
mFocusHasKeyEventListeners,
+ mHasPointerMovementEventListeners,
mData.mScrollTargets.mHorizontal,
mData.mScrollTargets.mVertical);
}
bool
FocusTarget::operator==(const FocusTarget& aRhs) const
{
if (mSequenceNumber != aRhs.mSequenceNumber ||
mFocusHasKeyEventListeners != aRhs.mFocusHasKeyEventListeners ||
+ mHasPointerMovementEventListeners != aRhs.mHasPointerMovementEventListeners ||
mType != aRhs.mType) {
return false;
}
if (mType == FocusTarget::eRefLayer) {
return mData.mRefLayerId == aRhs.mData.mRefLayerId;
} else if (mType == FocusTarget::eScrollLayer) {
return mData.mScrollTargets.mHorizontal == aRhs.mData.mScrollTargets.mHorizontal &&
--- a/gfx/layers/apz/src/FocusTarget.h
+++ b/gfx/layers/apz/src/FocusTarget.h
@@ -56,16 +56,18 @@ public:
public:
// The content sequence number recorded at the time of this class's creation
uint64_t mSequenceNumber;
// Whether there are keydown, keypress, or keyup event listeners
// in the event target chain of the focused element
bool mFocusHasKeyEventListeners;
+ // Whether there are event listeners that are triggered by pointer movement
+ bool mHasPointerMovementEventListeners;
FocusTargetType mType;
FocusTargetData mData;
};
} // namespace layers
} // namespace mozilla
--- a/gfx/layers/ipc/LayersMessageUtils.h
+++ b/gfx/layers/ipc/LayersMessageUtils.h
@@ -451,28 +451,30 @@ template <>
struct ParamTraits<mozilla::layers::FocusTarget>
{
typedef mozilla::layers::FocusTarget paramType;
static void Write(Message* aMsg, const paramType& aParam)
{
WriteParam(aMsg, aParam.mSequenceNumber);
WriteParam(aMsg, aParam.mFocusHasKeyEventListeners);
+ WriteParam(aMsg, aParam.mHasPointerMovementEventListeners);
WriteParam(aMsg, aParam.mType);
if (aParam.mType == mozilla::layers::FocusTarget::eRefLayer) {
WriteParam(aMsg, aParam.mData.mRefLayerId);
} else if (aParam.mType == mozilla::layers::FocusTarget::eScrollLayer) {
WriteParam(aMsg, aParam.mData.mScrollTargets);
}
}
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
{
if (!ReadParam(aMsg, aIter, &aResult->mSequenceNumber) ||
!ReadParam(aMsg, aIter, &aResult->mFocusHasKeyEventListeners) ||
+ !ReadParam(aMsg, aIter, &aResult->mHasPointerMovementEventListeners) ||
!ReadParam(aMsg, aIter, &aResult->mType)) {
return false;
}
if (aResult->mType == mozilla::layers::FocusTarget::eRefLayer) {
return ReadParam(aMsg, aIter, &aResult->mData.mRefLayerId);
} else if (aResult->mType == mozilla::layers::FocusTarget::eScrollLayer) {
return ReadParam(aMsg, aIter, &aResult->mData.mScrollTargets);