Import Cobalt 9.76656
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 934cdfb..236fcef 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -509,6 +509,10 @@
memory_tracker_tool_ =
memory_tracker::CreateMemoryTrackerTool(command_arg);
}
+
+ if (command_line->HasSwitch(switches::kDisableImageAnimations)) {
+ options.web_module_options.enable_image_animations = false;
+ }
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
if (command_line->HasSwitch(browser::switches::kDisableNavigationWhitelist)) {
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index 01647ae..5469a19 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -110,6 +110,9 @@
// Enables memory tracking by installing the memory tracker on startup.
const char kMemoryTracker[] = "memory_tracker";
+// Enables/disables animations on animated images (e.g. animated WebP).
+const char kDisableImageAnimations[] = "disable_image_animations";
+
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
// Disables the hard-coded navigation whitelist without disabling any other
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index 1073a3b..29eb721 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -45,6 +45,7 @@
extern const char kVideoDecoderStub[];
extern const char kWebDriverPort[];
extern const char kWebDriverListenIp[];
+extern const char kDisableImageAnimations[];
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
extern const char kDisableNavigationWhitelist[];
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 79d56ea..b916179 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -526,6 +526,7 @@
base::Unretained(this)),
data.options.layout_trigger, data.dom_max_element_depth,
data.layout_refresh_rate, data.network_module->preferred_language(),
+ data.options.enable_image_animations,
web_module_stat_tracker_->layout_stat_tracker()));
DCHECK(layout_manager_);
@@ -594,8 +595,8 @@
local_storage_database_.reset();
mesh_cache_.reset();
remote_typeface_cache_.reset();
- animated_image_tracker_.reset();
image_cache_.reset();
+ animated_image_tracker_.reset();
fetcher_factory_.reset();
dom_parser_.reset();
css_parser_.reset();
@@ -770,6 +771,9 @@
// Clear out the loader factory's resource provider, possibly aborting any
// in-progress loads.
loader_factory_->Suspend();
+
+ // Clear out any currently tracked animating images.
+ animated_image_tracker_->Reset();
}
void WebModule::Impl::FinishSuspend() {
@@ -865,7 +869,8 @@
image_cache_capacity_multiplier_when_playing_video(1.0f),
thread_priority(base::kThreadPriority_Normal),
loader_thread_priority(base::kThreadPriority_Low),
- animated_image_decode_thread_priority(base::kThreadPriority_Low) {}
+ animated_image_decode_thread_priority(base::kThreadPriority_Low),
+ enable_image_animations(true) {}
WebModule::WebModule(
const GURL& initial_url,
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index 4bed614..d86bbbc 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -170,6 +170,10 @@
scoped_refptr<input::InputPoller> input_poller;
script::JavaScriptEngine::Options javascript_options;
+
+ // Allows image animations to be enabled/disabled. Its default value
+ // is true to enable them.
+ bool enable_image_animations;
};
typedef layout::LayoutManager::LayoutResults LayoutResults;
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index b145a60..7b38425 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-70858
\ No newline at end of file
+76656
\ No newline at end of file
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc
index 269f2d1..77ea1a4 100644
--- a/src/cobalt/layout/box.cc
+++ b/src/cobalt/layout/box.cc
@@ -763,11 +763,27 @@
const ContainerBox* Box::GetStackingContext() const {
if (!parent_) return AsContainerBox();
- if (GetZIndex() == 0) {
- return GetContainingBlock();
+
+ const bool is_positioned = IsPositioned();
+
+ // If the box is an in-flow, non-positioned element, then simply return the
+ // parent as the stacking context.
+ // https://www.w3.org/TR/CSS21/visuren.html#z-index
+ if (!is_positioned && !IsStackingContext()) {
+ return parent_;
}
+
+ // If the box's position is not fixed and the z-index is 0, then the nearest
+ // absolute containing block is accepted if it is closer than the nearest
+ // stacking context.
+ bool accept_absolute_containing_block =
+ computed_style()->position() != cssom::KeywordValue::GetFixed() &&
+ GetZIndex() == 0;
+
ContainerBox* containing_block = parent_;
- while (!containing_block->IsStackingContext()) {
+ while (!containing_block->IsStackingContext() &&
+ (!accept_absolute_containing_block ||
+ !containing_block->IsContainingBlockForPositionAbsoluteElements())) {
containing_block = containing_block->parent_;
}
return containing_block;
@@ -783,42 +799,63 @@
}
void Box::UpdateCrossReferencesOfContainerBox(
- ContainerBox* source_box, bool is_nearest_containing_block,
- bool is_nearest_absolute_containing_block,
- bool is_nearest_fixed_containing_block, bool is_nearest_stacking_context) {
- // Containing blocks and stacking contexts only matter for positioned boxes.
- if (IsPositioned() || IsTransformed()) {
- bool is_my_containing_block;
- // Establish the containing block, as described in
- // http://www.w3.org/TR/CSS21/visudet.html#containing-block-details.
- if (computed_style()->position() == cssom::KeywordValue::GetAbsolute()) {
- // If the element has 'position: absolute', the containing block is
- // established by the nearest ancestor with a 'position' of 'absolute',
- // 'relative' or 'fixed'.
- is_my_containing_block = is_nearest_absolute_containing_block;
- } else if (computed_style()->position() ==
- cssom::KeywordValue::GetFixed()) {
+ ContainerBox* source_box, RelationshipToBox nearest_containing_block,
+ RelationshipToBox nearest_absolute_containing_block,
+ RelationshipToBox nearest_fixed_containing_block,
+ RelationshipToBox nearest_stacking_context) {
+ const bool is_positioned = IsPositioned();
+ bool is_position_fixed = false;
+
+ RelationshipToBox my_nearest_containing_block = nearest_containing_block;
+
+ // Establish the containing block, as described in
+ // http://www.w3.org/TR/CSS21/visudet.html#containing-block-details.
+ // Containing blocks only matter for descendant positioned boxes.
+ if (is_positioned) {
+ const scoped_refptr<cssom::PropertyValue>& position_property =
+ computed_style()->position();
+ if (position_property == cssom::KeywordValue::GetFixed()) {
+ is_position_fixed = true;
// If the element has 'position: fixed', the containing block is
// established by the viewport in the case of continuous media or the page
// area in the case of paged media.
- is_my_containing_block = is_nearest_fixed_containing_block;
- } else {
- // If the element's position is "relative" or "static", the containing
- // block is formed by the content edge of the nearest block container
- // ancestor box.
- is_my_containing_block = is_nearest_containing_block;
+ my_nearest_containing_block = nearest_fixed_containing_block;
+ } else if (position_property == cssom::KeywordValue::GetAbsolute()) {
+ // If the element has 'position: absolute', the containing block is
+ // established by the nearest ancestor with a 'position' of 'absolute',
+ // 'relative' or 'fixed'.
+ my_nearest_containing_block = nearest_absolute_containing_block;
}
+ // Otherwise, the element's position is "relative"; the containing block is
+ // formed by the content edge of the nearest block container ancestor box,
+ // which is the initial value of |my_nearest_containing_block|.
- // If this box has a z_index of zero, then its containing block is its
- // stacking context. Otherwise, the nearest stacking context is used.
- bool is_my_stacking_context =
- GetZIndex() == 0 ? is_my_containing_block : is_nearest_stacking_context;
-
- if (is_my_containing_block) {
+ if (my_nearest_containing_block == kIsBox) {
source_box->AddContainingBlockChild(this);
}
+ }
+
+ // Establish the stacking context, as described in
+ // https://www.w3.org/TR/CSS21/visuren.html#z-index,
+ // https://www.w3.org/TR/css3-color/#transparency, and
+ // https://www.w3.org/TR/css3-transforms/#transform-rendering.
+ // Stacking contexts only matter for descendant positioned boxes and child
+ // stacking contexts.
+ if (is_positioned || IsStackingContext()) {
+ // If the box's position is not fixed and the z-index is 0, then the nearest
+ // box between the absolute containing block and the stacking context is
+ // used; otherwise, the stacking context is always used.
+ bool is_my_stacking_context =
+ !is_position_fixed && GetZIndex() == 0
+ ? ((nearest_absolute_containing_block == kIsBox &&
+ nearest_stacking_context != kIsBoxDescendant) ||
+ (nearest_stacking_context == kIsBox &&
+ nearest_absolute_containing_block != kIsBoxDescendant))
+ : nearest_stacking_context == kIsBox;
+
if (is_my_stacking_context) {
- source_box->AddStackingContextChild(this);
+ source_box->AddStackingContextChild(this, my_nearest_containing_block,
+ GetZIndex());
}
}
}
diff --git a/src/cobalt/layout/box.h b/src/cobalt/layout/box.h
index 3484227..0d444c9 100644
--- a/src/cobalt/layout/box.h
+++ b/src/cobalt/layout/box.h
@@ -107,6 +107,12 @@
kInlineLevel,
};
+ enum RelationshipToBox {
+ kIsBoxAncestor,
+ kIsBox,
+ kIsBoxDescendant,
+ };
+
Box(const scoped_refptr<cssom::CSSComputedStyleDeclaration>&
css_computed_style_declaration,
UsedStyleProvider* used_style_provider,
@@ -155,6 +161,14 @@
// https://www.w3.org/TR/CSS21/visuren.html#absolutely-positioned
bool IsAbsolutelyPositioned() const;
+ // Returns true if the box serves as a stacking context for descendant
+ // elements. The core stacking context creation criteria is given here
+ // (https://www.w3.org/TR/CSS21/visuren.html#z-index) however it is extended
+ // by various other specification documents such as those describing opacity
+ // (https://www.w3.org/TR/css3-color/#transparency) and transforms
+ // (https://www.w3.org/TR/css3-transforms/#transform-rendering).
+ virtual bool IsStackingContext() const { return false; }
+
// Updates the size of margin, border, padding, and content boxes. Lays out
// in-flow descendants, estimates static positions (but not sizes) of
// out-of-flow descendants. Does not update the position of the box.
@@ -554,9 +568,10 @@
// the box tree that have it as their containing block or stacking context.
// This function is called recursively.
virtual void UpdateCrossReferencesOfContainerBox(
- ContainerBox* source_box, bool is_nearest_containing_block,
- bool is_nearest_absolute_containing_block,
- bool is_nearest_fixed_containing_block, bool is_nearest_stacking_context);
+ ContainerBox* source_box, RelationshipToBox nearest_containing_block,
+ RelationshipToBox nearest_absolute_containing_block,
+ RelationshipToBox nearest_fixed_containing_block,
+ RelationshipToBox nearest_stacking_context);
// Updates the horizontal margins for block level in-flow boxes. This is used
// for both non-replaced and replaced elements. See
diff --git a/src/cobalt/layout/container_box.cc b/src/cobalt/layout/container_box.cc
index a13d889..8f18e09 100644
--- a/src/cobalt/layout/container_box.cc
+++ b/src/cobalt/layout/container_box.cc
@@ -144,9 +144,6 @@
InvalidateRenderTreeNodesOfBoxAndAncestors();
}
-// Returns true if the given style allows a container box to act as a containing
-// block for absolutely positioned elements. For example it will be true if
-// this box's style is itself 'absolute'.
bool ContainerBox::IsContainingBlockForPositionAbsoluteElements() const {
return parent() == NULL || IsPositioned() || IsTransformed();
}
@@ -155,12 +152,6 @@
return parent() == NULL || IsTransformed();
}
-// Returns true if this container box serves as a stacking context for
-// descendant elements. The core stacking context creation criteria is given
-// here (https://www.w3.org/TR/CSS21/visuren.html#z-index) however it is
-// extended by various other specification documents such as those describing
-// opacity (https://www.w3.org/TR/css3-color/#transparency) and transforms
-// (https://www.w3.org/TR/css3-transforms/#transform-rendering).
bool ContainerBox::IsStackingContext() const {
bool has_opacity =
base::polymorphic_downcast<const cssom::NumberValue*>(
@@ -189,20 +180,22 @@
negative_z_index_child_.clear();
non_negative_z_index_child_.clear();
- const bool kIsNearestContainingBlockOfChildren = true;
- bool is_nearest_absolute_containing_block =
- IsContainingBlockForPositionAbsoluteElements();
- bool is_nearest_fixed_containing_block =
- IsContainingBlockForPositionFixedElements();
- bool is_nearest_stacking_context = IsStackingContext();
+ const RelationshipToBox kNearestContainingBlockOfChildren = kIsBox;
+ RelationshipToBox nearest_absolute_containing_block =
+ IsContainingBlockForPositionAbsoluteElements() ? kIsBox
+ : kIsBoxAncestor;
+ RelationshipToBox nearest_fixed_containing_block =
+ IsContainingBlockForPositionFixedElements() ? kIsBox : kIsBoxAncestor;
+ RelationshipToBox nearest_stacking_context =
+ IsStackingContext() ? kIsBox : kIsBoxAncestor;
for (Boxes::const_iterator child_box_iterator = child_boxes_.begin();
child_box_iterator != child_boxes_.end(); ++child_box_iterator) {
Box* child_box = *child_box_iterator;
child_box->UpdateCrossReferencesOfContainerBox(
- this, kIsNearestContainingBlockOfChildren,
- is_nearest_absolute_containing_block,
- is_nearest_fixed_containing_block, is_nearest_stacking_context);
+ this, kNearestContainingBlockOfChildren,
+ nearest_absolute_containing_block, nearest_fixed_containing_block,
+ nearest_stacking_context);
}
are_cross_references_valid_ = true;
@@ -215,7 +208,9 @@
positioned_child_boxes_.push_back(child_box);
}
-void ContainerBox::AddStackingContextChild(Box* child_box) {
+void ContainerBox::AddStackingContextChild(
+ Box* child_box, RelationshipToBox containing_block_relationship,
+ int z_index) {
DCHECK_NE(this, child_box);
DCHECK_EQ(this, child_box->GetStackingContext());
int child_z_index = child_box->GetZIndex();
@@ -224,9 +219,11 @@
"boxes that establish stacking contexts.";
if (child_z_index < 0) {
- negative_z_index_child_.insert(child_box);
+ negative_z_index_child_.insert(StackingContextChildInfo(
+ child_box, containing_block_relationship, z_index));
} else {
- non_negative_z_index_child_.insert(child_box);
+ non_negative_z_index_child_.insert(StackingContextChildInfo(
+ child_box, containing_block_relationship, z_index));
}
}
@@ -400,84 +397,68 @@
namespace {
-Vector2dLayoutUnit GetOffsetFromContainingBlockToStackingContext(
- Box* child_box) {
- DCHECK(child_box->IsPositioned() || child_box->IsTransformed());
-
+Vector2dLayoutUnit GetOffsetFromStackingContextToContainingBlock(
+ const Box* child_box,
+ const Box::RelationshipToBox
+ containing_block_relationship_to_stacking_context) {
Vector2dLayoutUnit relative_position;
- for (Box *containing_block = child_box->GetContainingBlock(),
- *current_box = child_box->GetStackingContext();
- current_box != containing_block;
- current_box = current_box->GetContainingBlock()) {
- if (!current_box) {
- DLOG(WARNING)
- << "Unsupported stacking context and containing block relation.";
- break;
- }
+ if (containing_block_relationship_to_stacking_context != Box::kIsBox) {
+ const Box* current_box =
+ containing_block_relationship_to_stacking_context == Box::kIsBoxAncestor
+ ? child_box->GetStackingContext()
+ : child_box->GetContainingBlock();
+ const Box* end_box =
+ containing_block_relationship_to_stacking_context == Box::kIsBoxAncestor
+ ? child_box->GetContainingBlock()
+ : child_box->GetStackingContext();
+
+ while (current_box != end_box) {
+ if (!current_box) {
+ DLOG(WARNING)
+ << "Unsupported stacking context and containing block relation.";
+ break;
+ }
#if !defined(NDEBUG)
- // We should not determine a used position through a transform, as
- // rectangles may not remain rectangles past it, and thus obtaining
- // a position may be misleading.
- if (current_box->IsTransformed()) {
- DLOG(WARNING) << "Boxes with stacking contexts above containing blocks "
- "with transforms may not be positioned correctly.";
- }
+ // We should not determine a used position through a transform, as
+ // rectangles may not remain rectangles past it, and thus obtaining
+ // a position may be misleading.
+ if (current_box->IsTransformed()) {
+ DLOG(WARNING) << "Boxes with stacking contexts unequal to their "
+ "containing blocks that include transforms may not be "
+ "positioned correctly.";
+ }
#endif
- relative_position += current_box->GetContentBoxOffsetFromMarginBox();
- relative_position += current_box->margin_box_offset_from_containing_block();
- }
- return relative_position;
-}
+ relative_position += current_box->GetContentBoxOffsetFromMarginBox();
+ relative_position +=
+ current_box->margin_box_offset_from_containing_block();
-Vector2dLayoutUnit GetOffsetFromStackingContextToContainingBlock(
- Box* child_box) {
- const scoped_refptr<cssom::PropertyValue>& child_box_position =
- child_box->computed_style()->position();
- if (child_box_position == cssom::KeywordValue::GetFixed()) {
- // Elements with fixed position will have their containing block farther
- // up the hierarchy than the stacking context, so handle this case
- // specially.
- return -GetOffsetFromContainingBlockToStackingContext(child_box);
+ if (current_box->computed_style()->position() ==
+ cssom::KeywordValue::GetAbsolute()) {
+ relative_position -= current_box->GetContainingBlock()
+ ->GetContentBoxOffsetFromPaddingBox();
+ }
+ current_box = current_box->GetContainingBlock();
+ }
+
+ // If the containing block is an ancestor of the stacking context, then
+ // reverse the relative position now. The earlier calculations were for the
+ // containing block being a descendant of the stacking context.
+ if (containing_block_relationship_to_stacking_context ==
+ Box::kIsBoxAncestor) {
+ relative_position = -relative_position;
+ }
}
- Vector2dLayoutUnit relative_position;
- if (child_box_position == cssom::KeywordValue::GetAbsolute()) {
+ if (child_box->computed_style()->position() ==
+ cssom::KeywordValue::GetAbsolute()) {
// The containing block is formed by the padding box instead of the content
// box, as described in
// http://www.w3.org/TR/CSS21/visudet.html#containing-block-details.
relative_position -=
child_box->GetContainingBlock()->GetContentBoxOffsetFromPaddingBox();
}
- for (Box *current_box = child_box->GetContainingBlock(),
- *stacking_context = child_box->GetStackingContext();
- current_box != stacking_context;
- current_box = current_box->GetContainingBlock()) {
- if (!current_box) {
- // Positioned elements may have their containing block farther
- // up the hierarchy than the stacking context, so handle this case here.
- DCHECK(child_box->IsPositioned() || child_box->IsTransformed());
- return -GetOffsetFromContainingBlockToStackingContext(child_box);
- }
-#if !defined(NDEBUG)
- // We should not determine a used position through a transform, as
- // rectangles may not remain rectangles past it, and thus obtaining
- // a position may be misleading.
- if (current_box->IsTransformed()) {
- DLOG(WARNING) << "Boxes with stacking contexts below containing blocks "
- "with transforms may not be positioned correctly.";
- }
-#endif
- relative_position += current_box->GetContentBoxOffsetFromMarginBox();
- relative_position += current_box->margin_box_offset_from_containing_block();
-
- if (current_box->computed_style()->position() ==
- cssom::KeywordValue::GetAbsolute()) {
- relative_position -= current_box->GetContainingBlock()
- ->GetContentBoxOffsetFromPaddingBox();
- }
- }
return relative_position;
}
@@ -490,11 +471,14 @@
// Render all children of the passed in list in sorted order.
for (ZIndexSortedList::const_iterator iter = z_index_child_list.begin();
iter != z_index_child_list.end(); ++iter) {
- Box* child_box = *iter;
+ const StackingContextChildInfo& stacking_context_child_info = *iter;
+ Box* child_box = stacking_context_child_info.box;
DCHECK_EQ(this, child_box->GetStackingContext());
Vector2dLayoutUnit position_offset =
- GetOffsetFromStackingContextToContainingBlock(child_box) +
+ GetOffsetFromStackingContextToContainingBlock(
+ child_box,
+ stacking_context_child_info.containing_block_relationship) +
offset_from_parent_node;
child_box->RenderAndAnimate(content_node_builder, position_offset);
@@ -516,14 +500,14 @@
}
void ContainerBox::UpdateCrossReferencesOfContainerBox(
- ContainerBox* source_box, bool is_nearest_containing_block,
- bool is_nearest_absolute_containing_block,
- bool is_nearest_fixed_containing_block, bool is_nearest_stacking_context) {
+ ContainerBox* source_box, RelationshipToBox nearest_containing_block,
+ RelationshipToBox nearest_absolute_containing_block,
+ RelationshipToBox nearest_fixed_containing_block,
+ RelationshipToBox nearest_stacking_context) {
// First update the source container box's cross references with this box.
Box::UpdateCrossReferencesOfContainerBox(
- source_box, is_nearest_containing_block,
- is_nearest_absolute_containing_block, is_nearest_fixed_containing_block,
- is_nearest_stacking_context);
+ source_box, nearest_containing_block, nearest_absolute_containing_block,
+ nearest_fixed_containing_block, nearest_stacking_context);
// In addition to updating the source container box's cross references with
// this box, we also recursively update it with our children.
@@ -532,30 +516,36 @@
// specified types, then the target container box cannot be the nearest box of
// that type for the children.
- const bool kIsNearestContainingBlockOfChildren = false;
- bool is_nearest_absolute_containing_block_of_children =
- is_nearest_absolute_containing_block &&
- !IsContainingBlockForPositionAbsoluteElements();
- bool is_nearest_fixed_containing_block_of_children =
- is_nearest_fixed_containing_block &&
- !IsContainingBlockForPositionFixedElements();
- bool is_nearest_stacking_context_of_children =
- is_nearest_stacking_context && !IsStackingContext();
+ const RelationshipToBox kNearestContainingBlockOfChildren = kIsBoxDescendant;
+ RelationshipToBox nearest_absolute_containing_block_of_children =
+ nearest_absolute_containing_block != kIsBoxDescendant &&
+ IsContainingBlockForPositionAbsoluteElements()
+ ? kIsBoxDescendant
+ : nearest_absolute_containing_block;
+ RelationshipToBox nearest_fixed_containing_block_of_children =
+ nearest_fixed_containing_block != kIsBoxDescendant &&
+ IsContainingBlockForPositionFixedElements()
+ ? kIsBoxDescendant
+ : nearest_fixed_containing_block;
+ RelationshipToBox nearest_stacking_context_of_children =
+ nearest_stacking_context != kIsBoxDescendant && IsStackingContext()
+ ? kIsBoxDescendant
+ : nearest_stacking_context;
// Only process the children if the target container box is still the nearest
// box of one of the types. If it is not, then it is impossible for any of the
// children to be added to the cross references.
- if (is_nearest_absolute_containing_block_of_children ||
- is_nearest_fixed_containing_block_of_children ||
- is_nearest_stacking_context_of_children) {
+ if (nearest_absolute_containing_block_of_children == kIsBox ||
+ nearest_fixed_containing_block_of_children == kIsBox ||
+ nearest_stacking_context_of_children == kIsBox) {
for (Boxes::const_iterator child_box_iterator = child_boxes_.begin();
child_box_iterator != child_boxes_.end(); ++child_box_iterator) {
Box* child_box = *child_box_iterator;
child_box->UpdateCrossReferencesOfContainerBox(
- source_box, kIsNearestContainingBlockOfChildren,
- is_nearest_absolute_containing_block_of_children,
- is_nearest_fixed_containing_block_of_children,
- is_nearest_stacking_context_of_children);
+ source_box, kNearestContainingBlockOfChildren,
+ nearest_absolute_containing_block_of_children,
+ nearest_fixed_containing_block_of_children,
+ nearest_stacking_context_of_children);
}
}
}
@@ -567,20 +557,24 @@
Vector2dLayoutUnit content_box_offset(border_left_width() + padding_left(),
border_top_width() + padding_top());
- // Render all positioned children in our stacking context that have negative
- // z-index values.
+
+ // Render all child stacking contexts and positioned children in our stacking
+ // context that have negative z-index values.
// https://www.w3.org/TR/CSS21/visuren.html#z-index
RenderAndAnimateStackingContextChildren(
negative_z_index_child_, border_node_builder, content_box_offset);
- // Render laid out child boxes.
+
+ // Render in-flow, non-positioned child boxes.
+ // https://www.w3.org/TR/CSS21/visuren.html#z-index
for (Boxes::const_iterator child_box_iterator = child_boxes_.begin();
child_box_iterator != child_boxes_.end(); ++child_box_iterator) {
Box* child_box = *child_box_iterator;
- if (!child_box->IsPositioned() && !child_box->IsTransformed()) {
+ if (!child_box->IsPositioned() && !child_box->IsStackingContext()) {
child_box->RenderAndAnimate(border_node_builder, content_box_offset);
}
}
- // Render all positioned children with non-negative z-index values.
+ // Render all child stacking contexts and positioned children in our stacking
+ // context that have non-negative z-index values.
// https://www.w3.org/TR/CSS21/visuren.html#z-index
RenderAndAnimateStackingContextChildren(
non_negative_z_index_child_, border_node_builder, content_box_offset);
diff --git a/src/cobalt/layout/container_box.h b/src/cobalt/layout/container_box.h
index 1f8f60a..bc64882 100644
--- a/src/cobalt/layout/container_box.h
+++ b/src/cobalt/layout/container_box.h
@@ -78,20 +78,40 @@
// section: https://www.w3.org/TR/css3-transforms/#transform-rendering.
bool IsContainingBlockForPositionFixedElements() const;
- // Returns true if this container box serves as a stacking context for
- // descendant elements.
- bool IsStackingContext() const;
+ // Returns true if the box serves as a stacking context for descendant
+ // elements. The core stacking context creation criteria is given here
+ // (https://www.w3.org/TR/CSS21/visuren.html#z-index) however it is extended
+ // by various other specification documents such as those describing opacity
+ // (https://www.w3.org/TR/css3-color/#transparency) and transforms
+ // (https://www.w3.org/TR/css3-transforms/#transform-rendering).
+ bool IsStackingContext() const OVERRIDE;
protected:
+ struct StackingContextChildInfo {
+ StackingContextChildInfo(Box* box,
+ RelationshipToBox containing_block_relationship,
+ int z_index)
+ : box(box),
+ containing_block_relationship(containing_block_relationship),
+ z_index(z_index) {}
+
+ Box* box;
+ RelationshipToBox containing_block_relationship;
+ int z_index;
+ };
+
class ZIndexComparator {
public:
- bool operator()(const Box* lhs, const Box* rhs) const {
- return lhs->GetZIndex() < rhs->GetZIndex();
+ bool operator()(const StackingContextChildInfo& lhs,
+ const StackingContextChildInfo& rhs) const {
+ return lhs.z_index < rhs.z_index;
}
};
- // Note: find(Box*) and erase(Box*) on ZIndexSortedList may not work as
- // expected due to the use of reflexive comparison for equality.
- typedef std::multiset<Box*, ZIndexComparator> ZIndexSortedList;
+ // Note: find(StackingContextChildInfo) and erase(StackingContextChildInfo) on
+ // ZIndexSortedList may not work as expected due to the use of reflexive
+ // comparison for equality.
+ typedef std::multiset<StackingContextChildInfo, ZIndexComparator>
+ ZIndexSortedList;
void UpdateRectOfPositionedChildBoxes(
const LayoutParams& relative_child_layout_params,
@@ -115,10 +135,10 @@
const Boxes& child_boxes() const { return child_boxes_; }
void UpdateCrossReferencesOfContainerBox(
- ContainerBox* source_box, bool is_nearest_containing_block,
- bool is_nearest_absolute_containing_block,
- bool is_nearest_fixed_containing_block,
- bool is_nearest_stacking_context) OVERRIDE;
+ ContainerBox* source_box, RelationshipToBox nearest_containing_block,
+ RelationshipToBox nearest_absolute_containing_block,
+ RelationshipToBox nearest_fixed_containing_block,
+ RelationshipToBox nearest_stacking_context) OVERRIDE;
bool ValidateUpdateSizeInputs(const LayoutParams& params) OVERRIDE;
void InvalidateUpdateSizeInputs() { update_size_results_valid_ = false; }
@@ -134,7 +154,9 @@
// These helper functions are called from
// Box::UpdateCrossReferencesOfContainerBox().
void AddContainingBlockChild(Box* child_box);
- void AddStackingContextChild(Box* child_box);
+ void AddStackingContextChild(Box* child_box,
+ RelationshipToBox containing_block_relationship,
+ int z_index);
// Updates used values of left/top/right/bottom given the child_box's
// 'position' property is set to 'relative'.
diff --git a/src/cobalt/layout/layout_manager.cc b/src/cobalt/layout/layout_manager.cc
index 88f5a41..0ada5d3 100644
--- a/src/cobalt/layout/layout_manager.cc
+++ b/src/cobalt/layout/layout_manager.cc
@@ -43,7 +43,7 @@
const OnRenderTreeProducedCallback& on_render_tree_produced,
LayoutTrigger layout_trigger, int dom_max_element_depth,
float layout_refresh_rate, const std::string& language,
- LayoutStatTracker* layout_stat_tracker);
+ bool enable_image_animations, LayoutStatTracker* layout_stat_tracker);
~Impl();
// From dom::DocumentObserver.
@@ -143,12 +143,12 @@
const OnRenderTreeProducedCallback& on_render_tree_produced,
LayoutTrigger layout_trigger, int dom_max_element_depth,
float layout_refresh_rate, const std::string& language,
- LayoutStatTracker* layout_stat_tracker)
+ bool enable_image_animations, LayoutStatTracker* layout_stat_tracker)
: window_(window),
locale_(icu::Locale::createCanonical(language.c_str())),
used_style_provider_(new UsedStyleProvider(
window->html_element_context(), window->document()->font_cache(),
- base::Bind(&AttachCameraNodes, window))),
+ base::Bind(&AttachCameraNodes, window), enable_image_animations)),
on_render_tree_produced_callback_(on_render_tree_produced),
layout_trigger_(layout_trigger),
layout_dirty_(StringPrintf("%s.Layout.IsDirty", name.c_str()), true,
@@ -359,10 +359,10 @@
const OnRenderTreeProducedCallback& on_render_tree_produced,
LayoutTrigger layout_trigger, const int dom_max_element_depth,
const float layout_refresh_rate, const std::string& language,
- LayoutStatTracker* layout_stat_tracker)
+ bool enable_image_animations, LayoutStatTracker* layout_stat_tracker)
: impl_(new Impl(name, window, on_render_tree_produced, layout_trigger,
dom_max_element_depth, layout_refresh_rate, language,
- layout_stat_tracker)) {}
+ enable_image_animations, layout_stat_tracker)) {}
LayoutManager::~LayoutManager() {}
diff --git a/src/cobalt/layout/layout_manager.h b/src/cobalt/layout/layout_manager.h
index e02cc0b..a2cde5c 100644
--- a/src/cobalt/layout/layout_manager.h
+++ b/src/cobalt/layout/layout_manager.h
@@ -64,6 +64,7 @@
const OnRenderTreeProducedCallback& on_render_tree_produced,
LayoutTrigger layout_trigger, const int dom_max_element_depth,
const float layout_refresh_rate, const std::string& language,
+ bool enable_image_animations,
LayoutStatTracker* layout_stat_tracker);
~LayoutManager();
diff --git a/src/cobalt/layout/used_style.cc b/src/cobalt/layout/used_style.cc
index 91e64d4..0d3dd43 100644
--- a/src/cobalt/layout/used_style.cc
+++ b/src/cobalt/layout/used_style.cc
@@ -408,12 +408,14 @@
UsedStyleProvider::UsedStyleProvider(
dom::HTMLElementContext* html_element_context, dom::FontCache* font_cache,
- const AttachCameraNodeFunction& attach_camera_node_function)
+ const AttachCameraNodeFunction& attach_camera_node_function,
+ bool enable_image_animations)
: font_cache_(font_cache),
animated_image_tracker_(html_element_context->animated_image_tracker()),
image_cache_(html_element_context->image_cache()),
mesh_cache_(html_element_context->mesh_cache()),
- attach_camera_node_function_(attach_camera_node_function) {}
+ attach_camera_node_function_(attach_camera_node_function),
+ enable_image_animations_(enable_image_animations) {}
scoped_refptr<dom::FontList> UsedStyleProvider::GetUsedFontList(
const scoped_refptr<cssom::PropertyValue>& font_family_refptr,
@@ -657,19 +659,23 @@
scoped_refptr<loader::image::AnimatedImage> animated_image =
base::polymorphic_downcast<loader::image::AnimatedImage*>(
used_background_image.get());
- DCHECK(animated_image);
scoped_refptr<render_tree::ImageNode> image_node =
new render_tree::ImageNode(
- NULL, image_rect, image_transform_data.image_node_transform_matrix);
- render_tree::animations::AnimateNode::Builder animate_node_builder;
- animate_node_builder.Add(
- image_node,
- base::Bind(&loader::image::AnimatedImage::AnimateCallback,
- animated_image, image_rect,
- image_transform_data.image_node_transform_matrix));
+ animated_image->GetFrameProvider()->GetFrame(), image_rect,
+ image_transform_data.image_node_transform_matrix);
+ if (!used_style_provider_->enable_image_animations()) {
+ background_node_ = image_node;
+ } else {
+ render_tree::animations::AnimateNode::Builder animate_node_builder;
+ animate_node_builder.Add(
+ image_node,
+ base::Bind(&loader::image::AnimatedImage::AnimateCallback,
+ animated_image->GetFrameProvider(), image_rect,
+ image_transform_data.image_node_transform_matrix));
- background_node_ = new render_tree::animations::AnimateNode(
- animate_node_builder, image_node);
+ background_node_ = new render_tree::animations::AnimateNode(
+ animate_node_builder, image_node);
+ }
}
}
diff --git a/src/cobalt/layout/used_style.h b/src/cobalt/layout/used_style.h
index 3e670df..f2ec746 100644
--- a/src/cobalt/layout/used_style.h
+++ b/src/cobalt/layout/used_style.h
@@ -68,9 +68,10 @@
const scoped_refptr<render_tree::Node>&, float max_horizontal_fov_rad,
float max_vertical_fov_rad)> AttachCameraNodeFunction;
- UsedStyleProvider(
- dom::HTMLElementContext* html_element_context, dom::FontCache* font_cache,
- const AttachCameraNodeFunction& attach_camera_node_function);
+ UsedStyleProvider(dom::HTMLElementContext* html_element_context,
+ dom::FontCache* font_cache,
+ const AttachCameraNodeFunction& attach_camera_node_function,
+ bool enable_image_animations);
scoped_refptr<dom::FontList> GetUsedFontList(
const scoped_refptr<cssom::PropertyValue>& font_family_refptr,
@@ -90,6 +91,8 @@
// images.
void UpdateAnimatedImages();
+ bool enable_image_animations() const { return enable_image_animations_; }
+
private:
// Called after layout is completed so that it can perform any necessary
// cleanup. Its primary current purpose is making a request to the font cache
@@ -117,6 +120,9 @@
scoped_refptr<cssom::PropertyValue> last_font_weight_refptr_;
scoped_refptr<dom::FontList> last_font_list_;
+ // If true, animated WebP images should animate, otherwise they should not.
+ const bool enable_image_animations_;
+
friend class UsedStyleProviderLayoutScope;
DISALLOW_COPY_AND_ASSIGN(UsedStyleProvider);
};
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-1-containing-block-above-stacking-context-should-be-padding-edge-for-absolute-positioned-elements-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/10-1-containing-block-above-stacking-context-should-be-padding-edge-for-absolute-positioned-elements-expected.png
new file mode 100644
index 0000000..6e56609
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-1-containing-block-above-stacking-context-should-be-padding-edge-for-absolute-positioned-elements-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/10-1-containing-block-above-stacking-context-should-be-padding-edge-for-absolute-positioned-elements.html b/src/cobalt/layout_tests/testdata/css-2-1/10-1-containing-block-above-stacking-context-should-be-padding-edge-for-absolute-positioned-elements.html
new file mode 100644
index 0000000..a52c5c7
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/10-1-containing-block-above-stacking-context-should-be-padding-edge-for-absolute-positioned-elements.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!--
+ | If the element has "position: absolute", the containing block is established
+ | by the padding edge of the nearest ancestor with a "position" of "absolute".
+ | http://www.w3.org/TR/CSS21/visudet.html#containing-block-details
+ -->
+<html>
+<head>
+ <style>
+ .containing-block-with-padding {
+ position: absolute;
+
+ width: 150px;
+ height: 150px;
+
+ padding-top: 50px;
+ padding-left: 50px;
+
+ background-color: rgb(100, 100, 255);
+ }
+ .stacking-context {
+ position: static;
+
+ opacity: .9;
+ }
+ .absolute-element {
+ position: absolute;
+
+ width: 100px;
+ height: 100px;
+
+ background-color: rgb(255, 100, 100);
+ }
+ </style>
+</head>
+<body>
+ <div class="containing-block-with-padding">
+ <div class="stacking-context">
+ <div class="absolute-element"></div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-nearest-ancestor-stacking-context-should-contain-element-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-nearest-ancestor-stacking-context-should-contain-element-expected.png
new file mode 100644
index 0000000..4abe46a
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-nearest-ancestor-stacking-context-should-contain-element-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-nearest-ancestor-stacking-context-should-contain-element.html b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-nearest-ancestor-stacking-context-should-contain-element.html
new file mode 100644
index 0000000..27788d4
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-nearest-ancestor-stacking-context-should-contain-element.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!--
+ | Stacking contexts are different from containing blocks. This test ensures
+ | that elements with an 'auto' z-index still use the correct stacking context
+ | when their containing block is further up the tree than their stacking
+ | context. The results should show the blue box in front of the gray box.
+ | https://www.w3.org/TR/CSS21/visuren.html#z-index
+ -->
+<html>
+<head>
+ <style>
+ .blue-container {
+ position: absolute;
+
+ left: 20px;
+ top: 20px;
+
+ width: 150px;
+ height: 150px;
+ z-index: 3;
+
+ background-color: rgb(100, 100, 100);
+ }
+ .blue {
+ position: fixed;
+
+ left: -20px;
+ top: -20px;
+
+ width: 100px;
+ height: 100px;
+
+ background-color: rgb(100, 100, 255);
+ }
+ </style>
+</head>
+<body>
+ <div class="blue-container">
+ <div class="blue"></div>
+ </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-relative-positioned-element-should-be-included-in-containing-stacking-context-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-relative-positioned-element-should-be-included-in-containing-stacking-context-expected.png
new file mode 100644
index 0000000..3fad754
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-relative-positioned-element-should-be-included-in-containing-stacking-context-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-relative-positioned-element-should-be-included-in-containing-stacking-context.html b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-relative-positioned-element-should-be-included-in-containing-stacking-context.html
new file mode 100644
index 0000000..5ba4ab5
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-relative-positioned-element-should-be-included-in-containing-stacking-context.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<!--
+ | Relative positioned elements should be placed in the correct stacking
+ | context, even when their containing block is nearer and is an in-flow,
+ | non-positioned descendants The results should show the blue box in front of
+ | the green box.
+ | https://www.w3.org/TR/CSS21/visuren.html#z-index
+ -->
+<html>
+<head>
+ <style>
+ .container {
+ position: absolute;
+
+ width: 150px;
+ height: 150px;
+
+ background-color: rgb(158, 158, 158);
+ }
+ .green {
+ position: absolute;
+
+ left: 0px;
+ top: 0px;
+
+ width: 80px;
+ height: 80px;
+
+ background-color: rgb(15, 157, 88);
+ }
+ .blue-container {
+ position: static;
+
+ left: 20px;
+ top: 20px;
+
+ width: 150px;
+ height: 150px;
+ }
+ .blue {
+ position: relative;
+
+ left: 40px;
+ top: 40px;
+
+ width: 80px;
+ height: 80px;
+
+ background-color: rgb(66, 133, 244);
+ }
+ </style>
+</head>
+<body>
+ <div class="container">
+ <div class="green"></div>
+ <div class="blue-container">
+ <div class="blue"></div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-relative-positioned-element-should-not-appear-on-top-of-later-sibling-expected.png b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-relative-positioned-element-should-not-appear-on-top-of-later-sibling-expected.png
new file mode 100644
index 0000000..3fad754
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-relative-positioned-element-should-not-appear-on-top-of-later-sibling-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-relative-positioned-element-should-not-appear-on-top-of-later-sibling.html b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-relative-positioned-element-should-not-appear-on-top-of-later-sibling.html
new file mode 100644
index 0000000..fecd57c
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css-2-1/9-9-1-relative-positioned-element-should-not-appear-on-top-of-later-sibling.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!--
+ | Relative positioned elements should appear in the correct draw order when
+ | they have later, absolutely positioned siblings. The results should show the
+ | blue box in front of the green box.
+ | https://www.w3.org/TR/CSS21/visuren.html#z-index
+ -->
+<html>
+<head>
+ <style>
+ .container {
+ position: absolute;
+
+ width: 150px;
+ height: 150px;
+
+ background-color: rgb(158, 158, 158);
+ }
+ .green {
+ position: relative;
+
+ left: 0px;
+ top: 0px;
+
+ width: 80px;
+ height: 80px;
+
+ background-color: rgb(15, 157, 88);
+ }
+ .blue {
+ position: absolute;
+
+ left: 40px;
+ top: 40px;
+
+ width: 80px;
+ height: 80px;
+
+ background-color: rgb(66, 133, 244);
+ }
+ </style>
+</head>
+<body>
+ <div class="container">
+ <div class="green"></div>
+ <div class="blue"></div>
+ </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt b/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
index 8b56461..823ef54 100644
--- a/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css-2-1/layout_tests.txt
@@ -5,6 +5,7 @@
10-1-absolute-positioned-elements-do-not-effect-containing-block-size
10-1-containing-block-should-be-ancestor-padding-edge-for-absolutely-positioned-elements
10-1-containing-block-should-be-ancestor-padding-edge-for-percentage-of-absolutely-positioned-elements
+10-1-containing-block-above-stacking-context-should-be-padding-edge-for-absolute-positioned-elements
10-3-1-auto-margin-should-become-zero-in-inline-non-replaced-elements
10-3-1-width-should-not-apply-to-inline-non-replaced-elements
10-3-2-auto-margin-should-become-zero-in-inline-replaced-elements
@@ -136,7 +137,10 @@
9-9-1-absolute-positioned-elements-should-render-on-top-of-static-elements
9-9-1-containing-block-may-be-farther-than-stacking-context
9-9-1-elements-with-transform-should-appear-over-normal-flow-elements
+9-9-1-nearest-ancestor-stacking-context-should-contain-element
9-9-1-negative-z-indices
+9-9-1-relative-positioned-element-should-not-appear-on-top-of-later-sibling
+9-9-1-relative-positioned-element-should-be-included-in-containing-stacking-context
9-9-1-simple-positive-z-indices
9-9-1-stacking-context-should-take-into-account-intermediate-containing-blocks
9-9-1-stacking-contexts-differ-from-containing-blocks
diff --git a/src/cobalt/layout_tests/testdata/css3-color/3-2-non-positioned-element-with-opacity-forms-stacking-context-expected.png b/src/cobalt/layout_tests/testdata/css3-color/3-2-non-positioned-element-with-opacity-forms-stacking-context-expected.png
new file mode 100644
index 0000000..4f6d2da
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-color/3-2-non-positioned-element-with-opacity-forms-stacking-context-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-color/3-2-non-positioned-element-with-opacity-forms-stacking-context.html b/src/cobalt/layout_tests/testdata/css3-color/3-2-non-positioned-element-with-opacity-forms-stacking-context.html
new file mode 100644
index 0000000..2b01d07
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-color/3-2-non-positioned-element-with-opacity-forms-stacking-context.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!--
+ | This test ensures that a non-positioned element with an opacity less than 1
+ | is painted by its containing stacking context in the order used for child
+ | stacking contexts and positioned elements. The results should draw the blue
+ | rectangle in front of the green rectangle.
+ | https://www.w3.org/TR/css3-color/#opacity
+ -->
+<html>
+<head>
+ <style>
+ .container {
+ position: absolute;
+
+ width: 150px;
+ height: 150px;
+
+ background-color: rgb(158, 158, 158);
+ }
+ .green {
+ position: absolute;
+
+ left: 20px;
+ top: 20px;
+
+ width: 80px;
+ height: 80px;
+
+ background-color: rgb(15, 157, 88);
+ }
+ .blue-container {
+ position: static;
+
+ opacity: .9;
+
+ width: 150px;
+ height: 150px;
+ }
+ .blue {
+ position: static;
+
+ width: 80px;
+ height: 80px;
+
+ background-color: rgb(66, 133, 244);
+ }
+ </style>
+</head>
+<body>
+ <div class="container">
+ <div class="green"></div>
+ <div class="blue-container">
+ <div class="blue"></div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-color/3-2-non-positioned-element-with-opacity-impacts-positioned-descendants-expected.png b/src/cobalt/layout_tests/testdata/css3-color/3-2-non-positioned-element-with-opacity-impacts-positioned-descendants-expected.png
new file mode 100644
index 0000000..8886f53
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-color/3-2-non-positioned-element-with-opacity-impacts-positioned-descendants-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-color/3-2-non-positioned-element-with-opacity-impacts-positioned-descendants.html b/src/cobalt/layout_tests/testdata/css3-color/3-2-non-positioned-element-with-opacity-impacts-positioned-descendants.html
new file mode 100644
index 0000000..da1c3c3
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-color/3-2-non-positioned-element-with-opacity-impacts-positioned-descendants.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<!--
+ | This test ensures that a non-positioned element with an opacity less than 1
+ | paints the layer it creates at the same stacking order that would be used if
+ | it were a positioned element with ‘z-index: 0’ and ‘opacity: 1’, impacting
+ | all of its positioned ancestors. The results should not display "This line is
+ | invisible."
+ | https://www.w3.org/TR/css3-color/#transparency
+ -->
+<head>
+ <style>
+ body {
+ font-family: Roboto;
+ font-size: 16px;
+ }
+
+ .positioned-container {
+ position: absolute;
+ width: 300px;
+ height: 50px;
+ background-color: #00CC00;
+ }
+
+ .opacity-test {
+ position: static;
+ opacity: 0;
+ width: 300px;
+ height: 50px;
+ background-color: #00CC00;
+ }
+
+ .positioned-red {
+ position: absolute;
+ width: 400px;
+ height: 20px;
+ background-color: #FF0000;
+ top: 60px;
+ }
+ </style>
+</head>
+<body>
+ <div class="positioned-container">
+ <div class="opacity-test">
+ <div class="positioned-red"> This line is invisible. </div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-color/layout_tests.txt b/src/cobalt/layout_tests/testdata/css3-color/layout_tests.txt
index 8b6aa39..678bedd 100644
--- a/src/cobalt/layout_tests/testdata/css3-color/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css3-color/layout_tests.txt
@@ -1,4 +1,6 @@
3-2-nested-opacity-compounds
+3-2-non-positioned-element-with-opacity-forms-stacking-context
+3-2-non-positioned-element-with-opacity-impacts-positioned-descendants
3-2-opacity-applies-to-children
3-2-opacity-applies-to-text
3-2-opacity-does-apply-to-background
diff --git a/src/cobalt/loader/image/animated_image_tracker.cc b/src/cobalt/loader/image/animated_image_tracker.cc
index 25280aa..870acce 100644
--- a/src/cobalt/loader/image/animated_image_tracker.cc
+++ b/src/cobalt/loader/image/animated_image_tracker.cc
@@ -16,6 +16,7 @@
#include <algorithm>
+#include "base/debug/trace_event.h"
#include "cobalt/base/polymorphic_downcast.h"
namespace cobalt {
@@ -25,6 +26,7 @@
AnimatedImageTracker::AnimatedImageTracker(
base::ThreadPriority animated_image_decode_thread_priority)
: animated_image_decode_thread_("AnimatedImage") {
+ TRACE_EVENT0("cobalt::loader::image", "AnimatedImageTracker::RecordImage()");
base::Thread::Options options(MessageLoop::TYPE_DEFAULT,
0 /* default stack size */,
animated_image_decode_thread_priority);
@@ -32,25 +34,37 @@
}
AnimatedImageTracker::~AnimatedImageTracker() {
- animated_image_decode_thread_.Stop();
+ TRACE_EVENT0("cobalt::loader::image", "AnimatedImageTracker::RecordImage()");
+ DCHECK(thread_checker_.CalledOnValidThread());
}
void AnimatedImageTracker::IncreaseURLCount(const GURL& url) {
+ TRACE_EVENT0("cobalt::loader::image",
+ "AnimatedImageTracker::IncreaseURLCount()");
+ DCHECK(thread_checker_.CalledOnValidThread());
current_url_counts_[url]++;
}
void AnimatedImageTracker::DecreaseURLCount(const GURL& url) {
+ TRACE_EVENT0("cobalt::loader::image",
+ "AnimatedImageTracker::DecreaseURLCount()");
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_GT(current_url_counts_[url], 0);
current_url_counts_[url]--;
}
void AnimatedImageTracker::RecordImage(
const GURL& url, loader::image::AnimatedImage* animated_image) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(animated_image);
+ TRACE_EVENT0("cobalt::loader::image", "AnimatedImageTracker::RecordImage()");
image_map_[url] = animated_image;
}
void AnimatedImageTracker::ProcessRecordedImages() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ TRACE_EVENT0("cobalt::loader::image",
+ "AnimatedImageTracker::ProcessRecordedImages()");
for (URLToIntMap::iterator it = current_url_counts_.begin();
it != current_url_counts_.end();) {
const GURL& url = it->first;
@@ -83,16 +97,33 @@
void AnimatedImageTracker::OnDisplayStart(
loader::image::AnimatedImage* animated_image) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(animated_image);
+ TRACE_EVENT0("cobalt::loader::image",
+ "AnimatedImageTracker::OnDisplayStart()");
animated_image->Play(animated_image_decode_thread_.message_loop_proxy());
}
void AnimatedImageTracker::OnDisplayEnd(
loader::image::AnimatedImage* animated_image) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(animated_image);
+ TRACE_EVENT0("cobalt::loader::image", "AnimatedImageTracker::OnDisplayEnd()");
animated_image->Stop();
}
+void AnimatedImageTracker::Reset() {
+ for (URLSet::const_iterator iter = playing_urls_.begin();
+ iter != playing_urls_.end(); ++iter) {
+ OnDisplayEnd(image_map_[iter->first]);
+ }
+
+ image_map_.clear();
+ current_url_counts_.clear();
+ previous_url_counts_.clear();
+ playing_urls_.clear();
+}
+
} // namespace image
} // namespace loader
} // namespace cobalt
diff --git a/src/cobalt/loader/image/animated_image_tracker.h b/src/cobalt/loader/image/animated_image_tracker.h
index 6fdb115..2cb9c72 100644
--- a/src/cobalt/loader/image/animated_image_tracker.h
+++ b/src/cobalt/loader/image/animated_image_tracker.h
@@ -50,19 +50,32 @@
// be called at the end of a layout.
void ProcessRecordedImages();
+ // Clears the AnimatedImageTracker, releasing any tracked in-progress
+ // animations.
+ void Reset();
+
private:
void OnDisplayStart(loader::image::AnimatedImage* image);
void OnDisplayEnd(loader::image::AnimatedImage* image);
- typedef std::map<GURL, loader::image::AnimatedImage*> URLToImageMap;
+ typedef std::map<GURL, scoped_refptr<loader::image::AnimatedImage> >
+ URLToImageMap;
typedef std::map<GURL, int> URLToIntMap;
typedef base::SmallMap<std::map<GURL, base::Unused>, 1> URLSet;
+ // The image decode thread is a thread created and owned by the
+ // AnimatedImageTracker, but used by the AnimatedImage decoders.
+ base::Thread animated_image_decode_thread_;
+
URLToImageMap image_map_;
URLToIntMap previous_url_counts_;
URLToIntMap current_url_counts_;
URLSet playing_urls_;
- base::Thread animated_image_decode_thread_;
+
+ // Used to ensure that all AnimatedImageTracker methods are called on the
+ // same thread (*not* |animated_image_decode_thread_|), the thread that we
+ // were constructed on.
+ base::ThreadChecker thread_checker_;
};
} // namespace image
diff --git a/src/cobalt/loader/image/animated_webp_image.cc b/src/cobalt/loader/image/animated_webp_image.cc
index e5b1b3b..eef28b9 100644
--- a/src/cobalt/loader/image/animated_webp_image.cc
+++ b/src/cobalt/loader/image/animated_webp_image.cc
@@ -16,6 +16,7 @@
#include "cobalt/loader/image/animated_webp_image.h"
+#include "base/synchronization/waitable_event.h"
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/loader/image/image_decoder.h"
#include "cobalt/render_tree/brush.h"
@@ -52,15 +53,22 @@
current_frame_index_(0),
next_frame_index_(0),
should_dispose_previous_frame_to_background_(false),
- resource_provider_(resource_provider) {}
+ resource_provider_(resource_provider),
+ frame_provider_(new FrameProvider()) {
+ TRACE_EVENT0("cobalt::loader::image",
+ "AnimatedWebPImage::AnimatedWebPImage()");
+}
-scoped_refptr<render_tree::Image> AnimatedWebPImage::GetFrame() {
- base::AutoLock lock(lock_);
- return current_canvas_;
+scoped_refptr<const AnimatedImage::FrameProvider>
+AnimatedWebPImage::GetFrameProvider() {
+ TRACE_EVENT0("cobalt::loader::image",
+ "AnimatedWebPImage::GetFrameProvider()");
+ return frame_provider_;
}
void AnimatedWebPImage::Play(
const scoped_refptr<base::MessageLoopProxy>& message_loop) {
+ TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::Play()");
base::AutoLock lock(lock_);
if (is_playing_) {
@@ -75,17 +83,21 @@
}
void AnimatedWebPImage::Stop() {
- if (!decode_closure_.callback().is_null()) {
+ TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::Stop()");
+ base::AutoLock lock(lock_);
+ if (is_playing_) {
message_loop_->PostTask(
FROM_HERE,
base::Bind(&AnimatedWebPImage::StopInternal, base::Unretained(this)));
}
}
-void AnimatedWebPImage::AppendChunk(const uint8* data, size_t input_byte) {
+void AnimatedWebPImage::AppendChunk(const uint8* data, size_t size) {
+ TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::AppendChunk()");
+ TRACK_MEMORY_SCOPE("Rendering");
base::AutoLock lock(lock_);
- data_buffer_.insert(data_buffer_.end(), data, data + input_byte);
+ data_buffer_.insert(data_buffer_.end(), data, data + size);
WebPData webp_data = {&data_buffer_[0], data_buffer_.size()};
WebPDemuxDelete(demux_);
demux_ = WebPDemuxPartial(&webp_data, &demux_state_);
@@ -116,19 +128,38 @@
}
AnimatedWebPImage::~AnimatedWebPImage() {
+ TRACE_EVENT0("cobalt::loader::image",
+ "AnimatedWebPImage::~AnimatedWebPImage()");
Stop();
- base::AutoLock lock(lock_);
+ bool is_playing = false;
+ {
+ base::AutoLock lock(lock_);
+ is_playing = is_playing_;
+ }
+ if (is_playing) {
+ base::WaitableEvent fence(true /* manual reset */,
+ false /* initially unsignaled */);
+ message_loop_->PostTask(FROM_HERE,
+ base::Bind(&base::WaitableEvent::Signal,
+ base::Unretained(&fence)));
+ fence.Wait();
+ }
WebPDemuxDelete(demux_);
}
void AnimatedWebPImage::StopInternal() {
- is_playing_ = false;
- decode_closure_.Cancel();
+ TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::StopInternal()");
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ base::AutoLock lock(lock_);
+ if (!decode_closure_.callback().is_null()) {
+ is_playing_ = false;
+ decode_closure_.Cancel();
+ }
}
void AnimatedWebPImage::PlayInternal() {
+ TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::PlayInternal()");
current_frame_time_ = base::TimeTicks::Now();
- DCHECK(message_loop_);
message_loop_->PostTask(
FROM_HERE,
base::Bind(&AnimatedWebPImage::DecodeFrames, base::Unretained(this)));
@@ -136,9 +167,12 @@
void AnimatedWebPImage::DecodeFrames() {
TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::DecodeFrames()");
+ TRACK_MEMORY_SCOPE("Rendering");
DCHECK(is_playing_ && received_first_frame_);
DCHECK(message_loop_->BelongsToCurrentThread());
+ base::AutoLock lock(lock_);
+
if (decode_closure_.callback().is_null()) {
decode_closure_.Reset(
base::Bind(&AnimatedWebPImage::DecodeFrames, base::Unretained(this)));
@@ -156,7 +190,6 @@
current_frame_index_ = next_frame_index_;
// Set up the next time to call the decode callback.
- base::AutoLock lock(lock_);
if (is_playing_) {
base::TimeDelta delay = next_frame_time_ - base::TimeTicks::Now();
const base::TimeDelta min_delay =
@@ -182,7 +215,11 @@
} // namespace
bool AnimatedWebPImage::DecodeOneFrame(int frame_index) {
- base::AutoLock lock(lock_);
+ TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::DecodeOneFrame()");
+ TRACK_MEMORY_SCOPE("Rendering");
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ lock_.AssertAcquired();
+
WebPIterator webp_iterator;
scoped_refptr<render_tree::Image> next_frame_image;
@@ -239,6 +276,7 @@
new render_tree::CompositionNode(builder);
current_canvas_ = resource_provider_->DrawOffscreenImage(root);
+ frame_provider_->SetFrame(current_canvas_);
}
if (webp_iterator.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
@@ -257,7 +295,12 @@
}
void AnimatedWebPImage::UpdateTimelineInfo() {
- base::AutoLock lock(lock_);
+ TRACE_EVENT0("cobalt::loader::image",
+ "AnimatedWebPImage::UpdateTimelineInfo()");
+ TRACK_MEMORY_SCOPE("Rendering");
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ lock_.AssertAcquired();
+
base::TimeTicks current_time = base::TimeTicks::Now();
next_frame_index_ = current_frame_index_ ? current_frame_index_ : 1;
while (true) {
@@ -293,6 +336,8 @@
scoped_ptr<render_tree::ImageData> AnimatedWebPImage::AllocateImageData(
const math::Size& size) {
+ TRACE_EVENT0("cobalt::loader::image",
+ "AnimatedWebPImage::AllocateImageData()");
TRACK_MEMORY_SCOPE("Rendering");
scoped_ptr<render_tree::ImageData> image_data =
resource_provider_->AllocateImageData(
diff --git a/src/cobalt/loader/image/animated_webp_image.h b/src/cobalt/loader/image/animated_webp_image.h
index e988519..179d42e 100644
--- a/src/cobalt/loader/image/animated_webp_image.h
+++ b/src/cobalt/loader/image/animated_webp_image.h
@@ -50,13 +50,13 @@
bool IsOpaque() const OVERRIDE { return is_opaque_; }
- scoped_refptr<render_tree::Image> GetFrame() OVERRIDE;
+ scoped_refptr<const FrameProvider> GetFrameProvider() OVERRIDE;
void Play(const scoped_refptr<base::MessageLoopProxy>& message_loop) OVERRIDE;
void Stop() OVERRIDE;
- void AppendChunk(const uint8* data, size_t input_byte);
+ void AppendChunk(const uint8* data, size_t size);
private:
~AnimatedWebPImage() OVERRIDE;
@@ -102,6 +102,7 @@
// The original encoded data.
std::vector<uint8> data_buffer_;
scoped_refptr<render_tree::Image> current_canvas_;
+ scoped_refptr<FrameProvider> frame_provider_;
base::Lock lock_;
};
diff --git a/src/cobalt/loader/image/image.h b/src/cobalt/loader/image/image.h
index feb1bd3..fe77dac 100644
--- a/src/cobalt/loader/image/image.h
+++ b/src/cobalt/loader/image/image.h
@@ -19,6 +19,7 @@
#include "base/memory/ref_counted.h"
#include "base/message_loop_proxy.h"
+#include "base/synchronization/lock.h"
#include "base/time.h"
#include "cobalt/math/size_f.h"
#include "cobalt/math/transform_2d.h"
@@ -77,13 +78,38 @@
// when playing animation.
class AnimatedImage : public Image {
public:
+ // FrameProvider nested classes are used as "frame containers". Typically
+ // they will be created by an AnimatedImage and have frames pushed into them
+ // by the creating AnimatedImage object. They can be handed to consumers to
+ // have frames pulled out of them. Its purpose is to allow AnimatedImage
+ // objects to be destroyed without affecting consumers of the FrameProvider.
+ // If the AnimatedImage is destroyed before the consumer is done pulling
+ // frames out of FrameProvider, they will simply be left with the last frame
+ // that was in there.
+ class FrameProvider : public base::RefCountedThreadSafe<FrameProvider> {
+ public:
+ void SetFrame(const scoped_refptr<render_tree::Image>& frame) {
+ base::AutoLock lock(mutex_);
+ frame_ = frame;
+ }
+
+ scoped_refptr<render_tree::Image> GetFrame() const {
+ base::AutoLock lock(mutex_);
+ return frame_;
+ }
+
+ private:
+ virtual ~FrameProvider() {}
+ friend class base::RefCountedThreadSafe<FrameProvider>;
+
+ mutable base::Lock mutex_;
+ scoped_refptr<render_tree::Image> frame_;
+ };
+
bool IsAnimated() const OVERRIDE { return true; }
bool IsOpaque() const OVERRIDE { return false; }
- // Get the current frame. Implementation should be thread safe.
- virtual scoped_refptr<render_tree::Image> GetFrame() = 0;
-
// Start playing the animation, decoding on the given message loop.
// Implementation should be thread safe.
virtual void Play(
@@ -92,14 +118,21 @@
// Stop playing the animation.
virtual void Stop() = 0;
+ // Returns a FrameProvider object from which frames can be pulled out of.
+ // The AnimatedImage object is expected to push frames into the FrameProvider
+ // as it generates them.
+ virtual scoped_refptr<const FrameProvider> GetFrameProvider() = 0;
+
// This callback is intended to be used in a render_tree::AnimateNode.
- void AnimateCallback(const math::RectF& destination_rect,
- const math::Matrix3F& local_transform,
- render_tree::ImageNode::Builder* image_node_builder,
- base::TimeDelta time) {
+ static void AnimateCallback(
+ scoped_refptr<const FrameProvider> frame_provider,
+ const math::RectF& destination_rect,
+ const math::Matrix3F& local_transform,
+ render_tree::ImageNode::Builder* image_node_builder,
+ base::TimeDelta time) {
UNREFERENCED_PARAMETER(time);
- image_node_builder->source = GetFrame();
+ image_node_builder->source = frame_provider->GetFrame();
image_node_builder->destination_rect = destination_rect;
image_node_builder->local_transform = local_transform;
}
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase_runner.py b/src/cobalt/webdriver_benchmarks/tv_testcase_runner.py
index 6f9981c..45c8556 100755
--- a/src/cobalt/webdriver_benchmarks/tv_testcase_runner.py
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase_runner.py
@@ -134,8 +134,9 @@
for command_line_arg in command_line_args:
args.append("--" + command_line_arg)
args.append("--enable_webdriver")
- args.append("--null_savegame")
args.append("--debug_console=off")
+ args.append("--disable_image_animations")
+ args.append("--null_savegame")
args.append("--url=" + _default_url)
self.launcher.SetArgs(args)