Commit 0129e48f authored by Findit's avatar Findit

Revert "Reland "Change autoscroll latching to top-most delta-consumable scroller""

This reverts commit 67eedb97.

Reason for revert:

Findit (https://goo.gl/kROfz5) identified CL at revision 842593 as the
culprit for flakes in the build cycles as shown on:
https://analysis.chromium.org/p/chromium/flake-portal/analysis/culprit?key=ag9zfmZpbmRpdC1mb3ItbWVyQwsSDEZsYWtlQ3VscHJpdCIxY2hyb21pdW0vNjdlZWRiOTcwYzJhYWY0MDI5Mzc1ZmYxODBmZDgxNzU4YTUzN2Q0NQw

Sample Failed Build: https://ci.chromium.org/b/8858279089687558800

Sample Failed Step: blink_web_tests on Mac-10.15

Sample Flaky Test: virtual/threaded-prefer-compositing/fast/scrolling/autoscroll-latch-clicked-node-if-parent-unscrollable.html

Original change's description:
> Reland "Change autoscroll latching to top-most delta-consumable scroller"
> 
> This is a reland of 07b882d4
> 
> The original CL was reverted due to a few layout tests failing in ASAN.
> The cause of these failures was that vertical_autoscroll_layout_box_
> and horizontal_autoscroll_layout_box were not being cleared in
> StopMiddleClickAutocroll. This resulted in heap-use-after-free errors.
> 
> In order to fix this issue, the two pointers are cleared in
> StopMiddleClickAutoscroll. They are also cleared if necessary in
> StopAutoscrollIfNeeded. In the latter, middle click autoscroll would be
> stopped if both layout boxes are to be cleared.
> 
> Original change's description:
> > Change autoscroll latching to top-most delta-consumable scroller
> >
> > Users will now be able to use middle click autoscroll to scroll a
> > parent div if the inner-most scroller is unable to scroll in that
> > direction.
> >
> > If there is no delta-consumable scroller, the top-most autoscrollable
> > scroller will be latched.
> >
> > Bug: 1107648
> > Change-Id: Iccd4efec3b1ce5d09c701d3d46052176275dbc32
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2488042
> > Reviewed-by: Robert Flack <flackr@chromium.org>
> > Reviewed-by: Rahul Arakeri <arakeri@microsoft.com>
> > Commit-Queue: Sahir Vellani <sahir.vellani@microsoft.com>
> > Cr-Commit-Position: refs/heads/master@{#835318}
> 
> Bug: 1107648
> Change-Id: Idf3c2253a25d2cfbe12a8ffe30bbf697c636d222
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2582733
> Reviewed-by: Robert Flack <flackr@chromium.org>
> Reviewed-by: Rahul Arakeri <arakeri@microsoft.com>
> Commit-Queue: Sahir Vellani <sahir.vellani@microsoft.com>
> Cr-Commit-Position: refs/heads/master@{#842593}


Change-Id: Ifb6a6fac520d4c9ad963cb329156a2b5d995817a
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 1107648
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2626451
Cr-Commit-Position: refs/heads/master@{#842770}
parent d890119a
...@@ -1842,7 +1842,6 @@ ScrollNode* ThreadedInputHandler::FindNodeToLatch(ScrollState* scroll_state, ...@@ -1842,7 +1842,6 @@ ScrollNode* ThreadedInputHandler::FindNodeToLatch(ScrollState* scroll_state,
ui::ScrollInputType type) { ui::ScrollInputType type) {
ScrollTree& scroll_tree = GetScrollTree(); ScrollTree& scroll_tree = GetScrollTree();
ScrollNode* scroll_node = nullptr; ScrollNode* scroll_node = nullptr;
ScrollNode* first_scrollable_node = nullptr;
for (ScrollNode* cur_node = starting_node; cur_node; for (ScrollNode* cur_node = starting_node; cur_node;
cur_node = scroll_tree.parent(cur_node)) { cur_node = scroll_tree.parent(cur_node)) {
if (GetViewport().ShouldScroll(*cur_node)) { if (GetViewport().ShouldScroll(*cur_node)) {
...@@ -1856,11 +1855,10 @@ ScrollNode* ThreadedInputHandler::FindNodeToLatch(ScrollState* scroll_state, ...@@ -1856,11 +1855,10 @@ ScrollNode* ThreadedInputHandler::FindNodeToLatch(ScrollState* scroll_state,
if (!cur_node->scrollable) if (!cur_node->scrollable)
continue; continue;
if (!first_scrollable_node) { // For UX reasons, autoscrolling should always latch to the top-most
first_scrollable_node = cur_node; // scroller, even if it can't scroll in the initial direction.
} if (type == ui::ScrollInputType::kAutoscroll ||
CanConsumeDelta(*scroll_state, *cur_node)) {
if (CanConsumeDelta(*scroll_state, *cur_node)) {
scroll_node = cur_node; scroll_node = cur_node;
break; break;
} }
...@@ -1882,13 +1880,6 @@ ScrollNode* ThreadedInputHandler::FindNodeToLatch(ScrollState* scroll_state, ...@@ -1882,13 +1880,6 @@ ScrollNode* ThreadedInputHandler::FindNodeToLatch(ScrollState* scroll_state,
} }
} }
// If the root scroller can not consume delta in an autoscroll, latch on
// to the top most autoscrollable scroller. See https://crbug.com/969150
if ((type == ui::ScrollInputType::kAutoscroll) && first_scrollable_node &&
!CanConsumeDelta(*scroll_state, *scroll_node)) {
scroll_node = first_scrollable_node;
}
return scroll_node; return scroll_node;
} }
......
...@@ -300,10 +300,8 @@ void EventHandler::StartMiddleClickAutoscroll(LayoutObject* layout_object) { ...@@ -300,10 +300,8 @@ void EventHandler::StartMiddleClickAutoscroll(LayoutObject* layout_object) {
AutoscrollController* controller = scroll_manager_->GetAutoscrollController(); AutoscrollController* controller = scroll_manager_->GetAutoscrollController();
if (!controller) if (!controller)
return; return;
LayoutBox* scrollable = LayoutBox::FindAutoscrollable( LayoutBox* scrollable = LayoutBox::FindAutoscrollable(
layout_object, /*is_middle_click_autoscroll*/ true); layout_object, /*is_middle_click_autoscroll*/ true);
controller->StartMiddleClickAutoscroll( controller->StartMiddleClickAutoscroll(
layout_object->GetFrame(), scrollable, layout_object->GetFrame(), scrollable,
LastKnownMousePositionInRootFrame(), LastKnownMousePositionInRootFrame(),
......
...@@ -133,20 +133,8 @@ AutoscrollController* ScrollManager::GetAutoscrollController() const { ...@@ -133,20 +133,8 @@ AutoscrollController* ScrollManager::GetAutoscrollController() const {
return nullptr; return nullptr;
} }
ScrollPropagationDirection ScrollManager::ComputePropagationDirection( static bool CanPropagate(const ScrollState& scroll_state, const Node& node) {
const ScrollState& scroll_state) { ScrollableArea* scrollable_area = node.GetLayoutBox()->GetScrollableArea();
if (scroll_state.deltaXHint() == 0 && scroll_state.deltaYHint() != 0)
return ScrollPropagationDirection::kVertical;
if (scroll_state.deltaXHint() != 0 && scroll_state.deltaYHint() == 0)
return ScrollPropagationDirection::kHorizontal;
if (scroll_state.deltaXHint() != 0 && scroll_state.deltaYHint() != 0)
return ScrollPropagationDirection::kBoth;
return ScrollPropagationDirection::kNone;
}
bool ScrollManager::CanPropagate(const LayoutBox* layout_box,
ScrollPropagationDirection direction) {
ScrollableArea* scrollable_area = layout_box->GetScrollableArea();
if (!scrollable_area) if (!scrollable_area)
return true; return true;
...@@ -154,106 +142,54 @@ bool ScrollManager::CanPropagate(const LayoutBox* layout_box, ...@@ -154,106 +142,54 @@ bool ScrollManager::CanPropagate(const LayoutBox* layout_box,
!scrollable_area->UserInputScrollable(kVerticalScrollbar)) !scrollable_area->UserInputScrollable(kVerticalScrollbar))
return true; return true;
switch (direction) { return (scroll_state.deltaXHint() == 0 ||
case ScrollPropagationDirection::kBoth: node.GetComputedStyle()->OverscrollBehaviorX() ==
return ((layout_box->StyleRef().OverscrollBehaviorX() == EOverscrollBehavior::kAuto) &&
EOverscrollBehavior::kAuto) && (scroll_state.deltaYHint() == 0 ||
(layout_box->StyleRef().OverscrollBehaviorY() == node.GetComputedStyle()->OverscrollBehaviorY() ==
EOverscrollBehavior::kAuto)); EOverscrollBehavior::kAuto);
case ScrollPropagationDirection::kVertical:
return layout_box->StyleRef().OverscrollBehaviorY() ==
EOverscrollBehavior::kAuto;
case ScrollPropagationDirection::kHorizontal:
return layout_box->StyleRef().OverscrollBehaviorX() ==
EOverscrollBehavior::kAuto;
case ScrollPropagationDirection::kNone:
return true;
default:
NOTREACHED();
}
} }
void ScrollManager::RecomputeScrollChain(const Node& start_node, void ScrollManager::RecomputeScrollChain(const Node& start_node,
const ScrollState& scroll_state, const ScrollState& scroll_state,
Deque<DOMNodeId>& scroll_chain, Deque<DOMNodeId>& scroll_chain) {
bool is_autoscroll) {
DCHECK(scroll_chain.IsEmpty()); DCHECK(scroll_chain.IsEmpty());
scroll_chain.clear(); scroll_chain.clear();
DCHECK(start_node.GetLayoutObject()); DCHECK(start_node.GetLayoutObject());
LayoutBox* cur_box = start_node.GetLayoutObject()->EnclosingBox();
if (is_autoscroll) { // Scrolling propagates along the containing block chain and ends at the
// Propagate the autoscroll along the layout object chain, and // RootScroller node. The RootScroller node will have a custom applyScroll
// append only the first node which is able to consume the scroll delta. // callback that performs scrolling as well as associated "root" actions like
// The scroll node is computed differently to regular scrolls in order to // browser control movement and overscroll glow.
// maintain consistency with the autoscroll controller. while (cur_box) {
LayoutBox* autoscrollable = LayoutBox::FindAutoscrollable( Node* cur_node = cur_box->GetNode();
start_node.GetLayoutObject(), is_autoscroll);
if (autoscrollable) {
Node* cur_node = autoscrollable->GetNode();
LayoutObject* layout_object = cur_node->GetLayoutObject();
while (layout_object &&
!CanScroll(scroll_state, *cur_node, is_autoscroll)) {
ScrollPropagationDirection direction =
ComputePropagationDirection(scroll_state);
if (!CanPropagate(cur_node->GetLayoutBox(), direction))
break;
if (!layout_object->Parent() &&
layout_object->GetNode() == layout_object->GetDocument() &&
layout_object->GetDocument().LocalOwner()) {
layout_object =
layout_object->GetDocument().LocalOwner()->GetLayoutObject();
} else {
layout_object = layout_object->Parent();
}
LayoutBox* new_autoscrollable =
LayoutBox::FindAutoscrollable(layout_object, is_autoscroll);
if (new_autoscrollable)
cur_node = new_autoscrollable->GetNode();
}
scroll_chain.push_front(DOMNodeIds::IdForNode(cur_node));
}
} else {
LayoutBox* cur_box = start_node.GetLayoutObject()->EnclosingBox();
// Scrolling propagates along the containing block chain and ends at the if (cur_node) {
// RootScroller node. The RootScroller node will have a custom applyScroll if (CanScroll(scroll_state, *cur_node))
// callback that performs scrolling as well as associated "root" actions scroll_chain.push_front(DOMNodeIds::IdForNode(cur_node));
// like browser control movement and overscroll glow.
while (cur_box) {
Node* cur_node = cur_box->GetNode();
if (cur_node) { if (cur_node->IsEffectiveRootScroller())
if (CanScroll(scroll_state, *cur_node, /* for_autoscroll */ false)) break;
scroll_chain.push_front(DOMNodeIds::IdForNode(cur_node));
if (cur_node->IsEffectiveRootScroller()) if (!CanPropagate(scroll_state, *cur_node)) {
break; // We should add the first node with non-auto overscroll-behavior to
// the scroll chain regardlessly, as it's the only node we can latch to.
ScrollPropagationDirection direction = if (scroll_chain.empty() ||
ComputePropagationDirection(scroll_state); scroll_chain.front() != DOMNodeIds::IdForNode(cur_node)) {
if (!CanPropagate(cur_node->GetLayoutBox(), direction)) { scroll_chain.push_front(DOMNodeIds::IdForNode(cur_node));
// We should add the first node with non-auto overscroll-behavior to
// the scroll chain regardlessly, as it's the only node we can latch
// to.
if (scroll_chain.empty() ||
scroll_chain.front() != DOMNodeIds::IdForNode(cur_node)) {
scroll_chain.push_front(DOMNodeIds::IdForNode(cur_node));
}
break;
} }
break;
} }
cur_box = cur_box->ContainingBlock();
} }
cur_box = cur_box->ContainingBlock();
} }
} }
bool ScrollManager::CanScroll(const ScrollState& scroll_state, bool ScrollManager::CanScroll(const ScrollState& scroll_state,
const Node& current_node, const Node& current_node) {
bool for_autoscroll) {
LayoutBox* scrolling_box = current_node.GetLayoutBox(); LayoutBox* scrolling_box = current_node.GetLayoutBox();
if (auto* element = DynamicTo<Element>(current_node)) if (auto* element = DynamicTo<Element>(current_node))
scrolling_box = element->GetLayoutBoxForScrolling(); scrolling_box = element->GetLayoutBoxForScrolling();
...@@ -262,9 +198,8 @@ bool ScrollManager::CanScroll(const ScrollState& scroll_state, ...@@ -262,9 +198,8 @@ bool ScrollManager::CanScroll(const ScrollState& scroll_state,
// We need to always add the global root scroller even if it isn't scrollable // We need to always add the global root scroller even if it isn't scrollable
// since we can always pinch-zoom and scroll as well as for overscroll // since we can always pinch-zoom and scroll as well as for overscroll
// effects. If autoscrolling, ignore this condition because we latch on // effects.
// to the deepest autoscrollable node. if (scrolling_box->IsGlobalRootScroller())
if (scrolling_box->IsGlobalRootScroller() && !for_autoscroll)
return true; return true;
// If this is the main LayoutView, and it's not the root scroller, that means // If this is the main LayoutView, and it's not the root scroller, that means
...@@ -272,10 +207,9 @@ bool ScrollManager::CanScroll(const ScrollState& scroll_state, ...@@ -272,10 +207,9 @@ bool ScrollManager::CanScroll(const ScrollState& scroll_state,
// scroll the LayoutView should cause panning of the visual viewport as well // scroll the LayoutView should cause panning of the visual viewport as well
// so ensure it gets added to the scroll chain. See LTHI::ApplyScroll for the // so ensure it gets added to the scroll chain. See LTHI::ApplyScroll for the
// equivalent behavior in CC. Node::NativeApplyScroll contains a special // equivalent behavior in CC. Node::NativeApplyScroll contains a special
// handler for this case. If autoscrolling, ignore this condition because we // handler for this case.
// latch on to the deepest autoscrollable node.
if (IsA<LayoutView>(scrolling_box) && if (IsA<LayoutView>(scrolling_box) &&
current_node.GetDocument().GetFrame()->IsMainFrame() && !for_autoscroll) { current_node.GetDocument().GetFrame()->IsMainFrame()) {
return true; return true;
} }
...@@ -339,8 +273,7 @@ bool ScrollManager::LogicalScroll(mojom::blink::ScrollDirection direction, ...@@ -339,8 +273,7 @@ bool ScrollManager::LogicalScroll(mojom::blink::ScrollDirection direction,
std::make_unique<ScrollStateData>(); std::make_unique<ScrollStateData>();
auto* scroll_state = auto* scroll_state =
MakeGarbageCollected<ScrollState>(std::move(scroll_state_data)); MakeGarbageCollected<ScrollState>(std::move(scroll_state_data));
RecomputeScrollChain(*node, *scroll_state, scroll_chain, RecomputeScrollChain(*node, *scroll_state, scroll_chain);
/* is_autoscroll */ false);
while (!scroll_chain.IsEmpty()) { while (!scroll_chain.IsEmpty()) {
Node* scroll_chain_node = DOMNodeIds::NodeForId(scroll_chain.TakeLast()); Node* scroll_chain_node = DOMNodeIds::NodeForId(scroll_chain.TakeLast());
...@@ -564,11 +497,21 @@ WebInputEventResult ScrollManager::HandleGestureScrollBegin( ...@@ -564,11 +497,21 @@ WebInputEventResult ScrollManager::HandleGestureScrollBegin(
delta_consumed_for_scroll_sequence_; delta_consumed_for_scroll_sequence_;
auto* scroll_state = auto* scroll_state =
MakeGarbageCollected<ScrollState>(std::move(scroll_state_data)); MakeGarbageCollected<ScrollState>(std::move(scroll_state_data));
// For middle click autoscroll, only scrollable area for
RecomputeScrollChain( // |scroll_gesture_handling_node_| should receive and handle all scroll
*scroll_gesture_handling_node_.Get(), *scroll_state, // events. It should not bubble up to the ancestor.
current_scroll_chain_, if (gesture_event.SourceDevice() == WebGestureDevice::kSyntheticAutoscroll) {
gesture_event.SourceDevice() == WebGestureDevice::kSyntheticAutoscroll); LayoutBox* scrollable = LayoutBox::FindAutoscrollable(
scroll_gesture_handling_node_->GetLayoutObject(),
/*is_middle_click_autoscroll*/ true);
if (scrollable) {
Node* scrollable_node = scrollable->GetNode();
current_scroll_chain_.push_back(DOMNodeIds::IdForNode(scrollable_node));
}
} else {
RecomputeScrollChain(*scroll_gesture_handling_node_.Get(), *scroll_state,
current_scroll_chain_);
}
TRACE_EVENT_INSTANT1("input", "Computed Scroll Chain", TRACE_EVENT_INSTANT1("input", "Computed Scroll Chain",
TRACE_EVENT_SCOPE_THREAD, "length", TRACE_EVENT_SCOPE_THREAD, "length",
......
...@@ -34,10 +34,6 @@ class Scrollbar; ...@@ -34,10 +34,6 @@ class Scrollbar;
class ScrollState; class ScrollState;
class WebGestureEvent; class WebGestureEvent;
// Scroll directions used to check whether propagation is possible in a given
// direction. Used in CanPropagate.
enum class ScrollPropagationDirection { kHorizontal, kVertical, kBoth, kNone };
// This class takes care of scrolling and resizing and the related states. The // This class takes care of scrolling and resizing and the related states. The
// user action that causes scrolling or resizing is determined in other *Manager // user action that causes scrolling or resizing is determined in other *Manager
// classes and they call into this class for doing the work. // classes and they call into this class for doing the work.
...@@ -112,13 +108,6 @@ class CORE_EXPORT ScrollManager : public GarbageCollected<ScrollManager>, ...@@ -112,13 +108,6 @@ class CORE_EXPORT ScrollManager : public GarbageCollected<ScrollManager>,
void AnimateSnapFling(base::TimeTicks monotonic_time); void AnimateSnapFling(base::TimeTicks monotonic_time);
// Determines whether the scroll-chain should be propagated upwards given a
// scroll direction.
static bool CanPropagate(const LayoutBox* layout_box,
ScrollPropagationDirection direction);
static ScrollPropagationDirection ComputePropagationDirection(
const ScrollState&);
private: private:
Node* NodeTargetForScrollableAreaElementId( Node* NodeTargetForScrollableAreaElementId(
CompositorElementId scrollable_area_element_id) const; CompositorElementId scrollable_area_element_id) const;
...@@ -144,11 +133,8 @@ class CORE_EXPORT ScrollManager : public GarbageCollected<ScrollManager>, ...@@ -144,11 +133,8 @@ class CORE_EXPORT ScrollManager : public GarbageCollected<ScrollManager>,
void RecomputeScrollChain(const Node& start_node, void RecomputeScrollChain(const Node& start_node,
const ScrollState&, const ScrollState&,
Deque<DOMNodeId>& scroll_chain, Deque<DOMNodeId>& scroll_chain);
bool is_autoscroll); bool CanScroll(const ScrollState&, const Node& current_node);
bool CanScroll(const ScrollState&,
const Node& current_node,
bool for_autoscroll);
// scroller_size is set only when scrolling non root scroller. // scroller_size is set only when scrolling non root scroller.
void ComputeScrollRelatedMetrics( void ComputeScrollRelatedMetrics(
......
...@@ -29,13 +29,11 @@ ...@@ -29,13 +29,11 @@
#include "third_party/blink/renderer/core/page/autoscroll_controller.h" #include "third_party/blink/renderer/core/page/autoscroll_controller.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/visual_viewport.h" #include "third_party/blink/renderer/core/frame/visual_viewport.h"
#include "third_party/blink/renderer/core/html/html_frame_owner_element.h" #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
#include "third_party/blink/renderer/core/input/event_handler.h" #include "third_party/blink/renderer/core/input/event_handler.h"
#include "third_party/blink/renderer/core/input/scroll_manager.h"
#include "third_party/blink/renderer/core/layout/hit_test_result.h" #include "third_party/blink/renderer/core/layout/hit_test_result.h"
#include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/core/page/chrome_client.h" #include "third_party/blink/renderer/core/page/chrome_client.h"
...@@ -145,18 +143,6 @@ void AutoscrollController::StopAutoscrollIfNeeded(LayoutObject* layout_object) { ...@@ -145,18 +143,6 @@ void AutoscrollController::StopAutoscrollIfNeeded(LayoutObject* layout_object) {
if (pressed_layout_object_ == layout_object) if (pressed_layout_object_ == layout_object)
pressed_layout_object_ = nullptr; pressed_layout_object_ = nullptr;
if (horizontal_autoscroll_layout_box_ == layout_object)
horizontal_autoscroll_layout_box_ = nullptr;
if (vertical_autoscroll_layout_box_ == layout_object)
vertical_autoscroll_layout_box_ = nullptr;
if (MiddleClickAutoscrollInProgress() && !horizontal_autoscroll_layout_box_ &&
!vertical_autoscroll_layout_box_) {
page_->GetChromeClient().AutoscrollEnd(layout_object->GetFrame());
autoscroll_type_ = kNoAutoscroll;
}
if (autoscroll_layout_object_ != layout_object) if (autoscroll_layout_object_ != layout_object)
return; return;
autoscroll_layout_object_ = nullptr; autoscroll_layout_object_ = nullptr;
...@@ -264,16 +250,7 @@ void AutoscrollController::HandleMouseMoveForMiddleClickAutoscroll( ...@@ -264,16 +250,7 @@ void AutoscrollController::HandleMouseMoveForMiddleClickAutoscroll(
if (!MiddleClickAutoscrollInProgress()) if (!MiddleClickAutoscrollInProgress())
return; return;
bool horizontal_autoscroll_possible = if (!autoscroll_layout_object_->CanBeScrolledAndHasScrollableArea()) {
horizontal_autoscroll_layout_box_ &&
horizontal_autoscroll_layout_box_->GetNode();
bool vertical_autoscroll_possible =
vertical_autoscroll_layout_box_ &&
vertical_autoscroll_layout_box_->GetNode();
if (horizontal_autoscroll_possible &&
!horizontal_autoscroll_layout_box_->CanBeScrolledAndHasScrollableArea() &&
vertical_autoscroll_possible &&
!vertical_autoscroll_layout_box_->CanBeScrolledAndHasScrollableArea()) {
StopMiddleClickAutoscroll(frame); StopMiddleClickAutoscroll(frame);
return; return;
} }
...@@ -300,17 +277,11 @@ void AutoscrollController::HandleMouseMoveForMiddleClickAutoscroll( ...@@ -300,17 +277,11 @@ void AutoscrollController::HandleMouseMoveForMiddleClickAutoscroll(
pow(fabs(distance.Height()), kExponent) * kMultiplier * y_signum); pow(fabs(distance.Height()), kExponent) * kMultiplier * y_signum);
bool can_scroll_vertically = bool can_scroll_vertically =
vertical_autoscroll_possible CanScrollDirection(autoscroll_layout_object_, frame->GetPage(),
? CanScrollDirection(vertical_autoscroll_layout_box_, ScrollOrientation::kVerticalScroll);
frame->GetPage(),
ScrollOrientation::kVerticalScroll)
: false;
bool can_scroll_horizontally = bool can_scroll_horizontally =
horizontal_autoscroll_possible CanScrollDirection(autoscroll_layout_object_, frame->GetPage(),
? CanScrollDirection(horizontal_autoscroll_layout_box_, ScrollOrientation::kHorizontalScroll);
frame->GetPage(),
ScrollOrientation::kHorizontalScroll)
: false;
if (velocity != last_velocity_) { if (velocity != last_velocity_) {
last_velocity_ = velocity; last_velocity_ = velocity;
...@@ -348,8 +319,7 @@ void AutoscrollController::StopMiddleClickAutoscroll(LocalFrame* frame) { ...@@ -348,8 +319,7 @@ void AutoscrollController::StopMiddleClickAutoscroll(LocalFrame* frame) {
autoscroll_type_ = kNoAutoscroll; autoscroll_type_ = kNoAutoscroll;
page_->GetChromeClient().SetCursorOverridden(false); page_->GetChromeClient().SetCursorOverridden(false);
frame->LocalFrameRoot().GetEventHandler().UpdateCursor(); frame->LocalFrameRoot().GetEventHandler().UpdateCursor();
horizontal_autoscroll_layout_box_ = nullptr; autoscroll_layout_object_ = nullptr;
vertical_autoscroll_layout_box_ = nullptr;
} }
bool AutoscrollController::MiddleClickAutoscrollInProgress() const { bool AutoscrollController::MiddleClickAutoscrollInProgress() const {
...@@ -368,68 +338,17 @@ void AutoscrollController::StartMiddleClickAutoscroll( ...@@ -368,68 +338,17 @@ void AutoscrollController::StartMiddleClickAutoscroll(
if (autoscroll_type_ != kNoAutoscroll) if (autoscroll_type_ != kNoAutoscroll)
return; return;
autoscroll_layout_object_ = scrollable;
autoscroll_type_ = kAutoscrollForMiddleClick; autoscroll_type_ = kAutoscrollForMiddleClick;
middle_click_mode_ = kMiddleClickInitial; middle_click_mode_ = kMiddleClickInitial;
middle_click_autoscroll_start_pos_global_ = position_global; middle_click_autoscroll_start_pos_global_ = position_global;
bool can_scroll_vertically = false; bool can_scroll_vertically =
bool can_scroll_horizontally = false; CanScrollDirection(autoscroll_layout_object_, frame->GetPage(),
ScrollOrientation::kVerticalScroll);
// Scroll propagation can be prevented in either direction independently. bool can_scroll_horizontally =
// We check whether autoscroll can be prevented in either direction after CanScrollDirection(autoscroll_layout_object_, frame->GetPage(),
// checking whether the layout box can be scrolled. If propagation is not ScrollOrientation::kHorizontalScroll);
// allowed, we do not perform further checks for whether parents can be
// scrolled in that direction.
bool can_propagate_vertically = true;
bool can_propagate_horizontally = true;
LayoutObject* layout_object = scrollable->GetNode()->GetLayoutObject();
while (layout_object && !(can_scroll_horizontally && can_scroll_vertically)) {
LayoutBox* layout_box;
if (layout_object->IsBox()) {
layout_box = To<LayoutBox>(layout_object);
// Check whether the layout box can be scrolled and has horizontal
// scrollable area.
if (can_propagate_vertically &&
CanScrollDirection(layout_box, frame->GetPage(),
ScrollOrientation::kVerticalScroll) &&
!vertical_autoscroll_layout_box_) {
vertical_autoscroll_layout_box_ = layout_box;
can_scroll_vertically = true;
}
// Check whether the layout box can be scrolled and has vertical
// scrollable area.
if (can_propagate_horizontally &&
CanScrollDirection(layout_box, frame->GetPage(),
ScrollOrientation::kHorizontalScroll) &&
!horizontal_autoscroll_layout_box_) {
horizontal_autoscroll_layout_box_ = layout_box;
can_scroll_horizontally = true;
}
}
can_propagate_vertically = ScrollManager::CanPropagate(
layout_box, ScrollPropagationDirection::kVertical);
can_propagate_horizontally = ScrollManager::CanPropagate(
layout_box, ScrollPropagationDirection::kHorizontal);
// Exit loop if we can't propagate to the parent in any direction or if
// layout boxes have been found for both directions.
if ((!can_propagate_vertically && !can_propagate_horizontally) ||
(can_scroll_horizontally && can_scroll_vertically))
break;
if (!layout_object->Parent() &&
layout_object->GetNode() == layout_object->GetDocument() &&
layout_object->GetDocument().LocalOwner()) {
layout_object =
layout_object->GetDocument().LocalOwner()->GetLayoutObject();
} else {
layout_object = layout_object->Parent();
}
}
UseCounter::Count(frame->GetDocument(), UseCounter::Count(frame->GetDocument(),
WebFeature::kMiddleClickAutoscrollStart); WebFeature::kMiddleClickAutoscrollStart);
......
...@@ -84,7 +84,7 @@ class CORE_EXPORT AutoscrollController final ...@@ -84,7 +84,7 @@ class CORE_EXPORT AutoscrollController final
// Middle-click autoscroll. // Middle-click autoscroll.
void StartMiddleClickAutoscroll(LocalFrame*, void StartMiddleClickAutoscroll(LocalFrame*,
LayoutBox* scrollable, LayoutBox*,
const FloatPoint& position, const FloatPoint& position,
const FloatPoint& position_global); const FloatPoint& position_global);
void HandleMouseMoveForMiddleClickAutoscroll( void HandleMouseMoveForMiddleClickAutoscroll(
...@@ -102,17 +102,15 @@ class CORE_EXPORT AutoscrollController final ...@@ -102,17 +102,15 @@ class CORE_EXPORT AutoscrollController final
Member<Page> page_; Member<Page> page_;
AutoscrollType autoscroll_type_ = kNoAutoscroll; AutoscrollType autoscroll_type_ = kNoAutoscroll;
LayoutBox* autoscroll_layout_object_ = nullptr;
// Selection and drag-and-drop autoscroll. // Selection and drag-and-drop autoscroll.
void ScheduleMainThreadAnimation(); void ScheduleMainThreadAnimation();
LayoutBox* autoscroll_layout_object_ = nullptr;
LayoutBox* pressed_layout_object_ = nullptr; LayoutBox* pressed_layout_object_ = nullptr;
PhysicalOffset drag_and_drop_autoscroll_reference_position_; PhysicalOffset drag_and_drop_autoscroll_reference_position_;
base::TimeTicks drag_and_drop_autoscroll_start_time_; base::TimeTicks drag_and_drop_autoscroll_start_time_;
// Middle-click autoscroll. // Middle-click autoscroll.
LayoutBox* horizontal_autoscroll_layout_box_ = nullptr;
LayoutBox* vertical_autoscroll_layout_box_ = nullptr;
FloatPoint middle_click_autoscroll_start_pos_global_; FloatPoint middle_click_autoscroll_start_pos_global_;
gfx::Vector2dF last_velocity_; gfx::Vector2dF last_velocity_;
MiddleClickMode middle_click_mode_ = kMiddleClickInitial; MiddleClickMode middle_click_mode_ = kMiddleClickInitial;
...@@ -122,9 +120,6 @@ class CORE_EXPORT AutoscrollController final ...@@ -122,9 +120,6 @@ class CORE_EXPORT AutoscrollController final
FRIEND_TEST_ALL_PREFIXES(AutoscrollControllerTest, FRIEND_TEST_ALL_PREFIXES(AutoscrollControllerTest,
ContinueAutoscrollAfterMouseLeaveEvent); ContinueAutoscrollAfterMouseLeaveEvent);
FRIEND_TEST_ALL_PREFIXES(AutoscrollControllerTest, StopAutoscrollOnResize); FRIEND_TEST_ALL_PREFIXES(AutoscrollControllerTest, StopAutoscrollOnResize);
FRIEND_TEST_ALL_PREFIXES(AutoscrollControllerTest, AutoscrollIsNotPropagated);
FRIEND_TEST_ALL_PREFIXES(AutoscrollControllerTest,
AutoscrollIsPropagatedInYDirection);
}; };
} // namespace blink } // namespace blink
......
...@@ -182,102 +182,4 @@ TEST_F(AutoscrollControllerTest, StopAutoscrollOnResize) { ...@@ -182,102 +182,4 @@ TEST_F(AutoscrollControllerTest, StopAutoscrollOnResize) {
EXPECT_TRUE(controller.IsAutoscrolling()); EXPECT_TRUE(controller.IsAutoscrolling());
} }
// Ensure that middle click autoscroll is not propagated in a direction when
// propagation is not allowed.
TEST_F(AutoscrollControllerTest, AutoscrollIsNotPropagated) {
SimRequest request("https://example.com/test.html", "text/html");
LoadURL("https://example.com/test.html");
request.Complete(R"HTML(
<!DOCTYPE html>
<html>
<head>
<style>
#scrollable {
width: 820px;
height: 620px;
overflow: auto;
overscroll-behavior: contain;
}
#inner {
width: 2500px;
background-color: aqua;
height: 100px;
}
</style>
</head>
<body style='width: 3000px; height: 3000px;'>
<div id="scrollable">
<div id="inner"></div>
</div>
</body>
</html>
)HTML");
Compositor().BeginFrame();
AutoscrollController& controller = GetAutoscrollController();
EXPECT_FALSE(controller.IsAutoscrolling());
LocalFrame* frame = GetDocument().GetFrame();
LayoutBox* scrollable =
GetDocument().getElementById("scrollable")->GetLayoutBox();
controller.StartMiddleClickAutoscroll(
frame, scrollable, FloatPoint(15.0, 15.0), FloatPoint(15.0, 15.0));
EXPECT_TRUE(controller.IsAutoscrolling());
EXPECT_TRUE(controller.horizontal_autoscroll_layout_box_);
EXPECT_FALSE(controller.vertical_autoscroll_layout_box_);
}
// Ensure that middle click autoscroll is propagated in a direction when
// overscroll-behavior is set to auto for a that direction.
TEST_F(AutoscrollControllerTest, AutoscrollIsPropagatedInYDirection) {
SimRequest request("https://example.com/test.html", "text/html");
LoadURL("https://example.com/test.html");
request.Complete(R"HTML(
<!DOCTYPE html>
<html>
<head>
<style>
#scrollable {
width: 820px;
height: 620px;
overflow: auto;
overscroll-behavior-x: contain;
}
#inner {
width: 1000px;
background-color: aqua;
height: 100px;
}
</style>
</head>
<body style='width: 3000px; height: 3000px;'>
<div id="scrollable">
<div id="inner"></div>
</div>
</body>
</html>
)HTML");
Compositor().BeginFrame();
AutoscrollController& controller = GetAutoscrollController();
EXPECT_FALSE(controller.IsAutoscrolling());
LocalFrame* frame = GetDocument().GetFrame();
LayoutBox* scrollable =
GetDocument().getElementById("scrollable")->GetLayoutBox();
controller.StartMiddleClickAutoscroll(
frame, scrollable, FloatPoint(15.0, 15.0), FloatPoint(15.0, 15.0));
EXPECT_TRUE(controller.IsAutoscrolling());
EXPECT_TRUE(controller.vertical_autoscroll_layout_box_);
EXPECT_TRUE(controller.horizontal_autoscroll_layout_box_);
}
} // namespace blink } // namespace blink
<!DOCTYPE HTML>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src='../../resources/gesture-util.js'></script>
<style>
body {
height: 2000px;
width: 2000px;
background: repeating-linear-gradient(
45deg,
#606dbc,
#606dbc 40px,
#465298 40px,
#465298 80px
) local;
}
#scroller {
width: 90px;
border: 2px solid black;
height: 90px;
position: absolute;
top: 50px;
overflow: auto;
background: repeating-linear-gradient(
135deg,
#bc6d60,
#bc6d60 40px,
#985246 40px,
#985246 80px
) local;
}
#contents {
height: 200px;
}
</style>
<div id="scroller">
<div id="contents"></div>
</div>
<script>
var scroller = document.getElementById('scroller');
promise_test(async () => {
assert_equals(scroller.scrollTop, 0);
await waitForCompositorCommit();
await mouseClickOn(12, 60, 1);
await mouseMoveTo(12, 260);
await mouseClickOn(12, 260, 1);
await waitFor(() => {
return scroller.scrollTop > 0;
}, "failed to scroll scroller vertically");
}, "Middle click autoscroll should scroll child if child delta can be consumed.");
promise_test(async () => {
assert_equals(document.scrollingElement.scrollLeft, 0)
await waitForCompositorCommit();
await mouseClickOn(12, 60, 1);
await mouseMoveTo(212, 60);
await mouseClickOn(212, 60, 1);
await waitFor(() => {
return document.scrollingElement.scrollLeft > 0;
}, "failed to scroll document body horizontally");
}, "Middle click autoscroll should scroll parent if child delta can not be consumed.");
</script>
\ No newline at end of file
...@@ -86,14 +86,15 @@ window.addEventListener('load', () => { ...@@ -86,14 +86,15 @@ window.addEventListener('load', () => {
// Autoscroll over the inner scroller. // Autoscroll over the inner scroller.
await autoScroll(startX, startY, endX, endY); await autoScroll(startX, startY, endX, endY);
await waitForAnimationEndTimeBased( () => { return window.scrollY; } ); await waitForAnimationEndTimeBased( () => { return window.scrollY; } );
assert_equals(frames[0].window.scrollY, 0, "Iframe frame should not scroll"); assert_equals(window.scrollY, 0, "Main frame should not scroll");
// Autoscroll over the iframe. // Autoscroll over the iframe.
startX = rect.right - 20; startX = rect.right - 20;
endX = startX; endX = startX;
await autoScroll(startX, startY, endX, endY); await autoScroll(startX, startY, endX, endY);
await waitForAnimationEndTimeBased( () => { return window.scrollY; } ); await waitForAnimationEndTimeBased( () => { return window.scrollY; } );
assert_true(window.scrollY > 0, "Main frame should not scroll"); assert_equals(window.scrollY, 0, "Main frame should not scroll");
assert_equals(frames[0].window.scrollY, 0, "IFrame must NOT scroll."); assert_equals(frames[0].window.scrollY, 0, "IFrame must NOT scroll.");
}); });
}); });
......
<!DOCTYPE html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../../resources/gesture-util.js"></script>
<style>
#container {
width:500px;
height:100px;
overflow:auto;
border:2px solid red;
padding:0px;
}
#spacer {
height:200px;
}
</style>
<ol>
<li>Middle-click inside the &lt;div&gt; with the red border below to activate autoscroll.</li>
<li>First, move the mouse such that you scroll the &lt;div&gt; in unavailable scroll direction and then in scrollable direction.</li>
<li>If bug repros, it won't scroll in available direction.</li>
</ol>
<div id="container">
<div id="spacer"></div>
</div>
<script>
window.onload = async function()
{
const container = document.getElementById("container");
const leftButton = 0;
const middleButton = 1;
promise_test (async (t) => {
await waitForCompositorCommit();
await mouseClickOn(container.offsetLeft + 10, container.offsetTop + 10, middleButton);
//Move mouse in unavailable direction first.
await mouseMoveTo(container.offsetLeft + (container.offsetWidth/2), container.offsetTop + 10);
//Move mouse in available direction to scroll the content.
await mouseMoveTo(container.offsetLeft + (container.offsetWidth/2), container.offsetTop + container.offsetHeight);
await waitFor(() => {
return container.scrollTop === container.scrollHeight - container.clientHeight;
}, "Failed to scroll container with middle-click autoscroll.");
}, "Container scrolled in available direction.");
}
</script>
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment