Commit 0bfab86a authored by Yi Gu's avatar Yi Gu Committed by Commit Bot

[VizHitTesting] Propagate pointer-events:none to RemoteFrame

When a remote frame has an local ancestor with pointer-events: none, the
frame and all its descendants should be excluded in hit testing.

See https://docs.google.com/document/d/1xN1bRigzahOgUM4cEuyHGhe069TRc5-0rXz1JZsgCP8/edit?usp=sharing

Bug: 1002245
Change-Id: I9e2f86b1d1e6727a66499639994d269d301f1a34
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1836954Reviewed-by: default avatarJeremy Roman <jbroman@chromium.org>
Reviewed-by: default avatarKen Buchanan <kenrb@chromium.org>
Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Commit-Queue: Yi Gu <yigu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#705125}
parent 974cd5d2
...@@ -2810,6 +2810,73 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, ...@@ -2810,6 +2810,73 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
kHitTestTolerance); kHitTestTolerance);
} }
IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
PointerEventsNoneWithNestedSameOriginIFrame) {
GURL main_url(embedded_test_server()->GetURL(
"/frame_tree/page_with_same_origin_nested_frames.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_url));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()
->root();
ASSERT_EQ(1U, root->child_count());
RenderWidgetHostViewBase* root_view = static_cast<RenderWidgetHostViewBase*>(
root->current_frame_host()->GetRenderWidgetHost()->GetView());
EXPECT_EQ(
" Site A ------------ proxies for B\n"
" +--Site A ------- proxies for B\n"
" +--Site B -- proxies for A\n"
"Where A = http://127.0.0.1/\n"
" B = http://baz.com/",
DepictFrameTree(root));
FrameTreeNode* child_node = root->child_at(0);
FrameTreeNode* grandchild_node = child_node->child_at(0);
// This is to make sure that the hit_test_data is clean before running the
// hit_test_data_change_observer.
WaitForHitTestData(child_node->current_frame_host());
WaitForHitTestData(grandchild_node->current_frame_host());
HitTestRegionObserver hit_test_data_change_observer(
root_view->GetRootFrameSinkId());
hit_test_data_change_observer.WaitForHitTestData();
EXPECT_TRUE(ExecuteScript(web_contents(),
"document.getElementById('wrapper').style."
"pointerEvents = 'none';"));
hit_test_data_change_observer.WaitForHitTestDataChange();
MainThreadFrameObserver observer(
root->current_frame_host()->GetRenderWidgetHost());
observer.Wait();
// ------------------------
// root 50px
// ---------------------
// |child 50px |
// 50px| -------------- |
// |50px| grand_child ||
// | | ||
// | |-------------||
// ---------------------
// DispatchMouseEventAndWaitUntilDispatch will make sure the mouse event goes
// to the right frame. Create a listener for the grandchild to verify that it
// does not receive the event. No need to create one for the child because
// root and child are on the same process.
RenderWidgetHostMouseEventMonitor grandchild_frame_monitor(
grandchild_node->current_frame_host()->GetRenderWidgetHost());
// Since child has pointer-events: none, (125, 125) should be claimed by root.
DispatchMouseEventAndWaitUntilDispatch(web_contents(), root_view,
gfx::PointF(125, 125), root_view,
gfx::PointF(125, 125));
EXPECT_FALSE(grandchild_frame_monitor.EventWasReceived());
}
IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
PointerEventsNoneWithNestedOOPIF) { PointerEventsNoneWithNestedOOPIF) {
GURL main_url(embedded_test_server()->GetURL( GURL main_url(embedded_test_server()->GetURL(
...@@ -2854,12 +2921,6 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, ...@@ -2854,12 +2921,6 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
root->current_frame_host()->GetRenderWidgetHost()); root->current_frame_host()->GetRenderWidgetHost());
observer.Wait(); observer.Wait();
// Create listeners for mouse events.
RenderWidgetHostMouseEventMonitor main_frame_monitor(
root->current_frame_host()->GetRenderWidgetHost());
RenderWidgetHostMouseEventMonitor child_frame_monitor(
child_node->current_frame_host()->GetRenderWidgetHost());
// ------------------------ // ------------------------
// root 50px // root 50px
// --------------------- // ---------------------
...@@ -2869,30 +2930,17 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, ...@@ -2869,30 +2930,17 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
// | | || // | | ||
// | |-------------|| // | |-------------||
// --------------------- // ---------------------
//
// Since child has pointer-events: none, (125, 125) should be claimed by root.
blink::WebMouseEvent mouse_event(
blink::WebInputEvent::kMouseDown, blink::WebInputEvent::kNoModifiers,
blink::WebInputEvent::GetStaticTimeStampForTests());
mouse_event.button = blink::WebPointerProperties::Button::kLeft;
SetWebEventPositions(&mouse_event, gfx::Point(125, 125), root_view);
mouse_event.click_count = 1;
main_frame_monitor.ResetEventReceived(); // DispatchMouseEventAndWaitUntilDispatch will make sure the mouse event goes
child_frame_monitor.ResetEventReceived(); // to the right frame. Create a listener for the child to verify that it does
// not receive the event.
InputEventAckWaiter waiter(root->current_frame_host()->GetRenderWidgetHost(), RenderWidgetHostMouseEventMonitor child_frame_monitor(
blink::WebInputEvent::kMouseDown); child_node->current_frame_host()->GetRenderWidgetHost());
RenderWidgetHostInputEventRouter* router =
web_contents()->GetInputEventRouter();
router->RouteMouseEvent(root_view, &mouse_event, ui::LatencyInfo());
waiter.Wait();
EXPECT_TRUE(main_frame_monitor.EventWasReceived()); // Since child has pointer-events: none, (125, 125) should be claimed by root.
EXPECT_NEAR(125, main_frame_monitor.event().PositionInWidget().x, DispatchMouseEventAndWaitUntilDispatch(web_contents(), root_view,
kHitTestTolerance); gfx::PointF(125, 125), root_view,
EXPECT_NEAR(125, main_frame_monitor.event().PositionInWidget().y, gfx::PointF(125, 125));
kHitTestTolerance);
EXPECT_FALSE(child_frame_monitor.EventWasReceived()); EXPECT_FALSE(child_frame_monitor.EventWasReceived());
} }
......
<!DOCTYPE html>
<style>
iframe {
top: 50px;
left: 50px;
width: 200px;
height: 200px;
}
</style>
<html>
<body>
<div id="wrapper">
<iframe src="page_with_positioned_frame.html"></iframe>
</div>
</body>
</html>
...@@ -261,6 +261,27 @@ void Frame::UpdateInheritedEffectiveTouchActionIfPossible() { ...@@ -261,6 +261,27 @@ void Frame::UpdateInheritedEffectiveTouchActionIfPossible() {
} }
} }
void Frame::UpdateVisibleToHitTesting() {
bool parent_visible_to_hit_testing = true;
if (auto* parent = Tree().Parent())
parent_visible_to_hit_testing = parent->GetVisibleToHitTesting();
bool self_visible_to_hit_testing = true;
if (auto* local_owner = DynamicTo<HTMLFrameOwnerElement>(owner_.Get())) {
self_visible_to_hit_testing =
local_owner->GetLayoutObject()
? local_owner->GetLayoutObject()->Style()->VisibleToHitTesting()
: true;
}
bool visible_to_hit_testing =
parent_visible_to_hit_testing && self_visible_to_hit_testing;
bool changed = visible_to_hit_testing_ != visible_to_hit_testing;
visible_to_hit_testing_ = visible_to_hit_testing;
if (changed)
DidChangeVisibleToHitTesting();
}
const std::string& Frame::ToTraceValue() { const std::string& Frame::ToTraceValue() {
// token's ToString() is latin1. // token's ToString() is latin1.
if (!trace_value_) if (!trace_value_)
......
...@@ -234,6 +234,9 @@ class CORE_EXPORT Frame : public GarbageCollected<Frame> { ...@@ -234,6 +234,9 @@ class CORE_EXPORT Frame : public GarbageCollected<Frame> {
return *window_agent_factory_; return *window_agent_factory_;
} }
bool GetVisibleToHitTesting() const { return visible_to_hit_testing_; }
void UpdateVisibleToHitTesting();
protected: protected:
// |inheriting_agent_factory| should basically be set to the parent frame or // |inheriting_agent_factory| should basically be set to the parent frame or
// opener's WindowAgentFactory. Pass nullptr if the frame is isolated from // opener's WindowAgentFactory. Pass nullptr if the frame is isolated from
...@@ -260,6 +263,8 @@ class CORE_EXPORT Frame : public GarbageCollected<Frame> { ...@@ -260,6 +263,8 @@ class CORE_EXPORT Frame : public GarbageCollected<Frame> {
return lifecycle_.GetState() == FrameLifecycle::kDetached; return lifecycle_.GetState() == FrameLifecycle::kDetached;
} }
virtual void DidChangeVisibleToHitTesting() = 0;
mutable FrameTree tree_node_; mutable FrameTree tree_node_;
Member<Page> page_; Member<Page> page_;
...@@ -279,6 +284,8 @@ class CORE_EXPORT Frame : public GarbageCollected<Frame> { ...@@ -279,6 +284,8 @@ class CORE_EXPORT Frame : public GarbageCollected<Frame> {
TouchAction inherited_effective_touch_action_ = TouchAction::kTouchActionAuto; TouchAction inherited_effective_touch_action_ = TouchAction::kTouchActionAuto;
bool visible_to_hit_testing_ = true;
private: private:
Member<FrameClient> client_; Member<FrameClient> client_;
const Member<WindowProxyManager> window_proxy_manager_; const Member<WindowProxyManager> window_proxy_manager_;
......
...@@ -1787,4 +1787,13 @@ void LocalFrame::EvictFromBackForwardCache() { ...@@ -1787,4 +1787,13 @@ void LocalFrame::EvictFromBackForwardCache() {
Client()->EvictFromBackForwardCache(); Client()->EvictFromBackForwardCache();
} }
void LocalFrame::DidChangeVisibleToHitTesting() {
// LayoutEmbeddedContent does not propagate style updates to descendants.
// Need to update the field manually.
for (Frame* child = Tree().FirstChild(); child;
child = child->Tree().NextSibling()) {
child->UpdateVisibleToHitTesting();
}
}
} // namespace blink } // namespace blink
...@@ -447,6 +447,8 @@ class CORE_EXPORT LocalFrame final : public Frame, ...@@ -447,6 +447,8 @@ class CORE_EXPORT LocalFrame final : public Frame,
void SetIsCapturingMediaCallback(IsCapturingMediaCallback callback); void SetIsCapturingMediaCallback(IsCapturingMediaCallback callback);
bool IsCapturingMedia() const; bool IsCapturingMedia() const;
void DidChangeVisibleToHitTesting() override;
private: private:
friend class FrameNavigationDisabler; friend class FrameNavigationDisabler;
......
...@@ -46,6 +46,7 @@ RemoteFrame::RemoteFrame(RemoteFrameClient* client, ...@@ -46,6 +46,7 @@ RemoteFrame::RemoteFrame(RemoteFrameClient* client,
dom_window_ = MakeGarbageCollected<RemoteDOMWindow>(*this); dom_window_ = MakeGarbageCollected<RemoteDOMWindow>(*this);
UpdateInertIfPossible(); UpdateInertIfPossible();
UpdateInheritedEffectiveTouchActionIfPossible(); UpdateInheritedEffectiveTouchActionIfPossible();
UpdateVisibleToHitTesting();
Initialize(); Initialize();
} }
...@@ -214,7 +215,7 @@ RemoteFrameClient* RemoteFrame::Client() const { ...@@ -214,7 +215,7 @@ RemoteFrameClient* RemoteFrame::Client() const {
return static_cast<RemoteFrameClient*>(Frame::Client()); return static_cast<RemoteFrameClient*>(Frame::Client());
} }
void RemoteFrame::PointerEventsChanged() { void RemoteFrame::DidChangeVisibleToHitTesting() {
if (!cc_layer_ || !is_surface_layer_) if (!cc_layer_ || !is_surface_layer_)
return; return;
...@@ -226,9 +227,9 @@ bool RemoteFrame::IsIgnoredForHitTest() const { ...@@ -226,9 +227,9 @@ bool RemoteFrame::IsIgnoredForHitTest() const {
HTMLFrameOwnerElement* owner = DeprecatedLocalOwner(); HTMLFrameOwnerElement* owner = DeprecatedLocalOwner();
if (!owner || !owner->GetLayoutObject()) if (!owner || !owner->GetLayoutObject())
return false; return false;
return owner->OwnerType() == FrameOwnerElementType::kPortal || return owner->OwnerType() == FrameOwnerElementType::kPortal ||
(owner->GetLayoutObject()->Style()->PointerEvents() == !visible_to_hit_testing_;
EPointerEvents::kNone);
} }
void RemoteFrame::SetCcLayer(cc::Layer* cc_layer, void RemoteFrame::SetCcLayer(cc::Layer* cc_layer,
...@@ -243,7 +244,10 @@ void RemoteFrame::SetCcLayer(cc::Layer* cc_layer, ...@@ -243,7 +244,10 @@ void RemoteFrame::SetCcLayer(cc::Layer* cc_layer,
is_surface_layer_ = is_surface_layer; is_surface_layer_ = is_surface_layer;
if (cc_layer_) { if (cc_layer_) {
GraphicsLayer::RegisterContentsLayer(cc_layer_); GraphicsLayer::RegisterContentsLayer(cc_layer_);
PointerEventsChanged(); if (is_surface_layer) {
static_cast<cc::SurfaceLayer*>(cc_layer_)->SetHasPointerEventsNone(
IsIgnoredForHitTest());
}
} }
To<HTMLFrameOwnerElement>(Owner())->SetNeedsCompositingUpdate(); To<HTMLFrameOwnerElement>(Owner())->SetNeedsCompositingUpdate();
......
...@@ -64,9 +64,10 @@ class CORE_EXPORT RemoteFrame final : public Frame { ...@@ -64,9 +64,10 @@ class CORE_EXPORT RemoteFrame final : public Frame {
RemoteFrameClient* Client() const; RemoteFrameClient* Client() const;
void PointerEventsChanged();
bool IsIgnoredForHitTest() const; bool IsIgnoredForHitTest() const;
void DidChangeVisibleToHitTesting() override;
private: private:
// Frame protected overrides: // Frame protected overrides:
void DetachImpl(FrameDetachType) override; void DetachImpl(FrameDetachType) override;
......
...@@ -308,11 +308,6 @@ void HTMLFrameOwnerElement::UpdateContainerPolicy(Vector<String>* messages) { ...@@ -308,11 +308,6 @@ void HTMLFrameOwnerElement::UpdateContainerPolicy(Vector<String>* messages) {
} }
} }
void HTMLFrameOwnerElement::PointerEventsChanged() {
if (auto* remote_frame = DynamicTo<RemoteFrame>(ContentFrame()))
remote_frame->PointerEventsChanged();
}
void HTMLFrameOwnerElement::FrameOwnerPropertiesChanged() { void HTMLFrameOwnerElement::FrameOwnerPropertiesChanged() {
// Don't notify about updates if ContentFrame() is null, for example when // Don't notify about updates if ContentFrame() is null, for example when
// the subframe hasn't been created yet; or if we are in the middle of // the subframe hasn't been created yet; or if we are in the middle of
......
...@@ -119,10 +119,6 @@ class CORE_EXPORT HTMLFrameOwnerElement : public HTMLElement, ...@@ -119,10 +119,6 @@ class CORE_EXPORT HTMLFrameOwnerElement : public HTMLElement,
// For unit tests, manually trigger the UpdateContainerPolicy method. // For unit tests, manually trigger the UpdateContainerPolicy method.
void UpdateContainerPolicyForTests() { UpdateContainerPolicy(); } void UpdateContainerPolicyForTests() { UpdateContainerPolicy(); }
// This function is to notify ChildFrameCompositor of pointer-events changes
// of an OOPIF.
void PointerEventsChanged();
void CancelPendingLazyLoad(); void CancelPendingLazyLoad();
void ParseAttribute(const AttributeModificationParams&) override; void ParseAttribute(const AttributeModificationParams&) override;
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "third_party/blink/renderer/core/frame/embedded_content_view.h" #include "third_party/blink/renderer/core/frame/embedded_content_view.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/remote_frame.h"
#include "third_party/blink/renderer/core/frame/remote_frame_view.h" #include "third_party/blink/renderer/core/frame/remote_frame_view.h"
#include "third_party/blink/renderer/core/html/html_frame_element_base.h" #include "third_party/blink/renderer/core/html/html_frame_element_base.h"
#include "third_party/blink/renderer/core/html/html_plugin_element.h" #include "third_party/blink/renderer/core/html/html_plugin_element.h"
...@@ -254,20 +255,28 @@ void LayoutEmbeddedContent::StyleDidChange(StyleDifference diff, ...@@ -254,20 +255,28 @@ void LayoutEmbeddedContent::StyleDidChange(StyleDifference diff,
const ComputedStyle* old_style) { const ComputedStyle* old_style) {
LayoutReplaced::StyleDidChange(diff, old_style); LayoutReplaced::StyleDidChange(diff, old_style);
if (!old_style || Style()->PointerEvents() != old_style->PointerEvents()) { if (EmbeddedContentView* embedded_content_view = GetEmbeddedContentView()) {
if (auto* frame_owner = DynamicTo<HTMLFrameOwnerElement>(GetNode())) if (StyleRef().Visibility() != EVisibility::kVisible) {
frame_owner->PointerEventsChanged(); embedded_content_view->Hide();
} else {
embedded_content_view->Show();
}
} }
EmbeddedContentView* embedded_content_view = GetEmbeddedContentView(); if (old_style &&
if (!embedded_content_view) StyleRef().VisibleToHitTesting() == old_style->VisibleToHitTesting()) {
return; return;
if (StyleRef().Visibility() != EVisibility::kVisible) {
embedded_content_view->Hide();
} else {
embedded_content_view->Show();
} }
auto* frame_owner = DynamicTo<HTMLFrameOwnerElement>(GetNode());
if (!frame_owner)
return;
auto* frame = frame_owner->ContentFrame();
if (!frame)
return;
frame->UpdateVisibleToHitTesting();
} }
void LayoutEmbeddedContent::UpdateLayout() { void LayoutEmbeddedContent::UpdateLayout() {
......
...@@ -250,6 +250,7 @@ _CONFIG = [ ...@@ -250,6 +250,7 @@ _CONFIG = [
# cc::Layers. # cc::Layers.
'cc::Layer', 'cc::Layer',
'cc::PictureLayer', 'cc::PictureLayer',
'cc::SurfaceLayer',
# cc::Layer helper data structs. # cc::Layer helper data structs.
'cc::ElementId', 'cc::ElementId',
......
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