From c5a5162346d3780e42b2e581acc3a1fe96c5630d Mon Sep 17 00:00:00 2001 From: Pyfisch Date: Fri, 5 Jan 2018 22:37:44 +0100 Subject: [PATCH 1/2] Implement root element backgrounds Closes #19421 --- components/layout_thread/lib.rs | 69 +++++++++++-------- tests/wpt/metadata/MANIFEST.json | 50 ++++++++++++++ .../background-root-1-ref.html | 19 +++++ .../css-backgrounds/background-root-1.html | 21 ++++++ .../css-backgrounds/background-root-2.html | 10 +++ .../reference/background-root-2-mismatch.html | 8 +++ 6 files changed, 150 insertions(+), 27 deletions(-) create mode 100644 tests/wpt/web-platform-tests/css/css-backgrounds/background-root-1-ref.html create mode 100644 tests/wpt/web-platform-tests/css/css-backgrounds/background-root-1.html create mode 100644 tests/wpt/web-platform-tests/css/css-backgrounds/background-root-2.html create mode 100644 tests/wpt/web-platform-tests/css/css-backgrounds/reference/background-root-2-mismatch.html diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index b89df5f6fa75..bc20d5d47eda 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -69,7 +69,6 @@ use layout::context::LayoutContext; use layout::context::RegisteredPainter; use layout::context::RegisteredPainters; use layout::context::malloc_size_of_persistent_local_context; -use layout::display_list_builder::ToGfxColor; use layout::flow::{Flow, GetBaseFlow, ImmutableFlowUtils, MutableOwnedFlowUtils}; use layout::flow_ref::FlowRef; use layout::incremental::{LayoutDamageComputation, RelayoutMode, SpecialRestyleDamage}; @@ -131,6 +130,7 @@ use style::invalidation::element::restyle_hints::RestyleHint; use style::logical_geometry::LogicalPoint; use style::media_queries::{Device, MediaList, MediaType}; use style::properties::PropertyId; +use style::properties::longhands::background_image::get_initial_value as get_initial_background_image; use style::selector_parser::SnapshotMap; use style::servo::restyle_damage::ServoRestyleDamage; use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards}; @@ -140,6 +140,7 @@ use style::thread_state::{self, ThreadState}; use style::timer::Timer; use style::traversal::DomTraversal; use style::traversal_flags::TraversalFlags; +use style::values::computed::Color; use style_traits::CSSPixel; use style_traits::DevicePixel; use style_traits::SpeculativePainter; @@ -962,6 +963,7 @@ impl LayoutThread { metadata.clone(), sender.clone(), || { + determine_root_background(layout_root); layout_root.mut_base().stacking_relative_position = LogicalPoint::zero(writing_mode).to_physical(writing_mode, self.viewport_size).to_vector(); @@ -1048,7 +1050,9 @@ impl LayoutThread { self.webrender_api.set_display_list( self.webrender_document, webrender_api::Epoch(epoch.0), - Some(get_root_flow_background_color(layout_root)), + // Do not provide an explicit background color. + // Webrender paints the screen white before displaying anything. + None, viewport_size, builder.finalize(), true, @@ -1664,35 +1668,46 @@ impl ProfilerMetadataFactory for LayoutThread { } } -// The default computed value for background-color is transparent (see -// http://dev.w3.org/csswg/css-backgrounds/#background-color). However, we -// need to propagate the background color from the root HTML/Body -// element (http://dev.w3.org/csswg/css-backgrounds/#special-backgrounds) if -// it is non-transparent. The phrase in the spec "If the canvas background -// is not opaque, what shows through is UA-dependent." is handled by rust-layers -// clearing the frame buffer to white. This ensures that setting a background -// color on an iframe element, while the iframe content itself has a default -// transparent background color is handled correctly. -fn get_root_flow_background_color(flow: &mut Flow) -> webrender_api::ColorF { - let transparent = webrender_api::ColorF { r: 0.0, g: 0.0, b: 0.0, a: 0.0 }; - if !flow.is_block_like() { - return transparent; +/// Propagate background if necessary from the to the document root. +/// +/// Backgrounds by default are transparent. This ensures that iframes +/// are displayed with a background from the surrounding context. +/// +/// Spec: https://drafts.csswg.org/css-backgrounds/#body-background +fn determine_root_background(root_flow: &mut Flow) { + if !root_flow.is_block_like() { + return; + } + let root_block = root_flow.as_mut_block(); + { + let root_bg = root_block.fragment.style.get_background(); + // If the root element already has a background do nothing. + // The background will be rendered correctly without changes. + if root_bg.background_image != get_initial_background_image() || + root_bg.background_color != Color::transparent() { + debug!("Some background found for root element."); + return; + } } + debug!("Root element without background. Trying to propagate child element background."); - let block_flow = flow.as_mut_block(); - let kid = match block_flow.base.children.iter_mut().next() { - None => return transparent, + let kid = match root_block.base.children.iter_mut().rev().next() { + None => return, + Some(ref kid) if !kid.is_block_like() => return, Some(kid) => kid, }; - if !kid.is_block_like() { - return transparent; - } - - let kid_block_flow = kid.as_block(); - kid_block_flow.fragment - .style - .resolve_color(kid_block_flow.fragment.style.get_background().background_color) - .to_gfx_color() + let kid_mut_block = kid.as_mut_block(); + let kid_background = ServoArc::make_mut(&mut kid_mut_block.fragment.style).mutate_background(); + // Copy background from to element. + let root_style = ServoArc::make_mut(&mut root_block.fragment.style); + *root_style.mutate_background() = kid_background.clone(); + // According to the spec all attributes should be set to their initial values + // but this is not strictly needed as only backgrounds are drawn if either a + // background-color or a background-image is present. + // This is to prevent that background images are displayed multiple times + // and overlap each other. + kid_background.set_background_image(get_initial_background_image()); + kid_background.set_background_color(Color::transparent()); } fn get_ua_stylesheets() -> Result { diff --git a/tests/wpt/metadata/MANIFEST.json b/tests/wpt/metadata/MANIFEST.json index a1f61f548ea3..eabf856d7fb6 100644 --- a/tests/wpt/metadata/MANIFEST.json +++ b/tests/wpt/metadata/MANIFEST.json @@ -96789,6 +96789,30 @@ {} ] ], + "css/css-backgrounds/background-root-1.html": [ + [ + "/css/css-backgrounds/background-root-1.html", + [ + [ + "/css/css-backgrounds/background-root-1-ref.html", + "==" + ] + ], + {} + ] + ], + "css/css-backgrounds/background-root-2.html": [ + [ + "/css/css-backgrounds/background-root-2.html", + [ + [ + "/css/css-backgrounds/reference/background-root-2-mismatch.html", + "!=" + ] + ], + {} + ] + ], "css/css-backgrounds/background-size-002.html": [ [ "/css/css-backgrounds/background-size-002.html", @@ -228250,6 +228274,11 @@ {} ] ], + "css/css-backgrounds/background-root-1-ref.html": [ + [ + {} + ] + ], "css/css-backgrounds/background-size/reference/background-size-contain.xht": [ [ {} @@ -228760,6 +228789,11 @@ {} ] ], + "css/css-backgrounds/reference/background-root-2-mismatch.html": [ + [ + {} + ] + ], "css/css-backgrounds/reference/background-size-002-ref.html": [ [ {} @@ -464077,6 +464111,18 @@ "36050bffda9382cfd978dc82a2f0244a535a6a46", "support" ], + "css/css-backgrounds/background-root-1-ref.html": [ + "180ae5a9427d3464428bd7a6be0d0e48642d0be1", + "support" + ], + "css/css-backgrounds/background-root-1.html": [ + "276e2d5bbfdf4f32d46dd4061ff34a375a3fadb5", + "reftest" + ], + "css/css-backgrounds/background-root-2.html": [ + "2602134be4a9f9332aaebc1275fffd4472f674a2", + "reftest" + ], "css/css-backgrounds/background-size-001.html": [ "7cf677bf25a1fcac569bd0accd28dd66e6060a1b", "testharness" @@ -466281,6 +466327,10 @@ "1141a4c270ace715755b9b8352dab9baffca27c4", "support" ], + "css/css-backgrounds/reference/background-root-2-mismatch.html": [ + "6b1a355f04d6a7cf434598b3f7f9ed137cb5e9ff", + "support" + ], "css/css-backgrounds/reference/background-size-002-ref.html": [ "33d8850f315bedabb7024031b091a14177034c1d", "support" diff --git a/tests/wpt/web-platform-tests/css/css-backgrounds/background-root-1-ref.html b/tests/wpt/web-platform-tests/css/css-backgrounds/background-root-1-ref.html new file mode 100644 index 000000000000..e04f0df8b71f --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-backgrounds/background-root-1-ref.html @@ -0,0 +1,19 @@ + + +A nice Rainbow 🌈 + + + + diff --git a/tests/wpt/web-platform-tests/css/css-backgrounds/background-root-1.html b/tests/wpt/web-platform-tests/css/css-backgrounds/background-root-1.html new file mode 100644 index 000000000000..b213cace5bcb --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-backgrounds/background-root-1.html @@ -0,0 +1,21 @@ + + +A nice Rainbow 🌈 + + + + + + diff --git a/tests/wpt/web-platform-tests/css/css-backgrounds/background-root-2.html b/tests/wpt/web-platform-tests/css/css-backgrounds/background-root-2.html new file mode 100644 index 000000000000..c1382a040b21 --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-backgrounds/background-root-2.html @@ -0,0 +1,10 @@ + + +A partially transparent (green) Background + + + diff --git a/tests/wpt/web-platform-tests/css/css-backgrounds/reference/background-root-2-mismatch.html b/tests/wpt/web-platform-tests/css/css-backgrounds/reference/background-root-2-mismatch.html new file mode 100644 index 000000000000..7b0de405cdfe --- /dev/null +++ b/tests/wpt/web-platform-tests/css/css-backgrounds/reference/background-root-2-mismatch.html @@ -0,0 +1,8 @@ + + +A partially transparent (green) Background + From 89be5f50448da3e8db84d06003af3145233e7ecf Mon Sep 17 00:00:00 2001 From: Pyfisch Date: Sun, 7 Jan 2018 16:01:30 +0100 Subject: [PATCH 2/2] WIP Changes to determine_root_background --- components/layout_thread/lib.rs | 76 +++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 18 deletions(-) diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index bc20d5d47eda..7ae0f30b693a 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -963,7 +963,7 @@ impl LayoutThread { metadata.clone(), sender.clone(), || { - determine_root_background(layout_root); + determine_root_background(document, layout_root); layout_root.mut_base().stacking_relative_position = LogicalPoint::zero(writing_mode).to_physical(writing_mode, self.viewport_size).to_vector(); @@ -1674,7 +1674,7 @@ impl ProfilerMetadataFactory for LayoutThread { /// are displayed with a background from the surrounding context. /// /// Spec: https://drafts.csswg.org/css-backgrounds/#body-background -fn determine_root_background(root_flow: &mut Flow) { +fn determine_root_background(document: Option<&ServoLayoutDocument>, root_flow: &mut Flow) { if !root_flow.is_block_like() { return; } @@ -1691,23 +1691,63 @@ fn determine_root_background(root_flow: &mut Flow) { } debug!("Root element without background. Trying to propagate child element background."); - let kid = match root_block.base.children.iter_mut().rev().next() { - None => return, - Some(ref kid) if !kid.is_block_like() => return, - Some(kid) => kid, + // Try to find the body node opaque id. + let mut body_node = None; + if let Some(root) = document.and_then(ServoLayoutDocument::root_element) { + for child in root.as_node().dom_children() { + if let Some(element) = child.as_element() { + if element.get_namespace() == "http://www.w3.org/1999/xhtml" && element.get_local_name() == "body" { + body_node = Some(child.opaque()); + } + } + } }; - let kid_mut_block = kid.as_mut_block(); - let kid_background = ServoArc::make_mut(&mut kid_mut_block.fragment.style).mutate_background(); - // Copy background from to element. - let root_style = ServoArc::make_mut(&mut root_block.fragment.style); - *root_style.mutate_background() = kid_background.clone(); - // According to the spec all attributes should be set to their initial values - // but this is not strictly needed as only backgrounds are drawn if either a - // background-color or a background-image is present. - // This is to prevent that background images are displayed multiple times - // and overlap each other. - kid_background.set_background_image(get_initial_background_image()); - kid_background.set_background_color(Color::transparent()); + if body_node.is_none() { + return + } + let body_node = body_node.unwrap(); + debug!("Body element {:?} found.", body_node); + + 'children: for kid_mut in root_block.base.children.iter_mut() { + if kid_mut.is_block_like() { + let kid_mut_block = kid_mut.as_mut_block(); + if kid_mut_block.fragment.node == body_node { + let kid_background = ServoArc::make_mut(&mut kid_mut_block.fragment.style).mutate_background(); + // Copy background from to element. + let root_style = ServoArc::make_mut(&mut root_block.fragment.style); + *root_style.mutate_background() = kid_background.clone(); + // According to the spec all attributes should be set to their initial values + // but this is not strictly needed as only backgrounds are drawn if either a + // background-color or a background-image is present. + // This is to prevent that background images are displayed multiple times + // and overlap each other. + kid_background.set_background_image(get_initial_background_image()); + kid_background.set_background_color(Color::transparent()); + break 'children; + } + } + if kid_mut.is_inline_flow() { + let kid_mut_inline = kid_mut.as_mut_inline(); + debug!("Testing an inline."); + for fragment in kid_mut_inline.fragments.fragments.as_mut_slice() { + debug!("Testing inline fragment: {:?}", fragment.node); + if fragment.node == body_node { + let kid_background = ServoArc::make_mut(&mut fragment.style).mutate_background(); + // Copy background from to element. + let root_style = ServoArc::make_mut(&mut root_block.fragment.style); + *root_style.mutate_background() = kid_background.clone(); + // According to the spec all attributes should be set to their initial values + // but this is not strictly needed as only backgrounds are drawn if either a + // background-color or a background-image is present. + // This is to prevent that background images are displayed multiple times + // and overlap each other. + kid_background.set_background_image(get_initial_background_image()); + kid_background.set_background_color(Color::transparent()); + break 'children; + } + } + } + } } fn get_ua_stylesheets() -> Result {