| // Copyright 2015 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/layout/block_formatting_context.h" |
| |
| #include <algorithm> |
| |
| #include "cobalt/cssom/keyword_value.h" |
| #include "cobalt/layout/box.h" |
| |
| namespace cobalt { |
| namespace layout { |
| |
| BlockFormattingContext::BlockFormattingContext( |
| const LayoutParams& layout_params) |
| : layout_params_(layout_params), collapsing_margin_(0) {} |
| |
| BlockFormattingContext::~BlockFormattingContext() {} |
| |
| void BlockFormattingContext::UpdateRect(Box* child_box) { |
| DCHECK(!child_box->IsAbsolutelyPositioned()); |
| |
| child_box->UpdateSize(layout_params_); |
| UpdatePosition(child_box); |
| |
| // Shrink-to-fit width cannot be less than the width of the widest child. |
| // https://www.w3.org/TR/CSS21/visudet.html#float-width |
| set_shrink_to_fit_width( |
| std::max(shrink_to_fit_width(), |
| child_box->GetMarginBoxRightEdgeOffsetFromContainingBlock())); |
| |
| // If "height" is "auto", the used value is the distance from box's top |
| // content edge to the bottom edge of the bottom margin of its last in-flow |
| // child. |
| // https://www.w3.org/TR/CSS21/visudet.html#normal-block |
| set_auto_height(child_box->GetMarginBoxBottomEdgeOffsetFromContainingBlock()); |
| collapsing_margin_ = child_box->margin_bottom(); |
| |
| // The baseline of an "inline-block" is the baseline of its last line box |
| // in the normal flow, unless it has no in-flow line boxes. |
| // https://www.w3.org/TR/CSS21/visudet.html#line-height |
| if (child_box->AffectsBaselineInBlockFormattingContext()) { |
| set_baseline_offset_from_top_content_edge( |
| child_box->top() + child_box->GetBaselineOffsetFromTopMarginEdge()); |
| } |
| } |
| |
| void BlockFormattingContext::EstimateStaticPosition(Box* child_box) { |
| DCHECK(child_box->IsAbsolutelyPositioned()); |
| |
| // The term "static position" (of an element) refers, roughly, to the position |
| // an element would have had in the normal flow. |
| // https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width |
| UpdatePosition(child_box); |
| } |
| |
| void BlockFormattingContext::UpdatePosition(Box* child_box) { |
| DCHECK_EQ(Box::kBlockLevel, child_box->GetLevel()); |
| |
| // In a block formatting context, each box's left outer edge touches |
| // the left edge of the containing block. |
| // https://www.w3.org/TR/CSS21/visuren.html#block-formatting |
| child_box->set_left(LayoutUnit()); |
| |
| // In a block formatting context, boxes are laid out one after the other, |
| // vertically, beginning at the top of a containing block. The vertical |
| // distance between two sibling boxes is determined by the "margin" |
| // properties. Vertical margins between adjacent block-level boxes in a block |
| // formatting context collapse. |
| // https://www.w3.org/TR/CSS21/visuren.html#block-formatting |
| |
| // When two or more margins collapse, the resulting margin width is the |
| // maximum of the collapsing margins' widths. |
| // https://www.w3.org/TR/CSS21/box.html#collapsing-margins |
| const LayoutUnit margin_top = child_box->margin_top(); |
| LayoutUnit collapsed_margin; |
| if ((margin_top >= LayoutUnit()) && (collapsing_margin_ >= LayoutUnit())) { |
| collapsed_margin = std::max(margin_top, collapsing_margin_); |
| } else if ((margin_top < LayoutUnit()) && |
| (collapsing_margin_ < LayoutUnit())) { |
| // If there are no positive margins, the maximum of the absolute values of |
| // the adjoining margins is deducted from zero. |
| collapsed_margin = LayoutUnit() + std::min(margin_top, collapsing_margin_); |
| } else { |
| // In the case of negative margins, the maximum of the absolute values of |
| // the negative adjoining margins is deducted from the maximum of the |
| // positive adjoining margins. |
| // When there is only one negative and one positive margin, that translates |
| // to: The margins are summed. |
| DCHECK(collapsing_margin_.GreaterEqualOrNaN(LayoutUnit()) || |
| margin_top.GreaterEqualOrNaN(LayoutUnit())); |
| collapsed_margin = collapsing_margin_ + margin_top; |
| } |
| |
| LayoutUnit combined_margin = collapsing_margin_ + margin_top; |
| child_box->set_top(auto_height() - combined_margin + collapsed_margin); |
| } |
| |
| } // namespace layout |
| } // namespace cobalt |