Commit ddcc3f7e authored by David Bokan's avatar David Bokan Committed by Commit Bot

Fix IntersectionObserver when using ForceZeroLayoutHeight

ForceZeroLayoutHeight is a Blink setting that's an Android WebView
quirk. When set, Blink will set the height on the initial containing
block (i.e. LayoutView) in the main frame to be 0.

In the linked bug, we never see the video frame because the iframe it's
hosted in is rendering throttled so it never paints. The reason it's
throttled is because the IntersectionObserver that determines its
visibility on screen uses the LayoutView's size. Because of the
ForceZeroLayoutHeight setting, the LayoutView size is empty so the
intersection is also always empty so we conclude the iframe is always
off screen.

The fix here is to carve out an exception in IntersectionObserver when
this setting is enabled. In that case, for the root element, we use the
FrameView's geometry instead.

Bug: 824730
Change-Id: Ief24437135d19120a4e9bba8a2f1777ac448eea7
Reviewed-on: https://chromium-review.googlesource.com/978624Reviewed-by: default avatarStefan Zager <szager@chromium.org>
Commit-Queue: David Bokan <bokan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#545812}
parent 69621ae3
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "core/testing/sim/SimCompositor.h" #include "core/testing/sim/SimCompositor.h"
#include "core/testing/sim/SimRequest.h" #include "core/testing/sim/SimRequest.h"
#include "core/testing/sim/SimTest.h" #include "core/testing/sim/SimTest.h"
#include "platform/geometry/FloatRect.h"
#include "platform/testing/UnitTestHelpers.h" #include "platform/testing/UnitTestHelpers.h"
#include "platform/wtf/Time.h" #include "platform/wtf/Time.h"
...@@ -22,12 +23,23 @@ class TestIntersectionObserverDelegate : public IntersectionObserverDelegate { ...@@ -22,12 +23,23 @@ class TestIntersectionObserverDelegate : public IntersectionObserverDelegate {
public: public:
TestIntersectionObserverDelegate(Document& document) TestIntersectionObserverDelegate(Document& document)
: document_(document), call_count_(0) {} : document_(document), call_count_(0) {}
void Deliver(const HeapVector<Member<IntersectionObserverEntry>>&, void Deliver(const HeapVector<Member<IntersectionObserverEntry>>& entry,
IntersectionObserver&) override { IntersectionObserver&) override {
call_count_++; call_count_++;
if (!entry.back()->intersectionRect()) {
last_intersection_rect_ = FloatRect();
} else {
last_intersection_rect_ =
FloatRect(entry.back()->intersectionRect()->x(),
entry.back()->intersectionRect()->y(),
entry.back()->intersectionRect()->width(),
entry.back()->intersectionRect()->height());
}
} }
ExecutionContext* GetExecutionContext() const override { return document_; } ExecutionContext* GetExecutionContext() const override { return document_; }
int CallCount() const { return call_count_; } int CallCount() const { return call_count_; }
FloatRect LastIntersectionRect() const { return last_intersection_rect_; }
void Trace(blink::Visitor* visitor) { void Trace(blink::Visitor* visitor) {
IntersectionObserverDelegate::Trace(visitor); IntersectionObserverDelegate::Trace(visitor);
...@@ -35,6 +47,7 @@ class TestIntersectionObserverDelegate : public IntersectionObserverDelegate { ...@@ -35,6 +47,7 @@ class TestIntersectionObserverDelegate : public IntersectionObserverDelegate {
} }
private: private:
FloatRect last_intersection_rect_;
Member<Document> document_; Member<Document> document_;
int call_count_; int call_count_;
}; };
...@@ -163,4 +176,62 @@ TEST_F(IntersectionObserverTest, DisconnectClearsNotifications) { ...@@ -163,4 +176,62 @@ TEST_F(IntersectionObserverTest, DisconnectClearsNotifications) {
EXPECT_EQ(observer_delegate->CallCount(), 1); EXPECT_EQ(observer_delegate->CallCount(), 1);
} }
TEST_F(IntersectionObserverTest, RootIntersectionWithForceZeroLayoutHeight) {
WebView().GetSettings()->SetForceZeroLayoutHeight(true);
WebView().Resize(WebSize(800, 600));
SimRequest main_resource("https://example.com/", "text/html");
LoadURL("https://example.com/");
main_resource.Complete(R"HTML(
<!DOCTYPE html>
<style>
body {
margin: 0;
height: 2000px;
}
#target {
width: 100px;
height: 100px;
position: absolute;
top: 1000px;
left: 200px;
}
</style>
<div id='target'></div>
)HTML");
IntersectionObserverInit observer_init;
DummyExceptionStateForTesting exception_state;
TestIntersectionObserverDelegate* observer_delegate =
new TestIntersectionObserverDelegate(GetDocument());
IntersectionObserver* observer = IntersectionObserver::Create(
observer_init, *observer_delegate, exception_state);
ASSERT_FALSE(exception_state.HadException());
Element* target = GetDocument().getElementById("target");
ASSERT_TRUE(target);
observer->observe(target, exception_state);
Compositor().BeginFrame();
testing::RunPendingTasks();
ASSERT_EQ(observer_delegate->CallCount(), 1);
EXPECT_TRUE(observer_delegate->LastIntersectionRect().IsEmpty());
GetDocument().View()->LayoutViewportScrollableArea()->SetScrollOffset(
ScrollOffset(0, 600), kProgrammaticScroll);
Compositor().BeginFrame();
testing::RunPendingTasks();
ASSERT_EQ(observer_delegate->CallCount(), 2);
EXPECT_FALSE(observer_delegate->LastIntersectionRect().IsEmpty());
EXPECT_EQ(FloatRect(200, 400, 100, 100),
observer_delegate->LastIntersectionRect());
GetDocument().View()->LayoutViewportScrollableArea()->SetScrollOffset(
ScrollOffset(0, 1200), kProgrammaticScroll);
Compositor().BeginFrame();
testing::RunPendingTasks();
ASSERT_EQ(observer_delegate->CallCount(), 3);
EXPECT_TRUE(observer_delegate->LastIntersectionRect().IsEmpty());
}
} // namespace blink } // namespace blink
...@@ -6,9 +6,11 @@ ...@@ -6,9 +6,11 @@
#include "core/frame/LocalFrame.h" #include "core/frame/LocalFrame.h"
#include "core/frame/LocalFrameView.h" #include "core/frame/LocalFrameView.h"
#include "core/frame/Settings.h"
#include "core/html/HTMLFrameOwnerElement.h" #include "core/html/HTMLFrameOwnerElement.h"
#include "core/layout/LayoutBox.h" #include "core/layout/LayoutBox.h"
#include "core/layout/LayoutView.h" #include "core/layout/LayoutView.h"
#include "core/page/Page.h"
#include "core/paint/PaintLayer.h" #include "core/paint/PaintLayer.h"
namespace blink { namespace blink {
...@@ -113,9 +115,19 @@ void IntersectionGeometry::InitializeTargetRect() { ...@@ -113,9 +115,19 @@ void IntersectionGeometry::InitializeTargetRect() {
void IntersectionGeometry::InitializeRootRect() { void IntersectionGeometry::InitializeRootRect() {
if (root_->IsLayoutView() && if (root_->IsLayoutView() &&
!RuntimeEnabledFeatures::RootLayerScrollingEnabled()) { !RuntimeEnabledFeatures::RootLayerScrollingEnabled()) {
root_rect_ = root_rect_ = LayoutRect(root_->GetFrameView()->VisibleContentRect());
LayoutRect(ToLayoutView(root_)->GetFrameView()->VisibleContentRect());
root_->MapToVisualRectInAncestorSpace(nullptr, root_rect_); root_->MapToVisualRectInAncestorSpace(nullptr, root_rect_);
} else if (root_->IsLayoutView() && root_->GetDocument().IsInMainFrame() &&
root_->GetDocument()
.GetPage()
->GetSettings()
.GetForceZeroLayoutHeight()) {
// The ForceZeroLayoutHeight quirk setting is used in Android WebView for
// compatibility and sets the initial-containing-block's (a.k.a.
// LayoutView) height to 0. Thus, we can't use its size for intersection
// testing. Use the FrameView geometry instead.
root_rect_ = LayoutRect(
LayoutPoint(), LayoutSize(root_->GetFrameView()->VisibleContentSize()));
} else if (root_->IsBox() && root_->HasOverflowClip()) { } else if (root_->IsBox() && root_->HasOverflowClip()) {
root_rect_ = LayoutRect(ToLayoutBox(root_)->ContentBoxRect()); root_rect_ = LayoutRect(ToLayoutBox(root_)->ContentBoxRect());
} else { } else {
......
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