diff --git a/components/script/dom/htmlprogresselement.rs b/components/script/dom/htmlprogresselement.rs index 13aedb6b37f..9e118aa7b74 100644 --- a/components/script/dom/htmlprogresselement.rs +++ b/components/script/dom/htmlprogresselement.rs @@ -2,26 +2,46 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::cell::Ref; + use dom_struct::dom_struct; use html5ever::{local_name, LocalName, Prefix}; use js::rust::HandleObject; +use crate::dom::attr::Attr; +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::ElementBinding::Element_Binding::ElementMethods; use crate::dom::bindings::codegen::Bindings::HTMLProgressElementBinding::HTMLProgressElementMethods; +use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods; +use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{ + ShadowRootMode, SlotAssignmentMode, +}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; -use crate::dom::bindings::root::{DomRoot, MutNullableDom}; +use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::bindings::str::DOMString; use crate::dom::document::Document; -use crate::dom::element::Element; +use crate::dom::element::{AttributeMutation, Element}; +use crate::dom::htmldivelement::HTMLDivElement; use crate::dom::htmlelement::HTMLElement; -use crate::dom::node::Node; +use crate::dom::node::{BindContext, Node, NodeTraits}; use crate::dom::nodelist::NodeList; +use crate::dom::shadowroot::IsUserAgentWidget; +use crate::dom::virtualmethods::VirtualMethods; use crate::script_runtime::CanGc; #[dom_struct] pub(crate) struct HTMLProgressElement { htmlelement: HTMLElement, labels_node_list: MutNullableDom, + shadow_tree: DomRefCell>, +} + +/// Holds handles to all slots in the UA shadow tree +#[derive(Clone, JSTraceable, MallocSizeOf)] +#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] +struct ShadowTree { + progress_bar: Dom, } impl HTMLProgressElement { @@ -33,6 +53,7 @@ impl HTMLProgressElement { HTMLProgressElement { htmlelement: HTMLElement::new_inherited(local_name, prefix, document), labels_node_list: MutNullableDom::new(None), + shadow_tree: Default::default(), } } @@ -53,6 +74,57 @@ impl HTMLProgressElement { can_gc, ) } + + fn create_shadow_tree(&self, can_gc: CanGc) { + let document = self.owner_document(); + let root = self + .upcast::() + .attach_shadow( + IsUserAgentWidget::Yes, + ShadowRootMode::Closed, + false, + SlotAssignmentMode::Manual, + can_gc, + ) + .expect("Attaching UA shadow root failed"); + + let progress_bar = HTMLDivElement::new(local_name!("div"), None, &document, None, can_gc); + // FIXME: This should use ::-moz-progress-bar + progress_bar + .upcast::() + .SetId("-servo-progress-bar".into(), can_gc); + root.upcast::() + .AppendChild(progress_bar.upcast::()) + .unwrap(); + + let _ = self.shadow_tree.borrow_mut().insert(ShadowTree { + progress_bar: progress_bar.as_traced(), + }); + self.upcast::() + .dirty(crate::dom::node::NodeDamage::OtherNodeDamage); + } + + fn shadow_tree(&self, can_gc: CanGc) -> Ref<'_, ShadowTree> { + if !self.upcast::().is_shadow_host() { + self.create_shadow_tree(can_gc); + } + + Ref::filter_map(self.shadow_tree.borrow(), Option::as_ref) + .ok() + .expect("UA shadow tree was not created") + } + + /// Update the visual width of bar + fn update_state(&self, can_gc: CanGc) { + let shadow_tree = self.shadow_tree(can_gc); + let position = (*self.Value() / *self.Max()) * 100.0; + let style = format!("width: {}%", position); + + shadow_tree + .progress_bar + .upcast::() + .set_string_attribute(&local_name!("style"), style.into(), can_gc); + } } impl HTMLProgressElementMethods for HTMLProgressElement { @@ -147,3 +219,27 @@ impl HTMLProgressElementMethods for HTMLProgressElement { } } } + +impl VirtualMethods for HTMLProgressElement { + fn super_type(&self) -> Option<&dyn VirtualMethods> { + Some(self.upcast::() as &dyn VirtualMethods) + } + + fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) { + self.super_type().unwrap().attribute_mutated(attr, mutation); + + let is_important_attribute = matches!( + attr.local_name(), + &local_name!("value") | &local_name!("max") + ); + if is_important_attribute { + self.update_state(CanGc::note()); + } + } + + fn bind_to_tree(&self, context: &BindContext) { + self.super_type().unwrap().bind_to_tree(context); + + self.update_state(CanGc::note()); + } +} diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs index 3fb7a52899e..900ed5d0607 100644 --- a/components/script/dom/virtualmethods.rs +++ b/components/script/dom/virtualmethods.rs @@ -42,6 +42,7 @@ use crate::dom::htmloptgroupelement::HTMLOptGroupElement; use crate::dom::htmloptionelement::HTMLOptionElement; use crate::dom::htmloutputelement::HTMLOutputElement; use crate::dom::htmlpreelement::HTMLPreElement; +use crate::dom::htmlprogresselement::HTMLProgressElement; use crate::dom::htmlscriptelement::HTMLScriptElement; use crate::dom::htmlselectelement::HTMLSelectElement; use crate::dom::htmlslotelement::HTMLSlotElement; @@ -253,6 +254,9 @@ pub(crate) fn vtable_for(node: &Node) -> &dyn VirtualMethods { NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLPreElement)) => { node.downcast::().unwrap() as &dyn VirtualMethods }, + NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLProgressElement)) => { + node.downcast::().unwrap() as &dyn VirtualMethods + }, NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLScriptElement)) => { node.downcast::().unwrap() as &dyn VirtualMethods }, diff --git a/resources/servo.css b/resources/servo.css index 2426c1b8f20..ee2aca1f66f 100644 --- a/resources/servo.css +++ b/resources/servo.css @@ -340,4 +340,19 @@ details > summary:first-of-type { } details[open] > summary:first-of-type { list-style-type: disclosure-open; -} \ No newline at end of file +} + +/* Styles for the element */ +progress { + display: inline-block; + width: 200px; + height: 6px; + border-radius: 3px; + border: 1px solid rgba(0, 0, 0, 0.5); +} +/* FIXME: This should use ::-moz-progress-bar */ +progress #-servo-progress-bar { + display: block; + height: 100%; + background-color: #7a3; +}