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)