Commit db03912d authored by David Black's avatar David Black Committed by Commit Bot

Handle vertical scrolling with embedded WebViews.

Previously, Assistant cards (WebViews) would consume any event that
they processed. This prevented our container from scrolling effectively,
because scroll gestures would be consumed.

Now, we prevent the WebView from receiving anything other than
mouse click related events. We need to give it those events because
Assistant cards contain links.

To also support link taps via gesture, we need to route taps back
to the WebView's manually. We do this by converting tap gestures into
mouse events.

Bug: b:110039692
Change-Id: Id0d44af0651490ddf58b6e0e0c70c41fec3fa654
Reviewed-on: https://chromium-review.googlesource.com/1174961
Commit-Queue: David Black <dmblack@google.com>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583863}
parent 1b331d20
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "ui/aura/window.h"
#include "ui/aura/window_targeter.h"
#include "ui/display/display.h" #include "ui/display/display.h"
#include "ui/display/screen.h" #include "ui/display/screen.h"
#include "ui/gfx/animation/slide_animation.h" #include "ui/gfx/animation/slide_animation.h"
...@@ -40,6 +42,32 @@ constexpr int kCornerRadiusDip = 20; ...@@ -40,6 +42,32 @@ constexpr int kCornerRadiusDip = 20;
// Animation. // Animation.
constexpr int kResizeAnimationDurationMs = 250; constexpr int kResizeAnimationDurationMs = 250;
// Window properties.
DEFINE_UI_CLASS_PROPERTY_KEY(bool, kOnlyAllowMouseClickEvents, false);
// AssistantContainerEventTargeter ---------------------------------------------
class AssistantContainerEventTargeter : public aura::WindowTargeter {
public:
AssistantContainerEventTargeter() = default;
~AssistantContainerEventTargeter() override = default;
// aura::WindowTargeter:
bool SubtreeShouldBeExploredForEvent(aura::Window* window,
const ui::LocatedEvent& event) override {
if (window->GetProperty(kOnlyAllowMouseClickEvents)) {
if (event.type() != ui::ET_MOUSE_PRESSED &&
event.type() != ui::ET_MOUSE_RELEASED) {
return false;
}
}
return aura::WindowTargeter::SubtreeShouldBeExploredForEvent(window, event);
}
private:
DISALLOW_COPY_AND_ASSIGN(AssistantContainerEventTargeter);
};
// AssistantContainerLayout ---------------------------------------------------- // AssistantContainerLayout ----------------------------------------------------
// The AssistantContainerLayout calculates preferred size to fit the largest // The AssistantContainerLayout calculates preferred size to fit the largest
...@@ -142,6 +170,16 @@ AssistantContainerView::~AssistantContainerView() { ...@@ -142,6 +170,16 @@ AssistantContainerView::~AssistantContainerView() {
assistant_controller_->ui_controller()->RemoveModelObserver(this); assistant_controller_->ui_controller()->RemoveModelObserver(this);
} }
// static
void AssistantContainerView::OnlyAllowMouseClickEvents(aura::Window* window) {
window->SetProperty(kOnlyAllowMouseClickEvents, true);
}
void AssistantContainerView::AddedToWidget() {
GetWidget()->GetNativeWindow()->SetEventTargeter(
std::make_unique<AssistantContainerEventTargeter>());
}
void AssistantContainerView::ChildPreferredSizeChanged(views::View* child) { void AssistantContainerView::ChildPreferredSizeChanged(views::View* child) {
PreferredSizeChanged(); PreferredSizeChanged();
} }
......
...@@ -11,14 +11,14 @@ ...@@ -11,14 +11,14 @@
#include "ui/gfx/animation/animation_delegate.h" #include "ui/gfx/animation/animation_delegate.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h" #include "ui/views/bubble/bubble_dialog_delegate_view.h"
namespace gfx {
class SlideAnimation;
} // namespace gfx
namespace aura { namespace aura {
class Window; class Window;
} // namespace aura } // namespace aura
namespace gfx {
class SlideAnimation;
} // namespace gfx
namespace ash { namespace ash {
class AssistantController; class AssistantController;
...@@ -34,7 +34,13 @@ class AssistantContainerView : public views::BubbleDialogDelegateView, ...@@ -34,7 +34,13 @@ class AssistantContainerView : public views::BubbleDialogDelegateView,
explicit AssistantContainerView(AssistantController* assistant_controller); explicit AssistantContainerView(AssistantController* assistant_controller);
~AssistantContainerView() override; ~AssistantContainerView() override;
// Instructs the event targeter for the Assistant window to only allow mouse
// click events to reach the specified |window|. All other events will not
// be explored by |window|'s subtree for handling.
static void OnlyAllowMouseClickEvents(aura::Window* window);
// views::BubbleDialogDelegateView: // views::BubbleDialogDelegateView:
void AddedToWidget() override;
int GetDialogButtons() const override; int GetDialogButtons() const override;
void ChildPreferredSizeChanged(views::View* child) override; void ChildPreferredSizeChanged(views::View* child) override;
void PreferredSizeChanged() override; void PreferredSizeChanged() override;
......
...@@ -8,18 +8,24 @@ ...@@ -8,18 +8,24 @@
#include "ash/assistant/assistant_interaction_controller.h" #include "ash/assistant/assistant_interaction_controller.h"
#include "ash/assistant/model/assistant_response.h" #include "ash/assistant/model/assistant_response.h"
#include "ash/assistant/model/assistant_ui_element.h" #include "ash/assistant/model/assistant_ui_element.h"
#include "ash/assistant/ui/assistant_container_view.h"
#include "ash/assistant/ui/assistant_ui_constants.h" #include "ash/assistant/ui/assistant_ui_constants.h"
#include "ash/assistant/ui/main_stage/assistant_text_element_view.h" #include "ash/assistant/ui/main_stage/assistant_text_element_view.h"
#include "ash/assistant/util/animation_util.h" #include "ash/assistant/util/animation_util.h"
#include "ash/public/cpp/app_list/answer_card_contents_registry.h" #include "ash/public/cpp/app_list/answer_card_contents_registry.h"
#include "ash/shell.h"
#include "base/base64.h" #include "base/base64.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/unguessable_token.h" #include "base/unguessable_token.h"
#include "ui/aura/window.h" #include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/compositor/callback_layer_animation_observer.h" #include "ui/compositor/callback_layer_animation_observer.h"
#include "ui/compositor/layer_animation_element.h" #include "ui/compositor/layer_animation_element.h"
#include "ui/compositor/layer_animator.h" #include "ui/compositor/layer_animator.h"
#include "ui/events/event.h"
#include "ui/events/event_sink.h"
#include "ui/events/event_utils.h"
#include "ui/views/controls/native/native_view_host.h" #include "ui/views/controls/native/native_view_host.h"
#include "ui/views/layout/box_layout.h" #include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h" #include "ui/views/layout/fill_layout.h"
...@@ -50,6 +56,31 @@ constexpr base::TimeDelta kUiElementAnimationFadeOutDuration = ...@@ -50,6 +56,31 @@ constexpr base::TimeDelta kUiElementAnimationFadeOutDuration =
// WebContents. // WebContents.
constexpr char kDataUriPrefix[] = "data:text/html;base64,"; constexpr char kDataUriPrefix[] = "data:text/html;base64,";
// Helpers ---------------------------------------------------------------------
void CreateAndSendMouseClick(aura::WindowTreeHost* host,
const gfx::Point& location_in_pixels) {
ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, location_in_pixels,
location_in_pixels, ui::EventTimeForNow(),
ui::EF_LEFT_MOUSE_BUTTON,
ui::EF_LEFT_MOUSE_BUTTON);
// Send an ET_MOUSE_PRESSED event.
ui::EventDispatchDetails details =
host->event_sink()->OnEventFromSource(&press_event);
if (details.dispatcher_destroyed)
return;
ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, location_in_pixels,
location_in_pixels, ui::EventTimeForNow(),
ui::EF_LEFT_MOUSE_BUTTON,
ui::EF_LEFT_MOUSE_BUTTON);
// Send an ET_MOUSE_RELEASED event.
ignore_result(host->event_sink()->OnEventFromSource(&release_event));
}
// CardElementViewHolder ------------------------------------------------------- // CardElementViewHolder -------------------------------------------------------
// This class uses a child widget to host a view for a card element that has an // This class uses a child widget to host a view for a card element that has an
...@@ -84,6 +115,49 @@ class CardElementViewHolder : public views::NativeViewHost, ...@@ -84,6 +115,49 @@ class CardElementViewHolder : public views::NativeViewHost,
// views::NativeViewHost: // views::NativeViewHost:
const char* GetClassName() const override { return "CardElementViewHolder"; } const char* GetClassName() const override { return "CardElementViewHolder"; }
void OnGestureEvent(ui::GestureEvent* event) override {
// We need to route GESTURE_TAP events to our Assistant card because links
// should be tappable. The Assistant card window will not receive gesture
// events so we convert the gesture into analogous mouse events.
if (event->type() != ui::ET_GESTURE_TAP) {
views::View::OnGestureEvent(event);
return;
}
// Consume the original event.
event->StopPropagation();
event->SetHandled();
aura::Window* root_window = GetWidget()->GetNativeWindow()->GetRootWindow();
// Get the appropriate event location in pixels.
gfx::Point location_in_pixels = event->location();
ConvertPointToScreen(this, &location_in_pixels);
aura::WindowTreeHost* host = root_window->GetHost();
host->ConvertDIPToPixels(&location_in_pixels);
wm::CursorManager* cursor_manager = Shell::Get()->cursor_manager();
// We want to prevent the cursor from changing its visibility during our
// mouse events because we are actually handling a gesture. To accomplish
// this, we cache the cursor's visibility and lock it in its current state.
const bool visible = cursor_manager->IsCursorVisible();
cursor_manager->LockCursor();
CreateAndSendMouseClick(host, location_in_pixels);
// Restore the original cursor visibility that may have changed during our
// sequence of mouse events. This change would not have been perceivable to
// the user since it occurred within our lock.
if (visible)
cursor_manager->ShowCursor();
else
cursor_manager->HideCursor();
// Release our cursor lock.
cursor_manager->UnlockCursor();
}
// views::ViewObserver: // views::ViewObserver:
void OnViewPreferredSizeChanged(views::View* view) override { void OnViewPreferredSizeChanged(views::View* view) override {
contents_view_->SetPreferredSize(view->GetPreferredSize()); contents_view_->SetPreferredSize(view->GetPreferredSize());
...@@ -92,6 +166,20 @@ class CardElementViewHolder : public views::NativeViewHost, ...@@ -92,6 +166,20 @@ class CardElementViewHolder : public views::NativeViewHost,
void Attach() { void Attach() {
views::NativeViewHost::Attach(child_widget_->GetNativeView()); views::NativeViewHost::Attach(child_widget_->GetNativeView());
aura::Window* const top_level_window = native_view()->GetToplevelWindow();
// Find the window for the Assistant card.
aura::Window* window = native_view();
while (window->parent() != top_level_window)
window = window->parent();
// The Assistant card window will consume all events that enter it. This
// prevents us from being able to scroll the native view hierarchy
// vertically. As such, we need to prevent the Assistant card window from
// receiving events it doesn't need. It needs mouse click events for
// handling links.
AssistantContainerView::OnlyAllowMouseClickEvents(window);
} }
private: private:
......
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