blob: aff5ebc23181e2a6f58479b12d54e9df9d19fa0a [file] [log] [blame]
// 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