blob: e446bf9a33a80a3ea6f37355867c61cfb4b246b4 [file] [log] [blame]
// Copyright 2014 The Cobalt Authors. 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/layout.h"
#include <utility>
#include "base/trace_event/trace_event.h"
#include "cobalt/base/stop_watch.h"
#include "cobalt/cssom/computed_style.h"
#include "cobalt/cssom/css_style_declaration.h"
#include "cobalt/dom/document.h"
#include "cobalt/dom/html_body_element.h"
#include "cobalt/dom/html_element_context.h"
#include "cobalt/dom/html_html_element.h"
#include "cobalt/extension/graphics.h"
#include "cobalt/layout/benchmark_stat_names.h"
#include "cobalt/layout/box_generator.h"
#include "cobalt/layout/initial_containing_block.h"
#include "cobalt/layout/layout_boxes.h"
#include "cobalt/layout/used_style.h"
#include "cobalt/render_tree/animations/animate_node.h"
#include "cobalt/render_tree/matrix_transform_node.h"
namespace cobalt {
namespace layout {
namespace {
class ScopedParagraph {
public:
explicit ScopedParagraph(const scoped_refptr<Paragraph>& paragraph)
: paragraph_(paragraph) {}
~ScopedParagraph() { paragraph_->Close(); }
scoped_refptr<Paragraph>& get() { return paragraph_; }
private:
scoped_refptr<Paragraph> paragraph_;
};
} // namespace
void UpdateComputedStylesAndLayoutBoxTree(
const icu::Locale& locale, const scoped_refptr<dom::Document>& document,
int dom_max_element_depth, UsedStyleProvider* used_style_provider,
LayoutStatTracker* layout_stat_tracker,
icu::BreakIterator* line_break_iterator,
icu::BreakIterator* character_break_iterator,
scoped_refptr<BlockLevelBlockContainerBox>* initial_containing_block,
bool clear_window_with_background_color) {
TRACE_EVENT0("cobalt::layout", "UpdateComputedStylesAndLayoutBoxTree()");
// Layout-related cleanup is performed on the UsedStyleProvider in this
// object's destructor.
UsedStyleProviderLayoutScope used_style_provider_layout_scope(
used_style_provider);
// Update the computed style of all elements in the DOM, if necessary.
document->UpdateComputedStyles();
base::StopWatch stop_watch_layout_box_tree(
LayoutStatTracker::kStopWatchTypeLayoutBoxTree,
base::StopWatch::kAutoStartOn, layout_stat_tracker);
// Create initial containing block.
InitialContainingBlockCreationResults
initial_containing_block_creation_results = CreateInitialContainingBlock(
*document->initial_computed_style_data(), document,
used_style_provider, layout_stat_tracker);
*initial_containing_block = initial_containing_block_creation_results.box;
if (clear_window_with_background_color) {
(*initial_containing_block)->set_blend_background_color(false);
}
// Associate the UI navigation root with the initial containing block.
if (document->window()) {
(*initial_containing_block)
->SetUiNavItem(document->window()->GetUiNavRoot());
}
// Generate boxes.
if (document->html()) {
TRACE_EVENT0("cobalt::layout", kBenchmarkStatBoxGeneration);
base::StopWatch stop_watch_box_generation(
LayoutStatTracker::kStopWatchTypeBoxGeneration,
base::StopWatch::kAutoStartOn, layout_stat_tracker);
// If the implicit root is a root for any observers, the initial containing
// block should reference the corresponding IntersectionObserverRoots.
dom::HTMLElement* html_element =
document->document_element()->AsHTMLElement();
BoxIntersectionObserverModule::IntersectionObserverRootVector roots =
html_element->GetLayoutIntersectionObserverRoots();
BoxIntersectionObserverModule::IntersectionObserverTargetVector targets =
html_element->GetLayoutIntersectionObserverTargets();
(*initial_containing_block)
->AddIntersectionObserverRootsAndTargets(std::move(roots),
std::move(targets));
ScopedParagraph scoped_paragraph(
new Paragraph(locale, (*initial_containing_block)->base_direction(),
Paragraph::DirectionalFormattingStack(),
line_break_iterator, character_break_iterator));
BoxGenerator::Context context(
used_style_provider, layout_stat_tracker, line_break_iterator,
character_break_iterator,
initial_containing_block_creation_results.background_style_source,
dom_max_element_depth);
BoxGenerator root_box_generator(
(*initial_containing_block)->css_computed_style_declaration(),
(*initial_containing_block)
->css_computed_style_declaration()
->animations(),
&(scoped_paragraph.get()), 1 /* dom_element_depth */, &context);
document->html()->Accept(&root_box_generator);
const Boxes& root_boxes = root_box_generator.boxes();
for (Boxes::const_iterator root_box_iterator = root_boxes.begin();
root_box_iterator != root_boxes.end(); ++root_box_iterator) {
(*initial_containing_block)->AddChild(*root_box_iterator);
}
}
// Split bidi level runs.
// The bidi levels were calculated for the paragraphs during box generation.
// Now the text boxes are split between level runs, so that they will be
// reversible during layout without requiring additional run-induced splits.
{
TRACE_EVENT0("cobalt::layout", "SplitBidiLevelRuns");
(*initial_containing_block)->SplitBidiLevelRuns();
}
// Layout.
{
TRACE_EVENT0("cobalt::layout", kBenchmarkStatUpdateUsedSizes);
base::StopWatch stop_watch_update_used_sizes(
LayoutStatTracker::kStopWatchTypeUpdateUsedSizes,
base::StopWatch::kAutoStartOn, layout_stat_tracker);
(*initial_containing_block)->set_left(LayoutUnit());
(*initial_containing_block)->set_top(LayoutUnit());
(*initial_containing_block)->UpdateSize(LayoutParams());
}
// Update all UI navigation elements with the sizes and positions of their
// corresponding layout boxes.
if (document->ui_nav_needs_layout()) {
TRACE_EVENT0("cobalt::layout", "UpdateUiNavigationItems");
document->set_ui_nav_needs_layout(false);
const auto& ui_nav_elements = document->ui_navigation_elements();
(*initial_containing_block)->UpdateUiNavigationItem();
for (dom::HTMLElement* html_element : ui_nav_elements) {
LayoutBoxes* layout_boxes = base::polymorphic_downcast<LayoutBoxes*>(
html_element->layout_boxes());
if (layout_boxes) {
for (Box* box : layout_boxes->boxes()) {
box->UpdateUiNavigationItem();
}
}
}
}
}
scoped_refptr<render_tree::Node> GenerateRenderTreeFromBoxTree(
UsedStyleProvider* used_style_provider,
LayoutStatTracker* layout_stat_tracker,
scoped_refptr<BlockLevelBlockContainerBox>* initial_containing_block) {
TRACE_EVENT0("cobalt::layout", "GenerateRenderTreeFromBoxTree()");
render_tree::CompositionNode::Builder render_tree_root_builder;
{
TRACE_EVENT0("cobalt::layout", kBenchmarkStatRenderAndAnimate);
base::StopWatch stop_watch_render_and_animate(
LayoutStatTracker::kStopWatchTypeRenderAndAnimate,
base::StopWatch::kAutoStartOn, layout_stat_tracker);
(*initial_containing_block)
->RenderAndAnimate(&render_tree_root_builder, math::Vector2dF(0, 0),
(initial_containing_block->get()));
}
// During computed style update and RenderAndAnimate, we get the actual images
// that are linked to their URLs. Now go through them and update the playing
// status for animated images.
used_style_provider->UpdateAnimatedImages();
render_tree::Node* static_root_node =
new render_tree::CompositionNode(std::move(render_tree_root_builder));
// Support insertion of a custom transform at the render tree root.
static const CobaltExtensionGraphicsApi* s_graphics_extension =
static_cast<const CobaltExtensionGraphicsApi*>(
SbSystemGetExtension(kCobaltExtensionGraphicsName));
float m00, m01, m02, m10, m11, m12, m20, m21, m22;
if (s_graphics_extension &&
strcmp(s_graphics_extension->name, kCobaltExtensionGraphicsName) == 0 &&
s_graphics_extension->version >= 5 &&
s_graphics_extension->GetRenderRootTransform(&m00, &m01, &m02, &m10, &m11,
&m12, &m20, &m21, &m22)) {
static_root_node = new render_tree::MatrixTransformNode(
static_root_node, math::Matrix3F::FromValues(m00, m01, m02, m10, m11,
m12, m20, m21, m22));
}
// Make it easy to animate the entire tree by placing an AnimateNode at the
// root to merge any sub-AnimateNodes.
render_tree::animations::AnimateNode* animate_node =
new render_tree::animations::AnimateNode(static_root_node);
return animate_node;
}
} // namespace layout
} // namespace cobalt