mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01:00
Barebones media UI
This commit is contained in:
parent
4f6b86f9f5
commit
1c02fc94a8
11 changed files with 177 additions and 25 deletions
|
@ -60,6 +60,7 @@ pub enum Resource {
|
||||||
PresentationalHintsCSS,
|
PresentationalHintsCSS,
|
||||||
QuirksModeCSS,
|
QuirksModeCSS,
|
||||||
RippyPNG,
|
RippyPNG,
|
||||||
|
MediaControls,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ResourceReaderMethods {
|
pub trait ResourceReaderMethods {
|
||||||
|
@ -94,6 +95,7 @@ fn resources_for_tests() -> Box<dyn ResourceReaderMethods + Sync + Send> {
|
||||||
Resource::PresentationalHintsCSS => "presentational-hints.css",
|
Resource::PresentationalHintsCSS => "presentational-hints.css",
|
||||||
Resource::QuirksModeCSS => "quirks-mode.css",
|
Resource::QuirksModeCSS => "quirks-mode.css",
|
||||||
Resource::RippyPNG => "rippy.png",
|
Resource::RippyPNG => "rippy.png",
|
||||||
|
Resource::MediaControls => "media_controls.js",
|
||||||
};
|
};
|
||||||
let mut path = env::current_exe().unwrap();
|
let mut path = env::current_exe().unwrap();
|
||||||
path = path.canonicalize().unwrap();
|
path = path.canonicalize().unwrap();
|
||||||
|
|
|
@ -163,6 +163,7 @@ use style::stylesheet_set::DocumentStylesheetSet;
|
||||||
use style::stylesheets::{Origin, OriginSet, Stylesheet};
|
use style::stylesheets::{Origin, OriginSet, Stylesheet};
|
||||||
use url::percent_encoding::percent_decode;
|
use url::percent_encoding::percent_decode;
|
||||||
use url::Host;
|
use url::Host;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
/// The number of times we are allowed to see spurious `requestAnimationFrame()` calls before
|
/// The number of times we are allowed to see spurious `requestAnimationFrame()` calls before
|
||||||
/// falling back to fake ones.
|
/// falling back to fake ones.
|
||||||
|
@ -385,6 +386,12 @@ pub struct Document {
|
||||||
shadow_roots: DomRefCell<HashSet<Dom<ShadowRoot>>>,
|
shadow_roots: DomRefCell<HashSet<Dom<ShadowRoot>>>,
|
||||||
/// Whether any of the shadow roots need the stylesheets flushed.
|
/// Whether any of the shadow roots need the stylesheets flushed.
|
||||||
shadow_roots_styles_changed: Cell<bool>,
|
shadow_roots_styles_changed: Cell<bool>,
|
||||||
|
/// List of registered media controls.
|
||||||
|
/// We need to keep this list to allow the media controls to
|
||||||
|
/// access the "privileged" document.servoGetMediaControls(id) API,
|
||||||
|
/// where `id` needs to match any of the registered ShadowRoots
|
||||||
|
/// hosting the media controls UI.
|
||||||
|
media_controls: DomRefCell<HashMap<String, Dom<ShadowRoot>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(JSTraceable, MallocSizeOf)]
|
#[derive(JSTraceable, MallocSizeOf)]
|
||||||
|
@ -2457,6 +2464,18 @@ impl Document {
|
||||||
self.responsive_images.borrow_mut().remove(i);
|
self.responsive_images.borrow_mut().remove(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn register_media_controls(&self, controls: &ShadowRoot) -> String {
|
||||||
|
let id = Uuid::new_v4().to_string();
|
||||||
|
self.media_controls
|
||||||
|
.borrow_mut()
|
||||||
|
.insert(id.clone(), Dom::from_ref(controls));
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unregister_media_controls(&self, id: &str) {
|
||||||
|
self.media_controls.borrow_mut().remove(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(MallocSizeOf, PartialEq)]
|
#[derive(MallocSizeOf, PartialEq)]
|
||||||
|
@ -2750,6 +2769,7 @@ impl Document {
|
||||||
delayed_tasks: Default::default(),
|
delayed_tasks: Default::default(),
|
||||||
shadow_roots: DomRefCell::new(HashSet::new()),
|
shadow_roots: DomRefCell::new(HashSet::new()),
|
||||||
shadow_roots_styles_changed: Cell::new(false),
|
shadow_roots_styles_changed: Cell::new(false),
|
||||||
|
media_controls: DomRefCell::new(HashMap::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4551,6 +4571,15 @@ impl DocumentMethods for Document {
|
||||||
fn ExitFullscreen(&self) -> Rc<Promise> {
|
fn ExitFullscreen(&self) -> Rc<Promise> {
|
||||||
self.exit_fullscreen()
|
self.exit_fullscreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Servo only API to get an instance of the controls of a specific
|
||||||
|
// media element matching the given id.
|
||||||
|
fn ServoGetMediaControls(&self, id: DOMString) -> Fallible<DomRoot<ShadowRoot>> {
|
||||||
|
match self.media_controls.borrow().get(&*id) {
|
||||||
|
Some(m) => Ok(DomRoot::from_ref(&*m)),
|
||||||
|
None => Err(Error::InvalidAccess),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_with_current_time_ms(marker: &Cell<u64>) {
|
fn update_with_current_time_ms(marker: &Cell<u64>) {
|
||||||
|
|
|
@ -78,7 +78,7 @@ use crate::dom::nodelist::NodeList;
|
||||||
use crate::dom::promise::Promise;
|
use crate::dom::promise::Promise;
|
||||||
use crate::dom::raredata::ElementRareData;
|
use crate::dom::raredata::ElementRareData;
|
||||||
use crate::dom::servoparser::ServoParser;
|
use crate::dom::servoparser::ServoParser;
|
||||||
use crate::dom::shadowroot::ShadowRoot;
|
use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot};
|
||||||
use crate::dom::text::Text;
|
use crate::dom::text::Text;
|
||||||
use crate::dom::validation::Validatable;
|
use crate::dom::validation::Validatable;
|
||||||
use crate::dom::virtualmethods::{vtable_for, VirtualMethods};
|
use crate::dom::virtualmethods::{vtable_for, VirtualMethods};
|
||||||
|
@ -231,13 +231,6 @@ impl FromStr for AdjacentPosition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether a shadow root hosts an User Agent widget.
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
pub enum IsUserAgentWidget {
|
|
||||||
No,
|
|
||||||
Yes,
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Element methods
|
// Element methods
|
||||||
//
|
//
|
||||||
|
@ -494,7 +487,7 @@ impl Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Steps 4, 5 and 6.
|
// Steps 4, 5 and 6.
|
||||||
let shadow_root = ShadowRoot::new(self, &*self.node.owner_doc());
|
let shadow_root = ShadowRoot::new(self, &*self.node.owner_doc(), is_ua_widget);
|
||||||
self.ensure_rare_data().shadow_root = Some(Dom::from_ref(&*shadow_root));
|
self.ensure_rare_data().shadow_root = Some(Dom::from_ref(&*shadow_root));
|
||||||
shadow_root
|
shadow_root
|
||||||
.upcast::<Node>()
|
.upcast::<Node>()
|
||||||
|
@ -504,6 +497,8 @@ impl Element {
|
||||||
self.node.owner_doc().register_shadow_root(&*shadow_root);
|
self.node.owner_doc().register_shadow_root(&*shadow_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
|
||||||
|
|
||||||
Ok(shadow_root)
|
Ok(shadow_root)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ use crate::dom::bindings::codegen::Bindings::HTMLMediaElementBinding::HTMLMediaE
|
||||||
use crate::dom::bindings::codegen::Bindings::HTMLSourceElementBinding::HTMLSourceElementMethods;
|
use crate::dom::bindings::codegen::Bindings::HTMLSourceElementBinding::HTMLSourceElementMethods;
|
||||||
use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorConstants::*;
|
use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorConstants::*;
|
||||||
use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethods;
|
use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethods;
|
||||||
|
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeBinding::NodeMethods;
|
||||||
use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{TextTrackKind, TextTrackMode};
|
use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{TextTrackKind, TextTrackMode};
|
||||||
use crate::dom::bindings::codegen::InheritTypes::{ElementTypeId, HTMLElementTypeId};
|
use crate::dom::bindings::codegen::InheritTypes::{ElementTypeId, HTMLElementTypeId};
|
||||||
use crate::dom::bindings::codegen::InheritTypes::{HTMLMediaElementTypeId, NodeTypeId};
|
use crate::dom::bindings::codegen::InheritTypes::{HTMLMediaElementTypeId, NodeTypeId};
|
||||||
|
@ -33,11 +34,12 @@ use crate::dom::document::Document;
|
||||||
use crate::dom::element::{
|
use crate::dom::element::{
|
||||||
cors_setting_for_element, reflect_cross_origin_attribute, set_cross_origin_attribute,
|
cors_setting_for_element, reflect_cross_origin_attribute, set_cross_origin_attribute,
|
||||||
};
|
};
|
||||||
use crate::dom::element::{AttributeMutation, Element};
|
use crate::dom::element::{AttributeMutation, Element, ElementCreator};
|
||||||
use crate::dom::event::Event;
|
use crate::dom::event::Event;
|
||||||
use crate::dom::eventtarget::EventTarget;
|
use crate::dom::eventtarget::EventTarget;
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::dom::htmlelement::HTMLElement;
|
use crate::dom::htmlelement::HTMLElement;
|
||||||
|
use crate::dom::htmlscriptelement::HTMLScriptElement;
|
||||||
use crate::dom::htmlsourceelement::HTMLSourceElement;
|
use crate::dom::htmlsourceelement::HTMLSourceElement;
|
||||||
use crate::dom::htmlvideoelement::HTMLVideoElement;
|
use crate::dom::htmlvideoelement::HTMLVideoElement;
|
||||||
use crate::dom::mediaerror::MediaError;
|
use crate::dom::mediaerror::MediaError;
|
||||||
|
@ -45,6 +47,7 @@ use crate::dom::mediastream::MediaStream;
|
||||||
use crate::dom::node::{document_from_node, window_from_node, Node, NodeDamage, UnbindContext};
|
use crate::dom::node::{document_from_node, window_from_node, Node, NodeDamage, UnbindContext};
|
||||||
use crate::dom::performanceresourcetiming::InitiatorType;
|
use crate::dom::performanceresourcetiming::InitiatorType;
|
||||||
use crate::dom::promise::Promise;
|
use crate::dom::promise::Promise;
|
||||||
|
use crate::dom::shadowroot::IsUserAgentWidget;
|
||||||
use crate::dom::texttrack::TextTrack;
|
use crate::dom::texttrack::TextTrack;
|
||||||
use crate::dom::texttracklist::TextTrackList;
|
use crate::dom::texttracklist::TextTrackList;
|
||||||
use crate::dom::timeranges::{TimeRanges, TimeRangesContainer};
|
use crate::dom::timeranges::{TimeRanges, TimeRangesContainer};
|
||||||
|
@ -59,6 +62,7 @@ use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingLi
|
||||||
use crate::script_thread::ScriptThread;
|
use crate::script_thread::ScriptThread;
|
||||||
use crate::task_source::TaskSource;
|
use crate::task_source::TaskSource;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
|
use embedder_traits::resources::{self, Resource as EmbedderResource};
|
||||||
use euclid::Size2D;
|
use euclid::Size2D;
|
||||||
use headers::{ContentLength, ContentRange, HeaderMapExt};
|
use headers::{ContentLength, ContentRange, HeaderMapExt};
|
||||||
use html5ever::{LocalName, Prefix};
|
use html5ever::{LocalName, Prefix};
|
||||||
|
@ -335,6 +339,11 @@ pub struct HTMLMediaElement {
|
||||||
current_fetch_context: DomRefCell<Option<HTMLMediaElementFetchContext>>,
|
current_fetch_context: DomRefCell<Option<HTMLMediaElementFetchContext>>,
|
||||||
/// Player Id reported the player thread
|
/// Player Id reported the player thread
|
||||||
id: Cell<u64>,
|
id: Cell<u64>,
|
||||||
|
/// Media controls id.
|
||||||
|
/// In order to workaround the lack of privileged JS context, we secure the
|
||||||
|
/// the access to the "privileged" document.servoGetMediaControls(id) API by
|
||||||
|
/// keeping a whitelist of media controls identifiers.
|
||||||
|
media_controls_id: DomRefCell<Option<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://html.spec.whatwg.org/multipage/#dom-media-networkstate>
|
/// <https://html.spec.whatwg.org/multipage/#dom-media-networkstate>
|
||||||
|
@ -397,6 +406,7 @@ impl HTMLMediaElement {
|
||||||
next_timeupdate_event: Cell::new(time::get_time() + Duration::milliseconds(250)),
|
next_timeupdate_event: Cell::new(time::get_time() + Duration::milliseconds(250)),
|
||||||
current_fetch_context: DomRefCell::new(None),
|
current_fetch_context: DomRefCell::new(None),
|
||||||
id: Cell::new(0),
|
id: Cell::new(0),
|
||||||
|
media_controls_id: DomRefCell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1712,6 +1722,47 @@ impl HTMLMediaElement {
|
||||||
.start(0)
|
.start(0)
|
||||||
.unwrap_or_else(|_| self.playback_position.get())
|
.unwrap_or_else(|_| self.playback_position.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_controls(&self) {
|
||||||
|
println!("render_controls");
|
||||||
|
// XXX cannot render controls while parsing.
|
||||||
|
// XXX render controls as a top layer.
|
||||||
|
// XXX check that controls are not already rendered.
|
||||||
|
let element = self.htmlelement.upcast::<Element>();
|
||||||
|
if let Ok(shadow_root) = element.attach_shadow(IsUserAgentWidget::Yes) {
|
||||||
|
let document = document_from_node(self);
|
||||||
|
let script = HTMLScriptElement::new(
|
||||||
|
local_name!("script"),
|
||||||
|
None,
|
||||||
|
&document,
|
||||||
|
ElementCreator::ScriptCreated,
|
||||||
|
);
|
||||||
|
let mut media_controls = resources::read_string(EmbedderResource::MediaControls);
|
||||||
|
// This is our hacky way to temporarily workaround the lack of a privileged
|
||||||
|
// JS context.
|
||||||
|
// The media controls UI accesses the document.servoGetMediaControls(id) API
|
||||||
|
// to get an instance to the media controls ShadowRoot.
|
||||||
|
// `id` needs to match the internally generated UUID assigned to a media element.
|
||||||
|
let id = document.register_media_controls(&shadow_root);
|
||||||
|
let media_controls = media_controls.as_mut_str().replace("@@@id@@@", &id);
|
||||||
|
*self.media_controls_id.borrow_mut() = Some(id);
|
||||||
|
script
|
||||||
|
.upcast::<Node>()
|
||||||
|
.SetTextContent(Some(DOMString::from(media_controls)));
|
||||||
|
if let Err(e) = shadow_root
|
||||||
|
.upcast::<Node>()
|
||||||
|
.AppendChild(&*script.upcast::<Node>())
|
||||||
|
{
|
||||||
|
warn!("Could not render media controls {:?}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hide_controls(&self) {
|
||||||
|
println!("hide_controls");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX Placeholder for [https://github.com/servo/servo/issues/22293]
|
// XXX Placeholder for [https://github.com/servo/servo/issues/22293]
|
||||||
|
@ -1754,6 +1805,9 @@ impl Drop for HTMLMediaElement {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.shutdown_player(&client_context_id, player.clone());
|
.shutdown_player(&client_context_id, player.clone());
|
||||||
}
|
}
|
||||||
|
if let Some(ref id) = *self.media_controls_id.borrow() {
|
||||||
|
document_from_node(self).unregister_media_controls(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2177,19 +2231,23 @@ impl VirtualMethods for HTMLMediaElement {
|
||||||
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
|
||||||
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
self.super_type().unwrap().attribute_mutated(attr, mutation);
|
||||||
|
|
||||||
if &local_name!("muted") == attr.local_name() {
|
|
||||||
self.SetMuted(mutation.new_value(attr).is_some());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if mutation.new_value(attr).is_none() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match attr.local_name() {
|
match attr.local_name() {
|
||||||
|
&local_name!("muted") => {
|
||||||
|
self.SetMuted(mutation.new_value(attr).is_some());
|
||||||
|
},
|
||||||
&local_name!("src") => {
|
&local_name!("src") => {
|
||||||
|
if mutation.new_value(attr).is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.media_element_load_algorithm();
|
self.media_element_load_algorithm();
|
||||||
},
|
},
|
||||||
|
&local_name!("controls") => {
|
||||||
|
if mutation.new_value(attr).is_some() {
|
||||||
|
self.render_controls();
|
||||||
|
} else {
|
||||||
|
self.hide_controls();
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ use dom_struct::dom_struct;
|
||||||
use html5ever::{LocalName, Prefix};
|
use html5ever::{LocalName, Prefix};
|
||||||
use net_traits::ReferrerPolicy;
|
use net_traits::ReferrerPolicy;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
|
use servo_url::ServoUrl;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use style::media_queries::MediaList;
|
use style::media_queries::MediaList;
|
||||||
use style::parser::ParserContext as CssParserContext;
|
use style::parser::ParserContext as CssParserContext;
|
||||||
|
@ -111,10 +112,22 @@ impl HTMLStyleElement {
|
||||||
let mq =
|
let mq =
|
||||||
Arc::new(shared_lock.wrap(MediaList::parse(&context, &mut CssParser::new(&mut input))));
|
Arc::new(shared_lock.wrap(MediaList::parse(&context, &mut CssParser::new(&mut input))));
|
||||||
let loader = StylesheetLoader::for_element(self.upcast());
|
let loader = StylesheetLoader::for_element(self.upcast());
|
||||||
|
let (url, origin) = if let Some(shadow_root) = self
|
||||||
|
.upcast::<Node>()
|
||||||
|
.containing_shadow_root() {
|
||||||
|
if shadow_root.is_user_agent_widget() {
|
||||||
|
(ServoUrl::parse(&format!("chrome://{:?}", window.get_url().to_string())).unwrap(), Origin::UserAgent)
|
||||||
|
} else {
|
||||||
|
(window.get_url(), Origin::Author)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(window.get_url(), Origin::Author)
|
||||||
|
};
|
||||||
|
|
||||||
let sheet = Stylesheet::from_str(
|
let sheet = Stylesheet::from_str(
|
||||||
&data,
|
&data,
|
||||||
window.get_url(),
|
url,
|
||||||
Origin::Author,
|
origin,
|
||||||
mq,
|
mq,
|
||||||
shared_lock,
|
shared_lock,
|
||||||
Some(&loader),
|
Some(&loader),
|
||||||
|
|
|
@ -28,6 +28,13 @@ use style::media_queries::Device;
|
||||||
use style::shared_lock::SharedRwLockReadGuard;
|
use style::shared_lock::SharedRwLockReadGuard;
|
||||||
use style::stylesheets::Stylesheet;
|
use style::stylesheets::Stylesheet;
|
||||||
|
|
||||||
|
/// Whether a shadow root hosts an User Agent widget.
|
||||||
|
#[derive(JSTraceable, MallocSizeOf, PartialEq)]
|
||||||
|
pub enum IsUserAgentWidget {
|
||||||
|
No,
|
||||||
|
Yes,
|
||||||
|
}
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#interface-shadowroot
|
// https://dom.spec.whatwg.org/#interface-shadowroot
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct ShadowRoot {
|
pub struct ShadowRoot {
|
||||||
|
@ -39,11 +46,13 @@ pub struct ShadowRoot {
|
||||||
author_styles: DomRefCell<AuthorStyles<StyleSheetInDocument>>,
|
author_styles: DomRefCell<AuthorStyles<StyleSheetInDocument>>,
|
||||||
stylesheet_list: MutNullableDom<StyleSheetList>,
|
stylesheet_list: MutNullableDom<StyleSheetList>,
|
||||||
window: Dom<Window>,
|
window: Dom<Window>,
|
||||||
|
/// Whether this ShadowRoot hosts a User Agent widget.
|
||||||
|
is_widget: IsUserAgentWidget,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShadowRoot {
|
impl ShadowRoot {
|
||||||
#[allow(unrooted_must_root)]
|
#[allow(unrooted_must_root)]
|
||||||
fn new_inherited(host: &Element, document: &Document) -> ShadowRoot {
|
fn new_inherited(host: &Element, document: &Document, is_widget: IsUserAgentWidget) -> ShadowRoot {
|
||||||
let document_fragment = DocumentFragment::new_inherited(document);
|
let document_fragment = DocumentFragment::new_inherited(document);
|
||||||
let node = document_fragment.upcast::<Node>();
|
let node = document_fragment.upcast::<Node>();
|
||||||
node.set_flag(NodeFlags::IS_IN_SHADOW_TREE, true);
|
node.set_flag(NodeFlags::IS_IN_SHADOW_TREE, true);
|
||||||
|
@ -59,12 +68,13 @@ impl ShadowRoot {
|
||||||
author_styles: DomRefCell::new(AuthorStyles::new()),
|
author_styles: DomRefCell::new(AuthorStyles::new()),
|
||||||
stylesheet_list: MutNullableDom::new(None),
|
stylesheet_list: MutNullableDom::new(None),
|
||||||
window: Dom::from_ref(document.window()),
|
window: Dom::from_ref(document.window()),
|
||||||
|
is_widget,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(host: &Element, document: &Document) -> DomRoot<ShadowRoot> {
|
pub fn new(host: &Element, document: &Document, is_widget: IsUserAgentWidget) -> DomRoot<ShadowRoot> {
|
||||||
reflect_dom_object(
|
reflect_dom_object(
|
||||||
Box::new(ShadowRoot::new_inherited(host, document)),
|
Box::new(ShadowRoot::new_inherited(host, document, is_widget)),
|
||||||
document.window(),
|
document.window(),
|
||||||
ShadowRootBinding::Wrap,
|
ShadowRootBinding::Wrap,
|
||||||
)
|
)
|
||||||
|
@ -152,6 +162,10 @@ impl ShadowRoot {
|
||||||
root,
|
root,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_user_agent_widget(&self) -> bool {
|
||||||
|
self.is_widget == IsUserAgentWidget::Yes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShadowRootMethods for ShadowRoot {
|
impl ShadowRootMethods for ShadowRoot {
|
||||||
|
|
|
@ -211,3 +211,9 @@ partial interface Document {
|
||||||
};
|
};
|
||||||
|
|
||||||
Document implements DocumentOrShadowRoot;
|
Document implements DocumentOrShadowRoot;
|
||||||
|
|
||||||
|
// Servo internal API.
|
||||||
|
partial interface Document {
|
||||||
|
[Throws]
|
||||||
|
ShadowRoot servoGetMediaControls(DOMString id);
|
||||||
|
};
|
||||||
|
|
|
@ -141,7 +141,15 @@ impl FetchResponseListener for StylesheetContext {
|
||||||
// TODO: Get the actual value. http://dev.w3.org/csswg/css-syntax/#environment-encoding
|
// TODO: Get the actual value. http://dev.w3.org/csswg/css-syntax/#environment-encoding
|
||||||
let environment_encoding = UTF_8;
|
let environment_encoding = UTF_8;
|
||||||
let protocol_encoding_label = metadata.charset.as_ref().map(|s| &**s);
|
let protocol_encoding_label = metadata.charset.as_ref().map(|s| &**s);
|
||||||
let final_url = metadata.final_url;
|
let final_url = if let Some(ref shadow_root) = self.shadow_root {
|
||||||
|
if shadow_root.root().is_user_agent_widget() {
|
||||||
|
ServoUrl::parse(&format!("chrome://{:?}", metadata.final_url.to_string())).unwrap()
|
||||||
|
} else {
|
||||||
|
metadata.final_url
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
metadata.final_url
|
||||||
|
};
|
||||||
|
|
||||||
let win = window_from_node(&*elem);
|
let win = window_from_node(&*elem);
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ fn filename(file: Resource) -> &'static str {
|
||||||
Resource::PresentationalHintsCSS => "presentational-hints.css",
|
Resource::PresentationalHintsCSS => "presentational-hints.css",
|
||||||
Resource::QuirksModeCSS => "quirks-mode.css",
|
Resource::QuirksModeCSS => "quirks-mode.css",
|
||||||
Resource::RippyPNG => "rippy.png",
|
Resource::RippyPNG => "rippy.png",
|
||||||
|
Resource::MediaControls => "media_controls.js",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -702,6 +702,9 @@ impl ResourceReaderMethods for ResourceReaderInstance {
|
||||||
Resource::BluetoothBlocklist => {
|
Resource::BluetoothBlocklist => {
|
||||||
&include_bytes!("../../../../resources/gatt_blocklist.txt")[..]
|
&include_bytes!("../../../../resources/gatt_blocklist.txt")[..]
|
||||||
},
|
},
|
||||||
|
Resource::MediaControls => {
|
||||||
|
&include_bytes!("../../../../resources/media_controls.js")[..]
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
23
resources/media_controls.js
Normal file
23
resources/media_controls.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
console.log('YO');
|
||||||
|
let controls = document.servoGetMediaControls("@@@id@@@");
|
||||||
|
|
||||||
|
let style = document.createElement("style");
|
||||||
|
style.textContent = `#controls {
|
||||||
|
-servo-top-layer: top;
|
||||||
|
display: block;
|
||||||
|
position: fixed;
|
||||||
|
left: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
height: 30px;
|
||||||
|
width: 100%;
|
||||||
|
background: blue;
|
||||||
|
}`;
|
||||||
|
controls.appendChild(style);
|
||||||
|
|
||||||
|
let div = document.createElement("div");
|
||||||
|
div.setAttribute("id", "controls");
|
||||||
|
let button = document.createElement("button");
|
||||||
|
button.textContent = "Click me";
|
||||||
|
div.appendChild(button);
|
||||||
|
controls.appendChild(div);
|
||||||
|
console.log('INNER', div.innerHTML);
|
Loading…
Add table
Add a link
Reference in a new issue