--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -5285,17 +5285,17 @@ EventStateManager::DoContentCommandScrol
break;
default:
return NS_ERROR_INVALID_ARG;
}
aEvent->mSucceeded = true;
nsIScrollableFrame* sf =
- ps->GetFrameToScrollAsScrollable(nsIPresShell::eEither);
+ ps->GetScrollableFrameToScroll(nsIPresShell::eEither);
aEvent->mIsEnabled = sf ?
(aEvent->mScroll.mIsHorizontal ?
WheelHandlingUtils::CanScrollOn(sf, aEvent->mScroll.mAmount, 0) :
WheelHandlingUtils::CanScrollOn(sf, 0, aEvent->mScroll.mAmount)) : false;
if (!aEvent->mIsEnabled || aEvent->mOnlyEnabledCheck) {
return NS_OK;
}
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1,14 +1,15 @@
/* -*- 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 <stack>
+#include <unordered_set>
#include "APZCTreeManager.h"
#include "AsyncPanZoomController.h"
#include "Compositor.h" // for Compositor
#include "DragTracker.h" // for DragTracker
#include "gfxPrefs.h" // for gfxPrefs
#include "HitTestingTreeNode.h" // for HitTestingTreeNode
#include "InputBlockState.h" // for InputBlockState
#include "InputData.h" // for InputData, etc
@@ -16,16 +17,17 @@
#include "mozilla/dom/Touch.h" // for Touch
#include "mozilla/gfx/GPUParent.h" // for GPUParent
#include "mozilla/gfx/Logging.h" // for gfx::TreeLog
#include "mozilla/gfx/Point.h" // for Point
#include "mozilla/layers/APZThreadUtils.h" // for AssertOnCompositorThread, etc
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
#include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
+#include "mozilla/layers/FocusState.h" // for FocusState
#include "mozilla/layers/LayerMetricsWrapper.h"
#include "mozilla/layers/WebRenderScrollDataWrapper.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/mozalloc.h" // for operator new
#include "mozilla/TouchEvents.h"
#include "mozilla/Preferences.h" // for Preferences
#include "mozilla/EventStateManager.h" // for WheelPrefs
#include "mozilla/webrender/WebRenderAPI.h"
@@ -77,16 +79,21 @@ struct APZCTreeManager::TreeBuildingStat
const APZPaintLogHelper mPaintLogger;
// State that is updated as we perform the tree build
// A list of nodes that need to be destroyed at the end of the tree building.
// This is initialized with all nodes in the old tree, and nodes are removed
// from it as we reuse them in the new tree.
nsTArray<RefPtr<HitTestingTreeNode>> mNodesToDestroy;
+ // A set of layer trees that are no longer in the hit testing tree. This is
+ // used to destroy unneeded focus targets at the end of tree building. This
+ // is needed in addition to mNodesToDestroy because a hit testing node for a
+ // layer tree can be removed without the whole layer tree being removed.
+ std::unordered_set<uint64_t> mLayersIdsToDestroy;
// This map is populated as we place APZCs into the new tree. Its purpose is
// to facilitate re-using the same APZC for different layers that scroll
// together (and thus have the same ScrollableLayerGuid).
std::unordered_map<ScrollableLayerGuid, AsyncPanZoomController*, ScrollableLayerGuidHash> mApzcMap;
};
class APZCTreeManager::CheckerboardFlushObserver : public nsIObserver {
@@ -257,16 +264,17 @@ APZCTreeManager::UpdateHitTestingTreeImp
// we are sure that the layer was removed and not just transplanted elsewhere. Doing that
// as part of a recursive tree walk is hard and so maintaining a list and removing
// APZCs that are still alive is much simpler.
ForEachNode<ReverseIterator>(mRootNode.get(),
[&state] (HitTestingTreeNode* aNode)
{
state.mNodesToDestroy.AppendElement(aNode);
});
+ state.mLayersIdsToDestroy = mFocusState.GetFocusTargetLayerIds();
mRootNode = nullptr;
if (aRoot) {
std::stack<gfx::TreeAutoIndent> indents;
std::stack<gfx::Matrix4x4> ancestorTransforms;
HitTestingTreeNode* parent = nullptr;
HitTestingTreeNode* next = nullptr;
uint64_t layersId = aRootLayerTreeId;
@@ -284,16 +292,19 @@ APZCTreeManager::UpdateHitTestingTreeImp
aLayerMetrics.Metrics(), layersId, ancestorTransforms.top(),
parent, next, state);
MOZ_ASSERT(node);
AsyncPanZoomController* apzc = node->GetApzc();
aLayerMetrics.SetApzc(apzc);
mApzcTreeLog << '\n';
+ // Mark that this layer tree is being used
+ state.mLayersIdsToDestroy.erase(layersId);
+
// Accumulate the CSS transform between layers that have an APZC.
// In the terminology of the big comment above APZCTreeManager::GetScreenToApzcTransform, if
// we are at layer M, then aAncestorTransform is NC * OC * PC, and we left-multiply MC and
// compute ancestorTransform to be MC * NC * OC * PC. This gets passed down as the ancestor
// transform to layer L when we recurse into the children below. If we are at a layer
// with an APZC, such as P, then we reset the ancestorTransform to just PC, to start
// the new accumulation as we go down.
// If a transform is a perspective transform, it's ignored for this purpose
@@ -329,24 +340,39 @@ APZCTreeManager::UpdateHitTestingTreeImp
for (size_t i = 0; i < state.mNodesToDestroy.Length(); i++) {
APZCTM_LOG("Destroying node at %p with APZC %p\n",
state.mNodesToDestroy[i].get(),
state.mNodesToDestroy[i]->GetApzc());
state.mNodesToDestroy[i]->Destroy();
}
+ // Clear out any focus targets that are no longer needed
+ for (auto layersId : state.mLayersIdsToDestroy) {
+ mFocusState.RemoveFocusTarget(layersId);
+ }
+
#if ENABLE_APZCTM_LOGGING
// Make the hit-test tree line up with the layer dump
printf_stderr("APZCTreeManager (%p)\n", this);
mRootNode->Dump(" ");
#endif
}
void
+APZCTreeManager::UpdateFocusState(uint64_t aRootLayerTreeId,
+ uint64_t aOriginatingLayersId,
+ const FocusTarget& aFocusTarget)
+{
+ mFocusState.Update(aRootLayerTreeId,
+ aOriginatingLayersId,
+ aFocusTarget);
+}
+
+void
APZCTreeManager::UpdateHitTestingTree(uint64_t aRootLayerTreeId,
Layer* aRoot,
bool aIsFirstPaint,
uint64_t aOriginatingLayersId,
uint32_t aPaintSequenceNumber)
{
LayerMetricsWrapper root(aRoot);
UpdateHitTestingTreeImpl(aRootLayerTreeId, root, aIsFirstPaint,
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -10,16 +10,17 @@
#include "gfxPoint.h" // for gfxPoint
#include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2
#include "mozilla/gfx/Logging.h" // for gfx::TreeLog
#include "mozilla/gfx/Matrix.h" // for Matrix4x4
#include "mozilla/layers/TouchCounter.h"// for TouchCounter
#include "mozilla/layers/IAPZCTreeManager.h" // for IAPZCTreeManager
#include "mozilla/layers/Keyboard.h" // for KeyboardMap
+#include "mozilla/layers/FocusState.h" // for FocusState
#include "mozilla/Mutex.h" // for Mutex
#include "mozilla/RefPtr.h" // for RefPtr
#include "mozilla/TimeStamp.h" // for mozilla::TimeStamp
#include "nsCOMPtr.h" // for already_AddRefed
#if defined(MOZ_WIDGET_ANDROID)
#include "mozilla/layers/AndroidDynamicToolbarAnimator.h"
#endif // defined(MOZ_WIDGET_ANDROID)
@@ -36,16 +37,17 @@ class WebRenderAPI;
namespace layers {
class Layer;
class AsyncPanZoomController;
class APZCTreeManagerParent;
class CompositorBridgeParent;
class OverscrollHandoffChain;
struct OverscrollHandoffState;
+class FocusTarget;
struct FlingHandoffState;
struct ScrollableLayerGuidHash;
class LayerMetricsWrapper;
class InputQueue;
class GeckoContentController;
class HitTestingTreeNode;
class WebRenderScrollData;
@@ -108,16 +110,29 @@ public:
* Initializes the global state used in AsyncPanZoomController.
* This is normally called when it is first needed in the constructor
* of APZCTreeManager, but can be called manually to force it to be
* initialized earlier.
*/
static void InitializeGlobalState();
/**
+ * Rebuild the focus state based on the focus target from the layer tree update
+ * that just occurred.
+ *
+ * @param aRootLayerTreeId The layer tree ID of the root layer corresponding
+ * to this APZCTreeManager
+ * @param aOriginatingLayersId The layer tree ID of the layer corresponding to
+ * this layer tree update.
+ */
+ void UpdateFocusState(uint64_t aRootLayerTreeId,
+ uint64_t aOriginatingLayersId,
+ const FocusTarget& aFocusTarget);
+
+ /**
* Rebuild the hit-testing tree based on the layer update that just came up.
* Preserve nodes and APZC instances where possible, but retire those whose
* layers are no longer in the layer tree.
*
* This must be called on the compositor thread as it walks the layer tree.
*
* @param aRootLayerTreeId The layer tree ID of the root layer corresponding
* to this APZCTreeManager
@@ -555,16 +570,20 @@ private:
RefPtr<HitTestingTreeNode> mRootNode;
/* Holds the zoom constraints for scrollable layers, as determined by the
* the main-thread gecko code. */
std::unordered_map<ScrollableLayerGuid, ZoomConstraints, ScrollableLayerGuidHash> mZoomConstraints;
/* A list of keyboard shortcuts to use for translating keyboard inputs into
* keyboard actions. This is gathered on the main thread from XBL bindings.
*/
KeyboardMap mKeyboardMap;
+ /* This tracks the focus targets of chrome and content and whether we have
+ * a current focus target or whether we are waiting for a new confirmation.
+ */
+ FocusState mFocusState;
/* This tracks the APZC that should receive all inputs for the current input event block.
* This allows touch points to move outside the thing they started on, but still have the
* touch events delivered to the same initial APZC. This will only ever be touched on the
* input delivery thread, and so does not require locking.
*/
RefPtr<AsyncPanZoomController> mApzcForInputBlock;
/* The hit result for the current input event block; this should always be in
* sync with mApzcForInputBlock.
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/FocusState.cpp
@@ -0,0 +1,90 @@
+/* -*- 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/FocusState.h"
+
+namespace mozilla {
+namespace layers {
+
+FocusState::FocusState()
+ : 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
+ 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;
+
+ switch (target.mType) {
+ case FocusTarget::eRefLayer: {
+ // Guard against infinite loops
+ MOZ_ASSERT(mFocusLayersId != target.mData.mRefLayerId);
+ if (mFocusLayersId == target.mData.mRefLayerId) {
+ return;
+ }
+
+ // The focus target is in a child layer tree
+ mFocusLayersId = target.mData.mRefLayerId;
+ break;
+ }
+ case FocusTarget::eScrollLayer: {
+ // This is the global focus target
+ mFocusHorizontalTarget = target.mData.mScrollTargets.mHorizontal;
+ mFocusVerticalTarget = target.mData.mScrollTargets.mVertical;
+ return;
+ }
+ case FocusTarget::eNone: {
+ return;
+ }
+ case FocusTarget::eSentinel: {
+ MOZ_ASSERT_UNREACHABLE("Invalid FocusTargetType");
+ }
+ }
+ }
+}
+
+std::unordered_set<uint64_t>
+FocusState::GetFocusTargetLayerIds() const
+{
+ std::unordered_set<uint64_t> layersIds;
+ layersIds.reserve(mFocusTree.size());
+
+ for (auto focusNode : mFocusTree) {
+ layersIds.insert(focusNode.first);
+ }
+
+ return layersIds;
+}
+
+void
+FocusState::RemoveFocusTarget(uint64_t aLayersId)
+{
+ mFocusTree.erase(aLayersId);
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/FocusState.h
@@ -0,0 +1,111 @@
+/* -*- 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/. */
+
+#ifndef mozilla_layers_FocusState_h
+#define mozilla_layers_FocusState_h
+
+#include <unordered_map> // for std::unordered_map
+#include <unordered_set> // for std::unordered_set
+
+#include "FrameMetrics.h" // for FrameMetrics::ViewID
+#include "mozilla/layers/FocusTarget.h" // for FocusTarget
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * This class is used for tracking chrome and content focus targets and calculating
+ * global focus information from them for use by APZCTreeManager for async keyboard
+ * scrolling.
+ *
+ * # Calculating the element to scroll
+ *
+ * Chrome and content processes have independently focused elements. This makes it
+ * difficult to calculate the global focused element and its scrollable frame from
+ * the chrome or content side. So instead we send the local focus information from
+ * each process to here and then calculate the global focus information. This
+ * local information resides in a `focus target`.
+ *
+ * A focus target indicates that either:
+ * 1. The focused element is a remote browser along with its layer tree ID
+ * 2. The focused element is not scrollable
+ * 3. The focused element is scrollable along with the ViewID's of its
+ scrollable layers
+ *
+ * Using this information we can determine the global focus information by
+ * starting at the focus target of the root layer tree ID and following remote
+ * browsers until we reach a scrollable or non-scrollable focus target.
+ *
+ * # Determinism and sequence numbers
+ *
+ * The focused element in content can be changed within any javascript code. And
+ * javascript can run in response to an event or at any moment from `setTimeout`
+ * and others. This makes it impossible to always have the current focus
+ * information in APZ as it can be changed asynchronously at any moment. If we
+ * don't have the latest focus information, we may incorrectly scroll a target
+ * when we shouldn't.
+ *
+ * A tradeoff is designed here whereby we will maintain deterministic focus
+ * changes for user input, but not for other javascript code. The reasoning
+ * here is that `setTimeout` and others are already non-deterministic and so it
+ * might not be as breaking to web content.
+ *
+ * To maintain deterministic focus changes for a given stream of user inputs, we
+ * invalidate our focus state whenever we receive a user input that may trigger
+ * event listeners. We then attach a new sequence number to these events and
+ * dispatch them to content. Content will then include the latest sequence number
+ * it has processed to every focus update. Using this we can determine whether
+ * any potentially focus changing events have yet to be handled by content.
+ *
+ * Once we have received the latest focus sequence number from content, we know
+ * that all event listeners triggered by user inputs, and their resulting focus
+ * changes, have been processed and so we have a current target that we can use
+ * again.
+ */
+class FocusState final
+{
+public:
+ FocusState();
+
+ /**
+ * Update the internal focus tree and recalculate the global focus target for
+ * a focus target update received from chrome or content.
+ *
+ * @param aRootLayerTreeId the layer tree ID of the root layer for the
+ parent APZCTreeManager
+ * @param aOriginatingLayersId the layer tree ID that this focus target
+ belongs to
+ */
+ void Update(uint64_t aRootLayerTreeId,
+ uint64_t aOriginatingLayersId,
+ const FocusTarget& aTarget);
+
+ /**
+ * Collects a set of the layer tree IDs that we have a focus target for.
+ */
+ std::unordered_set<uint64_t> GetFocusTargetLayerIds() const;
+
+ /**
+ * 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;
+
+ // 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;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_FocusState_h
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/FocusTarget.cpp
@@ -0,0 +1,91 @@
+/* -*- 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/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 {
+namespace layers {
+
+static already_AddRefed<nsIPresShell>
+GetRetargetEventPresShell(nsIPresShell* aRootPresShell)
+{
+ MOZ_ASSERT(aRootPresShell);
+
+ // Use the last focused window in this PresShell and its
+ // associated PresShell
+ nsCOMPtr<nsPIDOMWindowOuter> window =
+ aRootPresShell->GetFocusedDOMWindowInOurWindow();
+ if (!window) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIDocument> retargetEventDoc = window->GetExtantDoc();
+ if (!retargetEventDoc) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIPresShell> presShell = retargetEventDoc->GetShell();
+ return presShell.forget();
+}
+
+FocusTarget::FocusTarget() : mType(FocusTarget::eNone)
+{
+}
+
+FocusTarget::FocusTarget(nsIPresShell* aRootPresShell)
+{
+ 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();
+
+ // 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();
+ return;
+ }
+
+ mType = FocusTarget::eNone;
+ return;
+ }
+
+ // Gather the scrollable frames that would be scrolled in each direction
+ // for this scroll target
+ nsIScrollableFrame* horizontal =
+ presShell->GetScrollableFrameToScrollForContent(scrollTarget.get(),
+ nsIPresShell::eHorizontal);
+ nsIScrollableFrame* vertical =
+ presShell->GetScrollableFrameToScrollForContent(scrollTarget.get(),
+ nsIPresShell::eVertical);
+
+ // 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);
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/FocusTarget.h
@@ -0,0 +1,63 @@
+/* -*- 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/. */
+
+#ifndef mozilla_layers_FocusTarget_h
+#define mozilla_layers_FocusTarget_h
+
+#include <stdint.h> // for int32_t, uint32_t
+
+#include "FrameMetrics.h" // for FrameMetrics::ViewID
+
+class nsIPresShell;
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * This class is used for communicating information about the currently focused
+ * element of a document and the scrollable frames to use when keyboard scrolling
+ * it. It is created on the main thread at paint-time, but is then passed over
+ * IPC to the compositor/APZ code.
+ */
+class FocusTarget final
+{
+public:
+ struct ScrollTargets
+ {
+ FrameMetrics::ViewID mHorizontal;
+ FrameMetrics::ViewID mVertical;
+ };
+
+ enum FocusTargetType
+ {
+ eNone,
+ eRefLayer,
+ eScrollLayer,
+
+ // Used as an upper bound for ContiguousEnumSerializer
+ eSentinel,
+ };
+ union FocusTargetData
+ {
+ uint64_t mRefLayerId;
+ ScrollTargets mScrollTargets;
+ };
+
+ FocusTarget();
+
+ /**
+ * Construct a focus target for the specified top level PresShell
+ */
+ FocusTarget(nsIPresShell* aRootPresShell);
+
+public:
+ FocusTargetType mType;
+ FocusTargetData mData;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_FocusTarget_h
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -95,16 +95,18 @@ EXPORTS.mozilla.layers += [
'apz/public/IAPZCTreeManager.h',
'apz/public/MetricsSharingController.h',
# exporting things from apz/src is temporary until we extract a
# proper interface for the code there
'apz/src/APZCTreeManager.h',
'apz/src/APZUtils.h',
'apz/src/AsyncDragMetrics.h',
'apz/src/AsyncPanZoomAnimation.h',
+ 'apz/src/FocusState.h',
+ 'apz/src/FocusTarget.h',
'apz/src/Keyboard.h',
'apz/src/TouchCounter.h',
'apz/testutil/APZTestData.h',
'apz/util/ActiveElementManager.h',
'apz/util/APZCCallbackHelper.h',
'apz/util/APZEventState.h',
'apz/util/APZThreadUtils.h',
'apz/util/ChromeProcessController.h',
@@ -273,16 +275,18 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'andr
UNIFIED_SOURCES += [
'AnimationHelper.cpp',
'apz/public/IAPZCTreeManager.cpp',
'apz/src/APZCTreeManager.cpp',
'apz/src/AsyncPanZoomController.cpp',
'apz/src/Axis.cpp',
'apz/src/CheckerboardEvent.cpp',
'apz/src/DragTracker.cpp',
+ 'apz/src/FocusState.cpp',
+ 'apz/src/FocusTarget.cpp',
'apz/src/GestureEventListener.cpp',
'apz/src/HitTestingTreeNode.cpp',
'apz/src/InputBlockState.cpp',
'apz/src/InputQueue.cpp',
'apz/src/Keyboard.cpp',
'apz/src/OverscrollHandoffState.cpp',
'apz/src/PotentialCheckerboardDurationTracker.cpp',
'apz/src/QueuedInput.cpp',
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -2272,17 +2272,17 @@ PresShell::IntraLineMove(bool aForward,
}
NS_IMETHODIMP
PresShell::PageMove(bool aForward, bool aExtend)
{
nsIScrollableFrame *scrollableFrame =
- GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
+ GetScrollableFrameToScroll(nsIPresShell::eVertical);
if (!scrollableFrame)
return NS_OK;
RefPtr<nsFrameSelection> frameSelection = mSelection;
frameSelection->CommonPageMove(aForward, aExtend, scrollableFrame);
// After ScrollSelectionIntoView(), the pending notifications might be
// flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
@@ -2292,17 +2292,17 @@ PresShell::PageMove(bool aForward, bool
}
NS_IMETHODIMP
PresShell::ScrollPage(bool aForward)
{
nsIScrollableFrame* scrollFrame =
- GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
+ GetScrollableFrameToScroll(nsIPresShell::eVertical);
if (scrollFrame) {
mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
(uint32_t) ScrollInputMethod::MainThreadScrollPage);
scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
nsIScrollableFrame::PAGES,
nsIScrollableFrame::SMOOTH,
nullptr, nullptr,
nsIScrollableFrame::NOT_MOMENTUM,
@@ -2310,17 +2310,17 @@ PresShell::ScrollPage(bool aForward)
}
return NS_OK;
}
NS_IMETHODIMP
PresShell::ScrollLine(bool aForward)
{
nsIScrollableFrame* scrollFrame =
- GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
+ GetScrollableFrameToScroll(nsIPresShell::eVertical);
if (scrollFrame) {
mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
(uint32_t) ScrollInputMethod::MainThreadScrollLine);
int32_t lineCount = Preferences::GetInt("toolkit.scrollbox.verticalScrollDistance",
NS_DEFAULT_VERTICAL_SCROLL_DISTANCE);
scrollFrame->ScrollBy(nsIntPoint(0, aForward ? lineCount : -lineCount),
nsIScrollableFrame::LINES,
@@ -2331,17 +2331,17 @@ PresShell::ScrollLine(bool aForward)
}
return NS_OK;
}
NS_IMETHODIMP
PresShell::ScrollCharacter(bool aRight)
{
nsIScrollableFrame* scrollFrame =
- GetFrameToScrollAsScrollable(nsIPresShell::eHorizontal);
+ GetScrollableFrameToScroll(nsIPresShell::eHorizontal);
if (scrollFrame) {
mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
(uint32_t) ScrollInputMethod::MainThreadScrollCharacter);
int32_t h = Preferences::GetInt("toolkit.scrollbox.horizontalScrollDistance",
NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE);
scrollFrame->ScrollBy(nsIntPoint(aRight ? h : -h, 0),
nsIScrollableFrame::LINES,
nsIScrollableFrame::SMOOTH,
@@ -2351,17 +2351,17 @@ PresShell::ScrollCharacter(bool aRight)
}
return NS_OK;
}
NS_IMETHODIMP
PresShell::CompleteScroll(bool aForward)
{
nsIScrollableFrame* scrollFrame =
- GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
+ GetScrollableFrameToScroll(nsIPresShell::eVertical);
if (scrollFrame) {
mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
(uint32_t) ScrollInputMethod::MainThreadCompleteScroll);
scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
nsIScrollableFrame::WHOLE,
nsIScrollableFrame::SMOOTH,
nullptr, nullptr,
nsIScrollableFrame::NOT_MOMENTUM,
@@ -2825,22 +2825,19 @@ PresShell::FrameNeedsToContinueReflow(ns
nsLayoutUtils::IsProperAncestorFrame(mCurrentReflowRoot, aFrame),
"Frame passed in is not the descendant of mCurrentReflowRoot");
NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW,
"Frame passed in not in reflow?");
mFramesToDirty.PutEntry(aFrame);
}
-nsIScrollableFrame*
-nsIPresShell::GetFrameToScrollAsScrollable(
- nsIPresShell::ScrollDirection aDirection)
-{
- nsIScrollableFrame* scrollFrame = nullptr;
-
+already_AddRefed<nsIContent>
+nsIPresShell::GetContentForScrolling() const
+{
nsCOMPtr<nsIContent> focusedContent;
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
if (fm && mDocument) {
nsCOMPtr<nsIDOMElement> focusedElement;
fm->GetFocusedElementForWindow(mDocument->GetWindow(), false, nullptr,
getter_AddRefs(focusedElement));
focusedContent = do_QueryInterface(focusedElement);
}
@@ -2848,18 +2845,27 @@ nsIPresShell::GetFrameToScrollAsScrollab
nsISelection* domSelection =
mSelection->GetSelection(SelectionType::eNormal);
if (domSelection) {
nsCOMPtr<nsIDOMNode> focusedNode;
domSelection->GetFocusNode(getter_AddRefs(focusedNode));
focusedContent = do_QueryInterface(focusedNode);
}
}
- if (focusedContent) {
- nsIFrame* startFrame = focusedContent->GetPrimaryFrame();
+ return focusedContent.forget();
+}
+
+nsIScrollableFrame*
+nsIPresShell::GetScrollableFrameToScrollForContent(
+ nsIContent* aContent,
+ nsIPresShell::ScrollDirection aDirection)
+{
+ nsIScrollableFrame* scrollFrame = nullptr;
+ if (aContent) {
+ nsIFrame* startFrame = aContent->GetPrimaryFrame();
if (startFrame) {
scrollFrame = startFrame->GetScrollTargetFrame();
if (scrollFrame) {
startFrame = scrollFrame->GetScrolledFrame();
}
if (aDirection == nsIPresShell::eEither) {
scrollFrame =
nsLayoutUtils::GetNearestScrollableFrame(startFrame);
@@ -2872,16 +2878,23 @@ nsIPresShell::GetFrameToScrollAsScrollab
}
}
if (!scrollFrame) {
scrollFrame = GetRootScrollFrameAsScrollable();
}
return scrollFrame;
}
+nsIScrollableFrame*
+nsIPresShell::GetScrollableFrameToScroll(nsIPresShell::ScrollDirection aDirection)
+{
+ nsCOMPtr<nsIContent> content = GetContentForScrolling();
+ return GetScrollableFrameToScrollForContent(content.get(), aDirection);
+}
+
void
PresShell::CancelAllPendingReflows()
{
mDirtyRoots.Clear();
if (mObservingLayoutFlushes) {
GetPresContext()->RefreshDriver()->RemoveLayoutFlushObserver(this);
mObservingLayoutFlushes = false;
@@ -6738,16 +6751,27 @@ PresShell::GetRootWindow()
// If we don't have DOM window, we're zombie, we should find the root window
// with our parent shell.
nsCOMPtr<nsIPresShell> parent = GetParentPresShellForEventHandling();
NS_ENSURE_TRUE(parent, nullptr);
return parent->GetRootWindow();
}
+already_AddRefed<nsPIDOMWindowOuter>
+PresShell::GetFocusedDOMWindowInOurWindow()
+{
+ nsCOMPtr<nsPIDOMWindowOuter> rootWindow = GetRootWindow();
+ NS_ENSURE_TRUE(rootWindow, nullptr);
+ nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
+ nsFocusManager::GetFocusedDescendant(rootWindow, true,
+ getter_AddRefs(focusedWindow));
+ return focusedWindow.forget();
+}
+
already_AddRefed<nsIPresShell>
PresShell::GetParentPresShellForEventHandling()
{
NS_ENSURE_TRUE(mPresContext, nullptr);
// Now, find the parent pres shell and send the event there
nsCOMPtr<nsIDocShellTreeItem> treeItem = mPresContext->GetDocShell();
if (!treeItem) {
@@ -6784,27 +6808,16 @@ PresShell::RetargetEventToParent(WidgetG
}
void
PresShell::DisableNonTestMouseEvents(bool aDisable)
{
sDisableNonTestMouseEvents = aDisable;
}
-already_AddRefed<nsPIDOMWindowOuter>
-PresShell::GetFocusedDOMWindowInOurWindow()
-{
- nsCOMPtr<nsPIDOMWindowOuter> rootWindow = GetRootWindow();
- NS_ENSURE_TRUE(rootWindow, nullptr);
- nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
- nsFocusManager::GetFocusedDescendant(rootWindow, true,
- getter_AddRefs(focusedWindow));
- return focusedWindow.forget();
-}
-
void
PresShell::RecordMouseLocation(WidgetGUIEvent* aEvent)
{
if (!mPresContext)
return;
if (!mPresContext->IsRoot()) {
PresShell* rootPresShell = GetRootPresShell();
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -205,16 +205,18 @@ public:
virtual already_AddRefed<SourceSurface>
RenderSelection(nsISelection* aSelection,
const mozilla::LayoutDeviceIntPoint aPoint,
mozilla::LayoutDeviceIntRect* aScreenRect,
uint32_t aFlags) override;
virtual already_AddRefed<nsPIDOMWindowOuter> GetRootWindow() override;
+ virtual already_AddRefed<nsPIDOMWindowOuter> GetFocusedDOMWindowInOurWindow() override;
+
virtual LayerManager* GetLayerManager() override;
virtual bool AsyncPanZoomEnabled() override;
virtual void SetIgnoreViewportScrolling(bool aIgnore) override;
virtual nsresult SetResolution(float aResolution) override {
return SetResolutionImpl(aResolution, /* aScaleToResolution = */ false);
@@ -683,19 +685,16 @@ protected:
* event. Otherwise, false.
*/
nsresult HandleEventInternal(mozilla::WidgetEvent* aEvent,
nsEventStatus* aStatus,
bool aIsHandlingNativeEvent);
nsresult HandlePositionedEvent(nsIFrame* aTargetFrame,
mozilla::WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus);
- // This returns the focused DOM window under our top level window.
- // I.e., when we are deactive, this returns the *last* focused DOM window.
- already_AddRefed<nsPIDOMWindowOuter> GetFocusedDOMWindowInOurWindow();
/*
* This and the next two helper methods are used to target and position the
* context menu when the keyboard shortcut is used to open it.
*
* If another menu is open, the context menu is opened relative to the
* active menuitem within the menu, or the menu itself if no item is active.
* Otherwise, if the caret is visible, the menu is opened near the caret.
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -423,25 +423,41 @@ public:
nsIScrollableFrame* GetRootScrollFrameAsScrollable() const;
/*
* The same as GetRootScrollFrame, but returns an nsIScrollableFrame.
* Can be called by code not linked into gklayout.
*/
virtual nsIScrollableFrame* GetRootScrollFrameAsScrollableExternal() const;
- /*
+ /**
+ * Get the current focused content or DOM selection that should be the
+ * target for scrolling.
+ */
+ already_AddRefed<nsIContent> GetContentForScrolling() const;
+
+ /**
+ * Gets nearest scrollable frame from the specified content node. The frame
+ * is scrollable with overflow:scroll or overflow:auto in some direction when
+ * aDirection is eEither. Otherwise, this returns a nearest frame that is
+ * scrollable in the specified direction.
+ */
+ enum ScrollDirection { eHorizontal, eVertical, eEither };
+ nsIScrollableFrame* GetScrollableFrameToScrollForContent(
+ nsIContent* aContent,
+ ScrollDirection aDirection);
+
+ /**
* Gets nearest scrollable frame from current focused content or DOM
* selection if there is no focused content. The frame is scrollable with
* overflow:scroll or overflow:auto in some direction when aDirection is
* eEither. Otherwise, this returns a nearest frame that is scrollable in
* the specified direction.
*/
- enum ScrollDirection { eHorizontal, eVertical, eEither };
- nsIScrollableFrame* GetFrameToScrollAsScrollable(ScrollDirection aDirection);
+ nsIScrollableFrame* GetScrollableFrameToScroll(ScrollDirection aDirection);
/**
* Returns the page sequence frame associated with the frame hierarchy.
* Returns nullptr if not a paginated view.
*/
virtual nsIPageSequenceFrame* GetPageSequenceFrame() const = 0;
/**
@@ -1416,16 +1432,22 @@ public:
void IncrementPaintCount() { ++mPaintCount; }
/**
* Get the root DOM window of this presShell.
*/
virtual already_AddRefed<nsPIDOMWindowOuter> GetRootWindow() = 0;
/**
+ * This returns the focused DOM window under our top level window.
+ * I.e., when we are deactive, this returns the *last* focused DOM window.
+ */
+ virtual already_AddRefed<nsPIDOMWindowOuter> GetFocusedDOMWindowInOurWindow() = 0;
+
+ /**
* Get the layer manager for the widget of the root view, if it has
* one.
*/
virtual LayerManager* GetLayerManager() = 0;
/**
* Return true iff there is a widget rendering this presShell and that
* widget is APZ-enabled.
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -860,16 +860,35 @@ nsLayoutUtils::FindScrollableFrameFor(Vi
if (!content) {
return nullptr;
}
nsIFrame* scrollFrame = GetScrollFrameFromContent(content);
return scrollFrame ? scrollFrame->GetScrollTargetFrame() : nullptr;
}
+ViewID
+nsLayoutUtils::FindIDForScrollableFrame(nsIScrollableFrame* aScrollable)
+{
+ if (!aScrollable) {
+ return FrameMetrics::NULL_SCROLL_ID;
+ }
+
+ nsIFrame* scrollFrame = do_QueryFrame(aScrollable);
+ nsIContent* scrollContent = scrollFrame->GetContent();
+
+ FrameMetrics::ViewID scrollId;
+ if (scrollContent &&
+ nsLayoutUtils::FindIDFor(scrollContent, &scrollId)) {
+ return scrollId;
+ }
+
+ return FrameMetrics::NULL_SCROLL_ID;
+}
+
static nsRect
ApplyRectMultiplier(nsRect aRect, float aMultiplier)
{
if (aMultiplier == 1.0f) {
return aRect;
}
float newWidth = aRect.width * aMultiplier;
float newHeight = aRect.height * aMultiplier;
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -184,16 +184,21 @@ public:
static nsIContent* FindContentFor(ViewID aId);
/**
* Find the scrollable frame for a given ID.
*/
static nsIScrollableFrame* FindScrollableFrameFor(ViewID aId);
/**
+ * Find the ID for a given scrollable frame.
+ */
+ static ViewID FindIDForScrollableFrame(nsIScrollableFrame* aScrollable);
+
+ /**
* Get display port for the given element, relative to the specified entity,
* defaulting to the scrollport.
*/
static bool GetDisplayPort(nsIContent* aContent, nsRect *aResult,
RelativeTo aRelativeTo = RelativeTo::ScrollPort);
/**
* Check whether the given element has a displayport.