Absolute positioned element is not placed properly when parent becomes the containing block.
https://bugs.webkit.org/show_bug.cgi?id=157455
<rdar://problem/26212568>
Reviewed by Simon Fraser.
When a container becomes a containing block, we need to check if there are any positioned boxes in its subtree
in order to "re-parent" them. It basically means that we remove them from RenderBlock::positionedDescendants map
and they'll get re-inserted during the next layout correctly.
This patch fixes the case when a container becomes the containing block by setting the transform property and its positioned
child gets misplaced.
Source/WebCore:
Test: fast/block/containing-block-changes.html
* rendering/RenderBlock.cpp:
(WebCore::RenderBlock::removePositionedObjectsIfNeeded):
(WebCore::RenderBlock::styleWillChange):
* rendering/RenderBlock.h:
LayoutTests:
* fast/block/containing-block-changes-expected.html: Added.
* fast/block/containing-block-changes.html: Added.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@200736 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 917ed8f..5808437 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,20 @@
+2016-05-11 Zalan Bujtas <zalan@apple.com>
+
+ Absolute positioned element is not placed properly when parent becomes the containing block.
+ https://bugs.webkit.org/show_bug.cgi?id=157455
+ <rdar://problem/26212568>
+
+ Reviewed by Simon Fraser.
+
+ When a container becomes a containing block, we need to check if there are any positioned boxes in its subtree
+ in order to "re-parent" them. It basically means that we remove them from RenderBlock::positionedDescendants map
+ and they'll get re-inserted during the next layout correctly.
+ This patch fixes the case when a container becomes the containing block by setting the transform property and its positioned
+ child gets misplaced.
+
+ * fast/block/containing-block-changes-expected.html: Added.
+ * fast/block/containing-block-changes.html: Added.
+
2016-05-11 Ryosuke Niwa <rniwa@webkit.org>
Add a failing expectation on iOS for the test added in r200712
diff --git a/LayoutTests/fast/block/containing-block-changes-expected.html b/LayoutTests/fast/block/containing-block-changes-expected.html
new file mode 100644
index 0000000..99026dd
--- /dev/null
+++ b/LayoutTests/fast/block/containing-block-changes-expected.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>This tests that we properly reparent containing block descendants.</title>
+<style>
+.container {
+ position: relative;
+ height: 20px;
+ width: 20px;
+ border: 10px solid gray;
+}
+
+.indented {
+ left: 10px;
+}
+
+.box {
+ left: 0px;
+ top: 0px;
+ height: 10px;
+ width: 10px;
+ background-color: blue;
+}
+</style>
+</head>
+<body>
+<div class=box style="position: absolute"></div>
+<div class="container indented"><div class=box></div></div>
+<div class=container><div class=box></div></div>
+<div class="container indented"><div class=box></div></div>
+<div class="container indented"><div class=box></div></div>
+<div class="container indented"><div class=box></div></div>
+<div class=container><div class=box></div></div>
+<div class=container></div>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/fast/block/containing-block-changes.html b/LayoutTests/fast/block/containing-block-changes.html
new file mode 100644
index 0000000..0d63281
--- /dev/null
+++ b/LayoutTests/fast/block/containing-block-changes.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>This tests that we properly reparent containing block descendants.</title>
+<style>
+ .translate {
+ transform: translateX(10px);
+ }
+
+ .positioned {
+ position: relative;
+ }
+
+ .container {
+ height: 20px;
+ width: 20px;
+ border: 10px solid gray;
+ }
+
+ .box {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ height: 10px;
+ width: 10px;
+ background-color: blue;
+ }
+</style>
+</head>
+<body>
+<div class="container"><div class="box"></div></div>
+<div class="container"><div class="box"></div></div>
+<div class="container"><div class="box"></div></div>
+<div class="container"><div class="box"></div></div>
+<div class="container"><div class="box"></div></div>
+<div class="container"><div class="box"></div></div>
+<div class="container"><div class="box"></div></div>
+<script>
+if (window.testRunner)
+ testRunner.waitUntilDone();
+
+setTimeout(function() {
+ document.getElementsByClassName("container")[0].classList.toggle("translate");
+ document.getElementsByClassName("container")[1].classList.toggle("positioned");
+ document.getElementsByClassName("container")[2].classList.toggle("translate");
+ document.getElementsByClassName("container")[3].classList.toggle("positioned");
+ document.getElementsByClassName("container")[4].classList.toggle("positioned");
+ document.getElementsByClassName("container")[5].classList.toggle("translate");
+ document.getElementsByClassName("container")[6].classList.toggle("translate");
+ document.body.offsetHeight;
+ setTimeout(function() {
+ document.getElementsByClassName("container")[2].classList.toggle("positioned");
+ document.getElementsByClassName("container")[3].classList.toggle("translate");
+ document.getElementsByClassName("container")[4].classList.toggle("translate");
+ document.getElementsByClassName("container")[5].classList.toggle("positioned");
+ document.getElementsByClassName("container")[6].classList.toggle("positioned");
+ document.body.offsetHeight;
+ setTimeout(function() {
+ document.getElementsByClassName("container")[4].classList.toggle("positioned");
+ document.getElementsByClassName("container")[5].classList.toggle("translate");
+ document.getElementsByClassName("container")[6].classList.toggle("translate");
+ document.body.offsetHeight;
+ setTimeout(function() {
+ document.getElementsByClassName("container")[6].classList.toggle("positioned");
+ document.body.offsetHeight;
+ if (window.testRunner)
+ testRunner.notifyDone();
+ }, 0);
+ }, 0);
+ }, 0);
+}, 0);
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 674aa29..0367586 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,24 @@
+2016-05-11 Zalan Bujtas <zalan@apple.com>
+
+ Absolute positioned element is not placed properly when parent becomes the containing block.
+ https://bugs.webkit.org/show_bug.cgi?id=157455
+ <rdar://problem/26212568>
+
+ Reviewed by Simon Fraser.
+
+ When a container becomes a containing block, we need to check if there are any positioned boxes in its subtree
+ in order to "re-parent" them. It basically means that we remove them from RenderBlock::positionedDescendants map
+ and they'll get re-inserted during the next layout correctly.
+ This patch fixes the case when a container becomes the containing block by setting the transform property and its positioned
+ child gets misplaced.
+
+ Test: fast/block/containing-block-changes.html
+
+ * rendering/RenderBlock.cpp:
+ (WebCore::RenderBlock::removePositionedObjectsIfNeeded):
+ (WebCore::RenderBlock::styleWillChange):
+ * rendering/RenderBlock.h:
+
2016-05-11 Commit Queue <commit-queue@webkit.org>
Unreviewed, rolling out r200700, r200703, and r200713.
diff --git a/Source/WebCore/rendering/RenderBlock.cpp b/Source/WebCore/rendering/RenderBlock.cpp
index a90b5e8..cac5b02 100644
--- a/Source/WebCore/rendering/RenderBlock.cpp
+++ b/Source/WebCore/rendering/RenderBlock.cpp
@@ -236,38 +236,45 @@
return gRareDataMap ? gRareDataMap->contains(this) : false;
}
+void RenderBlock::removePositionedObjectsIfNeeded(const RenderStyle& oldStyle, const RenderStyle& newStyle)
+{
+ bool hadTransform = oldStyle.hasTransformRelatedProperty();
+ bool willHaveTransform = newStyle.hasTransformRelatedProperty();
+ if (oldStyle.position() == newStyle.position() && hadTransform == willHaveTransform)
+ return;
+
+ // We are no longer a containing block.
+ if (newStyle.position() == StaticPosition && !willHaveTransform) {
+ // Clear our positioned objects list. Our absolutely positioned descendants will be
+ // inserted into our containing block's positioned objects list during layout.
+ removePositionedObjects(nullptr, NewContainingBlock);
+ return;
+ }
+
+ // We are a new containing block.
+ if (oldStyle.position() == StaticPosition && !hadTransform) {
+ // Remove our absolutely positioned descendants from their current containing block.
+ // They will be inserted into our positioned objects list during layout.
+ auto* containingBlock = parent();
+ while (containingBlock && !is<RenderView>(*containingBlock)
+ && (containingBlock->style().position() == StaticPosition || (containingBlock->isInline() && !containingBlock->isReplaced()))) {
+ if (containingBlock->style().position() == RelativePosition && containingBlock->isInline() && !containingBlock->isReplaced()) {
+ containingBlock = containingBlock->containingBlock();
+ break;
+ }
+ containingBlock = containingBlock->parent();
+ }
+ if (containingBlock && is<RenderBlock>(*containingBlock))
+ downcast<RenderBlock>(*containingBlock).removePositionedObjects(this, NewContainingBlock);
+ }
+}
+
void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle& newStyle)
{
const RenderStyle* oldStyle = hasInitializedStyle() ? &style() : nullptr;
-
setReplaced(newStyle.isDisplayInlineType());
-
- if (oldStyle && oldStyle->hasTransformRelatedProperty() && !newStyle.hasTransformRelatedProperty())
- removePositionedObjects(nullptr, NewContainingBlock);
-
- if (oldStyle && parent() && diff == StyleDifferenceLayout && oldStyle->position() != newStyle.position()) {
- if (newStyle.position() == StaticPosition)
- // Clear our positioned objects list. Our absolutely positioned descendants will be
- // inserted into our containing block's positioned objects list during layout.
- removePositionedObjects(nullptr, NewContainingBlock);
- else if (oldStyle->position() == StaticPosition) {
- // Remove our absolutely positioned descendants from their current containing block.
- // They will be inserted into our positioned objects list during layout.
- auto containingBlock = parent();
- while (containingBlock && !is<RenderView>(*containingBlock)
- && (containingBlock->style().position() == StaticPosition || (containingBlock->isInline() && !containingBlock->isReplaced()))) {
- if (containingBlock->style().position() == RelativePosition && containingBlock->isInline() && !containingBlock->isReplaced()) {
- containingBlock = containingBlock->containingBlock();
- break;
- }
- containingBlock = containingBlock->parent();
- }
-
- if (is<RenderBlock>(*containingBlock))
- downcast<RenderBlock>(*containingBlock).removePositionedObjects(this, NewContainingBlock);
- }
- }
-
+ if (oldStyle)
+ removePositionedObjectsIfNeeded(*oldStyle, newStyle);
RenderBox::styleWillChange(diff, newStyle);
}
diff --git a/Source/WebCore/rendering/RenderBlock.h b/Source/WebCore/rendering/RenderBlock.h
index e3a49d1..d8aa29a 100644
--- a/Source/WebCore/rendering/RenderBlock.h
+++ b/Source/WebCore/rendering/RenderBlock.h
@@ -489,6 +489,8 @@
RenderFlowThread* updateCachedFlowThreadContainingBlock(RenderFlowThread*) const;
+ void removePositionedObjectsIfNeeded(const RenderStyle& oldStyle, const RenderStyle& newStyle);
+
private:
bool hasRareData() const;