diff options
Diffstat (limited to 'components/script')
20 files changed, 779 insertions, 110 deletions
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index e645fb181d5..e92d1d5c452 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -85,7 +85,6 @@ use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming, Re use offscreen_gl_context::GLLimits; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; -use script_layout_interface::reporter::CSSErrorReporter; use script_layout_interface::rpc::LayoutRPC; use script_layout_interface::OpaqueStyleAndLayoutData; use script_traits::DrawAPaintImageResult; @@ -116,6 +115,7 @@ use std::sync::{Arc, Mutex}; use std::time::{Instant, SystemTime}; use style::attr::{AttrIdentifier, AttrValue, LengthOrPercentageOrAuto}; use style::context::QuirksMode; +use style::dom::OpaqueNode; use style::element_state::*; use style::media_queries::MediaList; use style::properties::PropertyDeclarationBlock; @@ -419,7 +419,7 @@ unsafe_no_jsmanaged_fields!(BufferQueue, QuirksMode, StrTendril); unsafe_no_jsmanaged_fields!(Runtime); unsafe_no_jsmanaged_fields!(HeaderMap, Method); unsafe_no_jsmanaged_fields!(WindowProxyHandler); -unsafe_no_jsmanaged_fields!(UntrustedNodeAddress); +unsafe_no_jsmanaged_fields!(UntrustedNodeAddress, OpaqueNode); unsafe_no_jsmanaged_fields!(LengthOrPercentageOrAuto); unsafe_no_jsmanaged_fields!(RGBA); unsafe_no_jsmanaged_fields!(StorageType); @@ -455,7 +455,6 @@ unsafe_no_jsmanaged_fields!(Instant); unsafe_no_jsmanaged_fields!(RelativePos); unsafe_no_jsmanaged_fields!(OpaqueStyleAndLayoutData); unsafe_no_jsmanaged_fields!(PathBuf); -unsafe_no_jsmanaged_fields!(CSSErrorReporter); unsafe_no_jsmanaged_fields!(DrawAPaintImageResult); unsafe_no_jsmanaged_fields!(DocumentId); unsafe_no_jsmanaged_fields!(ImageKey); diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index d461cd11527..155098247b6 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -1508,6 +1508,11 @@ impl Element { .unwrap_or_else(|_| USVString(value.to_owned())) } + pub fn set_url_attribute(&self, local_name: &LocalName, value: USVString) { + assert!(*local_name == local_name.to_ascii_lowercase()); + self.set_attribute(local_name, AttrValue::String(value.to_string())); + } + pub fn get_string_attribute(&self, local_name: &LocalName) -> DOMString { match self.get_attribute(&ns!(), local_name) { Some(x) => x.Value(), diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index cf6466b1f27..d11cd2ed4de 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -38,13 +38,13 @@ use crate::dom::progressevent::ProgressEvent; use crate::dom::values::UNSIGNED_LONG_MAX; use crate::dom::virtualmethods::VirtualMethods; use crate::dom::window::Window; +use crate::image_listener::{add_cache_listener_for_element, ImageCacheListener}; use crate::microtask::{Microtask, MicrotaskRunnable}; use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use crate::script_thread::ScriptThread; use crate::task_source::TaskSource; use app_units::{Au, AU_PER_PX}; use cssparser::{Parser, ParserInput}; - use dom_struct::dom_struct; use euclid::Point2D; use html5ever::{LocalName, Prefix}; @@ -167,7 +167,7 @@ struct ImageContext { /// The cache ID for this request. id: PendingImageId, /// Used to mark abort - aborted: Cell<bool>, + aborted: bool, /// The document associated with this request doc: Trusted<Document>, /// timing data for this resource @@ -193,7 +193,7 @@ impl FetchResponseListener for ImageContext { if let Some(ref content_type) = metadata.content_type { let mime: Mime = content_type.clone().into_inner().into(); if mime.type_() == mime::MULTIPART && mime.subtype().as_str() == "x-mixed-replace" { - self.aborted.set(true); + self.aborted = true; } } } @@ -255,51 +255,13 @@ impl ResourceTimingListener for ImageContext { impl PreInvoke for ImageContext { fn should_invoke(&self) -> bool { - !self.aborted.get() + !self.aborted } } impl HTMLImageElement { /// Update the current image with a valid URL. fn fetch_image(&self, img_url: &ServoUrl) { - fn add_cache_listener_for_element( - image_cache: Arc<dyn ImageCache>, - id: PendingImageId, - elem: &HTMLImageElement, - ) { - let trusted_node = Trusted::new(elem); - let (responder_sender, responder_receiver) = ipc::channel().unwrap(); - - let window = window_from_node(elem); - let (task_source, canceller) = window - .task_manager() - .networking_task_source_with_canceller(); - let generation = elem.generation.get(); - ROUTER.add_route( - responder_receiver.to_opaque(), - Box::new(move |message| { - debug!("Got image {:?}", message); - // Return the image via a message to the script thread, which marks - // the element as dirty and triggers a reflow. - let element = trusted_node.clone(); - let image = message.to().unwrap(); - // FIXME(nox): Why are errors silenced here? - let _ = task_source.queue_with_canceller( - task!(process_image_response: move || { - let element = element.root(); - // Ignore any image response for a previous request that has been discarded. - if generation == element.generation.get() { - element.process_image_response(image); - } - }), - &canceller, - ); - }), - ); - - image_cache.add_listener(id, ImageResponder::new(responder_sender, id)); - } - let window = window_from_node(self); let image_cache = window.image_cache(); let response = image_cache.find_image_or_metadata( @@ -317,7 +279,7 @@ impl HTMLImageElement { }, Err(ImageState::Pending(id)) => { - add_cache_listener_for_element(image_cache.clone(), id, self); + add_cache_listener_for_element(image_cache, id, self); }, Err(ImageState::LoadError) => { @@ -339,7 +301,7 @@ impl HTMLImageElement { image_cache: window.image_cache(), status: Ok(()), id: id, - aborted: Cell::new(false), + aborted: false, doc: Trusted::new(&document), resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), url: img_url.clone(), @@ -1735,6 +1697,16 @@ impl FormControl for HTMLImageElement { } } +impl ImageCacheListener for HTMLImageElement { + fn generation_id(&self) -> u32 { + self.generation.get() + } + + fn process_image_response(&self, response: ImageResponse) { + self.process_image_response(response); + } +} + fn image_dimension_setter(element: &Element, attr: LocalName, value: u32) { // This setter is a bit weird: the IDL type is unsigned long, but it's parsed as // a dimension for rendering. diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index dd83c2061de..97170befaf1 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -51,10 +51,13 @@ use http::header::{self, HeaderMap, HeaderValue}; use ipc_channel::ipc; use ipc_channel::router::ROUTER; use mime::{self, Mime}; +use net_traits::image::base::Image; +use net_traits::image_cache::ImageResponse; use net_traits::request::{CredentialsMode, Destination, RequestInit}; use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, Metadata}; use net_traits::{NetworkError, ResourceFetchTiming, ResourceTimingType}; use script_layout_interface::HTMLMediaData; +use servo_config::prefs::PREFS; use servo_media::player::frame::{Frame, FrameRenderer}; use servo_media::player::{PlaybackState, Player, PlayerError, PlayerEvent, StreamType}; use servo_media::ServoMedia; @@ -85,6 +88,12 @@ impl MediaFrameRenderer { very_old_frame: None, } } + + fn render_poster_frame(&mut self, image: Arc<Image>) { + if let Some(image_id) = image.id { + self.current_frame = Some((image_id, image.width as i32, image.height as i32)); + } + } } impl FrameRenderer for MediaFrameRenderer { @@ -135,14 +144,11 @@ impl FrameRenderer for MediaFrameRenderer { self.current_frame = Some((image_key, frame.get_width(), frame.get_height())); }, } - self.api.update_resources(txn.resource_updates); } } #[dom_struct] -// FIXME(nox): A lot of tasks queued for this element should probably be in the -// media element event task source. pub struct HTMLMediaElement { htmlelement: HTMLElement, /// <https://html.spec.whatwg.org/multipage/#dom-media-networkstate> @@ -197,7 +203,7 @@ pub struct HTMLMediaElement { resource_url: DomRefCell<Option<ServoUrl>>, /// https://html.spec.whatwg.org/multipage/#dom-media-played #[ignore_malloc_size_of = "Rc"] - played: Rc<DomRefCell<TimeRangesContainer>>, + played: DomRefCell<TimeRangesContainer>, /// https://html.spec.whatwg.org/multipage/#dom-media-texttracks text_tracks_list: MutNullableDom<TextTrackList>, /// Time of last timeupdate notification. @@ -258,7 +264,7 @@ impl HTMLMediaElement { volume: Cell::new(1.0), seeking: Cell::new(false), resource_url: DomRefCell::new(None), - played: Rc::new(DomRefCell::new(TimeRangesContainer::new())), + played: DomRefCell::new(TimeRangesContainer::new()), text_tracks_list: Default::default(), next_timeupdate_event: Cell::new(time::get_time() + Duration::milliseconds(250)), current_fetch_context: DomRefCell::new(None), @@ -293,7 +299,7 @@ impl HTMLMediaElement { /// we pass true to that method again. /// /// <https://html.spec.whatwg.org/multipage/#delaying-the-load-event-flag> - fn delay_load_event(&self, delay: bool) { + pub fn delay_load_event(&self, delay: bool) { let mut blocker = self.delaying_the_load_event_flag.borrow_mut(); if delay && blocker.is_none() { *blocker = Some(LoadBlocker::new(&document_from_node(self), LoadType::Media)); @@ -1080,6 +1086,30 @@ impl HTMLMediaElement { task_source.queue_simple_event(self.upcast(), atom!("seeked"), &window); } + /// https://html.spec.whatwg.org/multipage/#poster-frame + pub fn process_poster_response(&self, image: ImageResponse) { + if !self.show_poster.get() { + return; + } + + // Step 6. + if let ImageResponse::Loaded(image, _) = image { + self.frame_renderer + .lock() + .unwrap() + .render_poster_frame(image); + self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage); + if let Some(testing_on) = PREFS.get("media.testing.enabled").as_boolean() { + if !testing_on { + return; + } + let window = window_from_node(self); + let task_source = window.task_manager().media_element_task_source(); + task_source.queue_simple_event(self.upcast(), atom!("postershown"), &window); + } + } + } + fn setup_media_player(&self) -> Result<(), PlayerError> { let (action_sender, action_receiver) = ipc::channel().unwrap(); @@ -1624,7 +1654,18 @@ impl HTMLMediaElementMethods for HTMLMediaElement { // https://html.spec.whatwg.org/multipage/#dom-media-played fn Played(&self) -> DomRoot<TimeRanges> { - TimeRanges::new(self.global().as_window(), self.played.clone()) + TimeRanges::new(self.global().as_window(), self.played.borrow().clone()) + } + + // https://html.spec.whatwg.org/multipage/#dom-media-buffered + fn Buffered(&self) -> DomRoot<TimeRanges> { + let mut buffered = TimeRangesContainer::new(); + if let Ok(ranges) = self.player.buffered() { + for range in ranges { + let _ = buffered.add(range.start as f64, range.end as f64); + } + } + TimeRanges::new(self.global().as_window(), buffered) } // https://html.spec.whatwg.org/multipage/#dom-media-texttracks @@ -1693,11 +1734,13 @@ impl VirtualMethods for HTMLMediaElement { fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) { self.super_type().unwrap().attribute_mutated(attr, mutation); + if mutation.new_value(attr).is_none() { + return; + } + match attr.local_name() { &local_name!("src") => { - if mutation.new_value(attr).is_some() { - self.media_element_load_algorithm(); - } + self.media_element_load_algorithm(); }, _ => (), }; diff --git a/components/script/dom/htmlvideoelement.rs b/components/script/dom/htmlvideoelement.rs index bc7e4510b66..e8c831221d0 100644 --- a/components/script/dom/htmlvideoelement.rs +++ b/components/script/dom/htmlvideoelement.rs @@ -2,15 +2,44 @@ * 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 crate::document_loader::{LoadBlocker, LoadType}; +use crate::dom::attr::Attr; +use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::HTMLVideoElementBinding; use crate::dom::bindings::codegen::Bindings::HTMLVideoElementBinding::HTMLVideoElementMethods; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::refcounted::Trusted; +use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; use crate::dom::document::Document; +use crate::dom::element::{AttributeMutation, Element}; +use crate::dom::globalscope::GlobalScope; use crate::dom::htmlmediaelement::{HTMLMediaElement, ReadyState}; -use crate::dom::node::Node; +use crate::dom::node::{document_from_node, window_from_node, Node}; +use crate::dom::performanceresourcetiming::InitiatorType; +use crate::dom::virtualmethods::VirtualMethods; +use crate::fetch::FetchCanceller; +use crate::image_listener::{add_cache_listener_for_element, ImageCacheListener}; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use dom_struct::dom_struct; use html5ever::{LocalName, Prefix}; +use ipc_channel::ipc; +use ipc_channel::router::ROUTER; +use net_traits::image_cache::UsePlaceholder; +use net_traits::image_cache::{CanRequestImages, ImageCache, ImageOrMetadataAvailable}; +use net_traits::image_cache::{ImageResponse, ImageState, PendingImageId}; +use net_traits::request::{CredentialsMode, Destination, RequestInit}; +use net_traits::{ + CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, FetchResponseMsg, +}; +use net_traits::{NetworkError, ResourceFetchTiming, ResourceTimingType}; +use servo_url::ServoUrl; use std::cell::Cell; +use std::sync::{Arc, Mutex}; + +const DEFAULT_WIDTH: u32 = 300; +const DEFAULT_HEIGHT: u32 = 150; #[dom_struct] pub struct HTMLVideoElement { @@ -19,6 +48,13 @@ pub struct HTMLVideoElement { video_width: Cell<u32>, /// https://html.spec.whatwg.org/multipage/#dom-video-videoheight video_height: Cell<u32>, + /// Incremented whenever tasks associated with this element are cancelled. + generation_id: Cell<u32>, + /// Poster frame fetch request canceller. + poster_frame_canceller: DomRefCell<FetchCanceller>, + /// Load event blocker. Will block the load event while the poster frame + /// is being fetched. + load_blocker: DomRefCell<Option<LoadBlocker>>, } impl HTMLVideoElement { @@ -29,8 +65,11 @@ impl HTMLVideoElement { ) -> HTMLVideoElement { HTMLVideoElement { htmlmediaelement: HTMLMediaElement::new_inherited(local_name, prefix, document), - video_width: Cell::new(0), - video_height: Cell::new(0), + video_width: Cell::new(DEFAULT_WIDTH), + video_height: Cell::new(DEFAULT_HEIGHT), + generation_id: Cell::new(0), + poster_frame_canceller: DomRefCell::new(Default::default()), + load_blocker: Default::default(), } } @@ -64,6 +103,117 @@ impl HTMLVideoElement { pub fn set_video_height(&self, height: u32) { self.video_height.set(height); } + + pub fn allow_load_event(&self) { + LoadBlocker::terminate(&mut *self.load_blocker.borrow_mut()); + } + + /// https://html.spec.whatwg.org/multipage/#poster-frame + fn fetch_poster_frame(&self, poster_url: &str) { + // Step 1. + let cancel_receiver = self.poster_frame_canceller.borrow_mut().initialize(); + self.generation_id.set(self.generation_id.get() + 1); + + // Step 2. + if poster_url.is_empty() { + return; + } + + // Step 3. + let poster_url = match document_from_node(self).url().join(&poster_url) { + Ok(url) => url, + Err(_) => return, + }; + + // Step 4. + // We use the image cache for poster frames so we save as much + // network activity as possible. + let window = window_from_node(self); + let image_cache = window.image_cache(); + let response = image_cache.find_image_or_metadata( + poster_url.clone().into(), + UsePlaceholder::No, + CanRequestImages::Yes, + ); + match response { + Ok(ImageOrMetadataAvailable::ImageAvailable(image, url)) => { + self.process_image_response(ImageResponse::Loaded(image, url)); + }, + + Err(ImageState::Pending(id)) => { + add_cache_listener_for_element(image_cache, id, self); + }, + + Err(ImageState::NotRequested(id)) => { + add_cache_listener_for_element(image_cache, id, self); + self.do_fetch_poster_frame(poster_url, id, cancel_receiver); + }, + + _ => (), + } + } + + /// https://html.spec.whatwg.org/multipage/#poster-frame + fn do_fetch_poster_frame( + &self, + poster_url: ServoUrl, + id: PendingImageId, + cancel_receiver: ipc::IpcReceiver<()>, + ) { + // Continuation of step 4. + let document = document_from_node(self); + let request = RequestInit { + url: poster_url.clone(), + destination: Destination::Image, + credentials_mode: CredentialsMode::Include, + use_url_credentials: true, + origin: document.origin().immutable().clone(), + pipeline_id: Some(document.global().pipeline_id()), + ..RequestInit::default() + }; + + // Step 5. + // This delay must be independent from the ones created by HTMLMediaElement during + // its media load algorithm, otherwise a code like + // <video poster="poster.png"></video> + // (which triggers no media load algorithm unless a explicit call to .load() is done) + // will block the document's load event forever. + let mut blocker = self.load_blocker.borrow_mut(); + LoadBlocker::terminate(&mut *blocker); + *blocker = Some(LoadBlocker::new( + &document_from_node(self), + LoadType::Image(poster_url.clone()), + )); + + let window = window_from_node(self); + let context = Arc::new(Mutex::new(PosterFrameFetchContext::new( + self, poster_url, id, + ))); + + let (action_sender, action_receiver) = ipc::channel().unwrap(); + let (task_source, canceller) = window + .task_manager() + .networking_task_source_with_canceller(); + let listener = NetworkListener { + context, + task_source, + canceller: Some(canceller), + }; + ROUTER.add_route( + action_receiver.to_opaque(), + Box::new(move |message| { + listener.notify_fetch(message.to().unwrap()); + }), + ); + let global = self.global(); + global + .core_resource_thread() + .send(CoreResourceMsg::Fetch( + request, + FetchChannels::ResponseMsg(action_sender, Some(cancel_receiver)), + )) + .unwrap(); + } } impl HTMLVideoElementMethods for HTMLVideoElement { @@ -82,4 +232,152 @@ impl HTMLVideoElementMethods for HTMLVideoElement { } self.video_height.get() } + + // https://html.spec.whatwg.org/multipage/#dom-video-poster + make_getter!(Poster, "poster"); + + // https://html.spec.whatwg.org/multipage/#dom-video-poster + make_setter!(SetPoster, "poster"); + + // For testing purposes only. This is not an event from + // https://html.spec.whatwg.org/multipage/#dom-video-poster + event_handler!(postershown, GetOnpostershown, SetOnpostershown); +} + +impl VirtualMethods for HTMLVideoElement { + fn super_type(&self) -> Option<&dyn VirtualMethods> { + Some(self.upcast::<HTMLMediaElement>() as &dyn VirtualMethods) + } + + fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) { + self.super_type().unwrap().attribute_mutated(attr, mutation); + + if let Some(new_value) = mutation.new_value(attr) { + match attr.local_name() { + &local_name!("poster") => { + self.fetch_poster_frame(&new_value); + }, + _ => (), + }; + } + } +} + +impl ImageCacheListener for HTMLVideoElement { + fn generation_id(&self) -> u32 { + self.generation_id.get() + } + + fn process_image_response(&self, response: ImageResponse) { + self.htmlmediaelement.process_poster_response(response); + } +} + +struct PosterFrameFetchContext { + /// Reference to the script thread image cache. + image_cache: Arc<dyn ImageCache>, + /// The element that initiated the request. + elem: Trusted<HTMLVideoElement>, + /// The cache ID for this request. + id: PendingImageId, + /// True if this response is invalid and should be ignored. + cancelled: bool, + /// Timing data for this resource + resource_timing: ResourceFetchTiming, + /// Url for the resource + url: ServoUrl, +} + +impl FetchResponseListener for PosterFrameFetchContext { + fn process_request_body(&mut self) {} + fn process_request_eof(&mut self) {} + + fn process_response(&mut self, metadata: Result<FetchMetadata, NetworkError>) { + self.image_cache + .notify_pending_response(self.id, FetchResponseMsg::ProcessResponse(metadata.clone())); + + let metadata = metadata.ok().map(|meta| match meta { + FetchMetadata::Unfiltered(m) => m, + FetchMetadata::Filtered { unsafe_, .. } => unsafe_, + }); + + let status_is_ok = metadata + .as_ref() + .and_then(|m| m.status.as_ref()) + .map_or(true, |s| s.0 >= 200 && s.0 < 300); + + if !status_is_ok { + self.cancelled = true; + self.elem + .root() + .poster_frame_canceller + .borrow_mut() + .cancel(); + } + } + + fn process_response_chunk(&mut self, payload: Vec<u8>) { + if self.cancelled { + // An error was received previously, skip processing the payload. + return; + } + + self.image_cache + .notify_pending_response(self.id, FetchResponseMsg::ProcessResponseChunk(payload)); + } + + fn process_response_eof(&mut self, response: Result<ResourceFetchTiming, NetworkError>) { + self.elem.root().allow_load_event(); + self.image_cache + .notify_pending_response(self.id, FetchResponseMsg::ProcessResponseEOF(response)); + } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + network_listener::submit_timing(self) + } +} + +impl ResourceTimingListener for PosterFrameFetchContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + let initiator_type = InitiatorType::LocalName( + self.elem + .root() + .upcast::<Element>() + .local_name() + .to_string(), + ); + (initiator_type, self.url.clone()) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + document_from_node(&*self.elem.root()).global() + } +} + +impl PreInvoke for PosterFrameFetchContext { + fn should_invoke(&self) -> bool { + true + } +} + +impl PosterFrameFetchContext { + fn new(elem: &HTMLVideoElement, url: ServoUrl, id: PendingImageId) -> PosterFrameFetchContext { + let window = window_from_node(elem); + PosterFrameFetchContext { + image_cache: window.image_cache(), + elem: Trusted::new(elem), + id, + cancelled: false, + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), + url, + } + } } diff --git a/components/script/dom/macros.rs b/components/script/dom/macros.rs index 454f1a678f8..a566fee4fb6 100644 --- a/components/script/dom/macros.rs +++ b/components/script/dom/macros.rs @@ -112,8 +112,8 @@ macro_rules! make_url_setter( use crate::dom::bindings::inheritance::Castable; use crate::dom::element::Element; let element = self.upcast::<Element>(); - element.set_string_attribute(&local_name!($htmlname), - DOMString::from(value.0)); + element.set_url_attribute(&local_name!($htmlname), + value); } ); ); diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 675b9dcd403..d316000eee4 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -407,6 +407,8 @@ pub mod nodeiterator; pub mod nodelist; pub mod offlineaudiocompletionevent; pub mod offlineaudiocontext; +pub mod offscreencanvas; +pub mod offscreencanvasrenderingcontext2d; pub mod oscillatornode; pub mod pagetransitionevent; pub mod paintrenderingcontext2d; diff --git a/components/script/dom/offscreencanvas.rs b/components/script/dom/offscreencanvas.rs new file mode 100644 index 00000000000..93c836be5ae --- /dev/null +++ b/components/script/dom/offscreencanvas.rs @@ -0,0 +1,146 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::OffscreenCanvasBinding::{ + OffscreenCanvasMethods, OffscreenRenderingContext, Wrap as OffscreenCanvasWrap, +}; +use crate::dom::bindings::error::Fallible; +use crate::dom::bindings::reflector::reflect_dom_object; +use crate::dom::bindings::reflector::DomObject; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::bindings::str::DOMString; +use crate::dom::eventtarget::EventTarget; +use crate::dom::globalscope::GlobalScope; +use crate::dom::htmlcanvaselement::HTMLCanvasElement; +use crate::dom::offscreencanvasrenderingcontext2d::OffscreenCanvasRenderingContext2D; +use dom_struct::dom_struct; +use euclid::Size2D; +use js::jsapi::JSContext; +use js::rust::HandleValue; +use ref_filter_map; +use std::cell::Cell; +use std::cell::Ref; + +#[must_root] +#[derive(Clone, JSTraceable, MallocSizeOf)] +pub enum OffscreenCanvasContext { + OffscreenContext2d(Dom<OffscreenCanvasRenderingContext2D>), + //WebGL(Dom<WebGLRenderingContext>), + //WebGL2(Dom<WebGL2RenderingContext>), +} + +#[dom_struct] +pub struct OffscreenCanvas { + eventtarget: EventTarget, + height: Cell<u64>, + width: Cell<u64>, + context: DomRefCell<Option<OffscreenCanvasContext>>, + placeholder: Option<Dom<HTMLCanvasElement>>, +} + +impl OffscreenCanvas { + pub fn new_inherited( + height: u64, + width: u64, + placeholder: Option<&HTMLCanvasElement>, + ) -> OffscreenCanvas { + OffscreenCanvas { + eventtarget: EventTarget::new_inherited(), + height: Cell::new(height), + width: Cell::new(width), + context: DomRefCell::new(None), + placeholder: placeholder.map(Dom::from_ref), + } + } + + pub fn new( + global: &GlobalScope, + height: u64, + width: u64, + placeholder: Option<&HTMLCanvasElement>, + ) -> DomRoot<OffscreenCanvas> { + reflect_dom_object( + Box::new(OffscreenCanvas::new_inherited(height, width, placeholder)), + global, + OffscreenCanvasWrap, + ) + } + + pub fn Constructor( + global: &GlobalScope, + height: u64, + width: u64, + ) -> Fallible<DomRoot<OffscreenCanvas>> { + let offscreencanvas = OffscreenCanvas::new(global, height, width, None); + Ok(offscreencanvas) + } + + pub fn get_size(&self) -> Size2D<u64> { + Size2D::new(self.Width(), self.Height()) + } + + pub fn context(&self) -> Option<Ref<OffscreenCanvasContext>> { + ref_filter_map::ref_filter_map(self.context.borrow(), |ctx| ctx.as_ref()) + } + + #[allow(unsafe_code)] + fn get_or_init_2d_context(&self) -> Option<DomRoot<OffscreenCanvasRenderingContext2D>> { + if let Some(ctx) = self.context() { + return match *ctx { + OffscreenCanvasContext::OffscreenContext2d(ref ctx) => Some(DomRoot::from_ref(ctx)), + }; + } + let size = self.get_size(); + let context = OffscreenCanvasRenderingContext2D::new(&self.global(), self, size); + *self.context.borrow_mut() = Some(OffscreenCanvasContext::OffscreenContext2d( + Dom::from_ref(&*context), + )); + Some(context) + } +} + +impl OffscreenCanvasMethods for OffscreenCanvas { + // https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-getcontext + #[allow(unsafe_code)] + unsafe fn GetContext( + &self, + _cx: *mut JSContext, + id: DOMString, + _options: HandleValue, + ) -> Option<OffscreenRenderingContext> { + match &*id { + "2d" => self + .get_or_init_2d_context() + .map(OffscreenRenderingContext::OffscreenCanvasRenderingContext2D), + /*"webgl" | "experimental-webgl" => self + .get_or_init_webgl_context(cx, options) + .map(OffscreenRenderingContext::WebGLRenderingContext), + "webgl2" | "experimental-webgl2" => self + .get_or_init_webgl2_context(cx, options) + .map(OffscreenRenderingContext::WebGL2RenderingContext),*/ + _ => None, + } + } + + // https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-width + fn Width(&self) -> u64 { + return self.width.get(); + } + + // https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-width + fn SetWidth(&self, value: u64) { + self.width.set(value); + } + + // https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-height + fn Height(&self) -> u64 { + return self.height.get(); + } + + // https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-height + fn SetHeight(&self, value: u64) { + self.height.set(value); + } +} diff --git a/components/script/dom/offscreencanvasrenderingcontext2d.rs b/components/script/dom/offscreencanvasrenderingcontext2d.rs new file mode 100644 index 00000000000..a02d2d4a85b --- /dev/null +++ b/components/script/dom/offscreencanvasrenderingcontext2d.rs @@ -0,0 +1,55 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::codegen::Bindings::OffscreenCanvasRenderingContext2DBinding; +use crate::dom::bindings::codegen::Bindings::OffscreenCanvasRenderingContext2DBinding::OffscreenCanvasRenderingContext2DMethods; +use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::globalscope::GlobalScope; +use crate::dom::offscreencanvas::OffscreenCanvas; +use dom_struct::dom_struct; +use euclid::Size2D; + +#[dom_struct] +pub struct OffscreenCanvasRenderingContext2D { + reflector_: Reflector, + canvas: Option<Dom<OffscreenCanvas>>, +} + +impl OffscreenCanvasRenderingContext2D { + pub fn new_inherited( + _global: &GlobalScope, + canvas: Option<&OffscreenCanvas>, + _size: Size2D<u64>, + ) -> OffscreenCanvasRenderingContext2D { + OffscreenCanvasRenderingContext2D { + reflector_: Reflector::new(), + canvas: canvas.map(Dom::from_ref), + } + } + + pub fn new( + _global: &GlobalScope, + canvas: &OffscreenCanvas, + _size: Size2D<u64>, + ) -> DomRoot<OffscreenCanvasRenderingContext2D> { + let boxed = Box::new(OffscreenCanvasRenderingContext2D::new_inherited( + _global, + Some(canvas), + _size, + )); + reflect_dom_object( + boxed, + _global, + OffscreenCanvasRenderingContext2DBinding::Wrap, + ) + } +} + +impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContext2D { + // https://html.spec.whatwg.org/multipage/offscreencontext2d-canvas + fn Canvas(&self) -> DomRoot<OffscreenCanvas> { + DomRoot::from_ref(self.canvas.as_ref().expect("No canvas.")) + } +} diff --git a/components/script/dom/timeranges.rs b/components/script/dom/timeranges.rs index c82daac23a7..7b4d1978a60 100644 --- a/components/script/dom/timeranges.rs +++ b/components/script/dom/timeranges.rs @@ -2,7 +2,6 @@ * 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 crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::TimeRangesBinding; use crate::dom::bindings::codegen::Bindings::TimeRangesBinding::TimeRangesMethods; use crate::dom::bindings::error::{Error, Fallible}; @@ -12,9 +11,8 @@ use crate::dom::bindings::root::DomRoot; use crate::dom::window::Window; use dom_struct::dom_struct; use std::fmt; -use std::rc::Rc; -#[derive(JSTraceable, MallocSizeOf)] +#[derive(Clone, JSTraceable, MallocSizeOf)] struct TimeRange { start: f64, end: f64, @@ -57,7 +55,7 @@ pub enum TimeRangesError { OutOfRange, } -#[derive(Debug, JSTraceable, MallocSizeOf)] +#[derive(Clone, Debug, JSTraceable, MallocSizeOf)] pub struct TimeRangesContainer { ranges: Vec<TimeRange>, } @@ -128,25 +126,18 @@ impl TimeRangesContainer { #[dom_struct] pub struct TimeRanges { reflector_: Reflector, - #[ignore_malloc_size_of = "Rc"] - ranges: Rc<DomRefCell<TimeRangesContainer>>, + ranges: TimeRangesContainer, } -//XXX(ferjm) We'll get warnings about unused methods until we use this -// on the media element implementation. -#[allow(dead_code)] impl TimeRanges { - fn new_inherited(ranges: Rc<DomRefCell<TimeRangesContainer>>) -> TimeRanges { + fn new_inherited(ranges: TimeRangesContainer) -> TimeRanges { Self { reflector_: Reflector::new(), ranges, } } - pub fn new( - window: &Window, - ranges: Rc<DomRefCell<TimeRangesContainer>>, - ) -> DomRoot<TimeRanges> { + pub fn new(window: &Window, ranges: TimeRangesContainer) -> DomRoot<TimeRanges> { reflect_dom_object( Box::new(TimeRanges::new_inherited(ranges)), window, @@ -158,13 +149,12 @@ impl TimeRanges { impl TimeRangesMethods for TimeRanges { // https://html.spec.whatwg.org/multipage/#dom-timeranges-length fn Length(&self) -> u32 { - self.ranges.borrow().len() + self.ranges.len() } // https://html.spec.whatwg.org/multipage/#dom-timeranges-start fn Start(&self, index: u32) -> Fallible<Finite<f64>> { self.ranges - .borrow() .start(index) .map(Finite::wrap) .map_err(|_| Error::IndexSize) @@ -173,7 +163,6 @@ impl TimeRangesMethods for TimeRanges { // https://html.spec.whatwg.org/multipage/#dom-timeranges-end fn End(&self, index: u32) -> Fallible<Finite<f64>> { self.ranges - .borrow() .end(index) .map(Finite::wrap) .map_err(|_| Error::IndexSize) diff --git a/components/script/dom/userscripts.rs b/components/script/dom/userscripts.rs index 6ae80ee8024..1ba79ac5b69 100644 --- a/components/script/dom/userscripts.rs +++ b/components/script/dom/userscripts.rs @@ -3,9 +3,10 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::refcounted::Trusted; use crate::dom::globalscope::GlobalScope; use crate::dom::htmlheadelement::HTMLHeadElement; -use crate::dom::node::Node; +use crate::dom::node::document_from_node; use js::jsval::UndefinedValue; use servo_config::opts; use std::fs::{read_dir, File}; @@ -13,14 +14,18 @@ use std::io::Read; use std::path::PathBuf; pub fn load_script(head: &HTMLHeadElement) { - if let Some(ref path_str) = opts::get().userscripts { - let node = head.upcast::<Node>(); - let doc = node.owner_doc(); - let win = doc.window(); + let path_str = match opts::get().userscripts.clone() { + Some(p) => p, + None => return, + }; + let doc = document_from_node(head); + let win = Trusted::new(doc.window()); + doc.add_delayed_task(task!(UserScriptExecute: move || { + let win = win.root(); let cx = win.get_cx(); rooted!(in(cx) let mut rval = UndefinedValue()); - let path = PathBuf::from(path_str); + let path = PathBuf::from(&path_str); let mut files = read_dir(&path) .expect("Bad path passed to --userscripts") .filter_map(|e| e.ok()) @@ -35,7 +40,12 @@ pub fn load_script(head: &HTMLHeadElement) { f.read_to_end(&mut contents).unwrap(); let script_text = String::from_utf8_lossy(&contents); win.upcast::<GlobalScope>() - .evaluate_js_on_global_with_result(&script_text, rval.handle_mut()); + .evaluate_script_on_global_with_result( + &script_text, + &file.to_string_lossy(), + rval.handle_mut(), + 1, + ); } - } + })); } diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs index 559cf63b8ea..7a41642a201 100644 --- a/components/script/dom/virtualmethods.rs +++ b/components/script/dom/virtualmethods.rs @@ -6,6 +6,7 @@ use crate::dom::attr::Attr; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::ElementTypeId; use crate::dom::bindings::inheritance::HTMLElementTypeId; +use crate::dom::bindings::inheritance::HTMLMediaElementTypeId; use crate::dom::bindings::inheritance::NodeTypeId; use crate::dom::bindings::inheritance::SVGElementTypeId; use crate::dom::bindings::inheritance::SVGGraphicsElementTypeId; @@ -49,6 +50,7 @@ use crate::dom::htmltablesectionelement::HTMLTableSectionElement; use crate::dom::htmltemplateelement::HTMLTemplateElement; use crate::dom::htmltextareaelement::HTMLTextAreaElement; use crate::dom::htmltitleelement::HTMLTitleElement; +use crate::dom::htmlvideoelement::HTMLVideoElement; use crate::dom::node::{ChildrenMutation, CloneChildrenFlag, Node, UnbindContext}; use crate::dom::svgsvgelement::SVGSVGElement; use html5ever::LocalName; @@ -208,8 +210,13 @@ pub fn vtable_for(node: &Node) -> &dyn VirtualMethods { NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) => { node.downcast::<HTMLLinkElement>().unwrap() as &dyn VirtualMethods }, - NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLMediaElement(_))) => { - node.downcast::<HTMLMediaElement>().unwrap() as &dyn VirtualMethods + NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLMediaElement( + media_el, + ))) => match media_el { + HTMLMediaElementTypeId::HTMLVideoElement => { + node.downcast::<HTMLVideoElement>().unwrap() as &dyn VirtualMethods + }, + _ => node.downcast::<HTMLMediaElement>().unwrap() as &dyn VirtualMethods, }, NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLMetaElement)) => { node.downcast::<HTMLMetaElement>().unwrap() as &dyn VirtualMethods diff --git a/components/script/dom/webidls/HTMLMediaElement.webidl b/components/script/dom/webidls/HTMLMediaElement.webidl index d083b80ffff..0acd2a2c9a6 100644 --- a/components/script/dom/webidls/HTMLMediaElement.webidl +++ b/components/script/dom/webidls/HTMLMediaElement.webidl @@ -23,7 +23,7 @@ interface HTMLMediaElement : HTMLElement { const unsigned short NETWORK_NO_SOURCE = 3; readonly attribute unsigned short networkState; [CEReactions] attribute DOMString preload; - // readonly attribute TimeRanges buffered; + readonly attribute TimeRanges buffered; void load(); CanPlayTypeResult canPlayType(DOMString type); diff --git a/components/script/dom/webidls/HTMLVideoElement.webidl b/components/script/dom/webidls/HTMLVideoElement.webidl index 00ebe8dcfee..c79aefafc99 100644 --- a/components/script/dom/webidls/HTMLVideoElement.webidl +++ b/components/script/dom/webidls/HTMLVideoElement.webidl @@ -11,6 +11,10 @@ interface HTMLVideoElement : HTMLMediaElement { // attribute unsigned long height; readonly attribute unsigned long videoWidth; readonly attribute unsigned long videoHeight; - // [CEReactions] - // attribute DOMString poster; + [CEReactions] attribute DOMString poster; +}; + +partial interface HTMLVideoElement { + [Pref="media.testing.enabled"] + attribute EventHandler onpostershown; }; diff --git a/components/script/dom/webidls/OffscreenCanvas.webidl b/components/script/dom/webidls/OffscreenCanvas.webidl new file mode 100644 index 00000000000..032caa10a0f --- /dev/null +++ b/components/script/dom/webidls/OffscreenCanvas.webidl @@ -0,0 +1,25 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// https://html.spec.whatwg.org/multipage/#the-offscreencanvas-interface +typedef (OffscreenCanvasRenderingContext2D or WebGLRenderingContext or WebGL2RenderingContext) +OffscreenRenderingContext; + +dictionary ImageEncodeOptions { + DOMString type = "image/png"; + unrestricted double quality = 1.0; +}; + +//enum OffscreenRenderingContextId { "2d", "webgl", "webgl2" }; + +[Constructor([EnforceRange] unsigned long long width, [EnforceRange] unsigned long long height), +Exposed=(Window,Worker)/*, Transferable*/, Pref="dom.offscreen_canvas.enabled"] +interface OffscreenCanvas : EventTarget { + attribute /*[EnforceRange]*/ unsigned long long width; + attribute /*[EnforceRange]*/ unsigned long long height; + + OffscreenRenderingContext? getContext(DOMString contextId, optional any options = null); + //ImageBitmap transferToImageBitmap(); + //Promise<Blob> convertToBlob(optional ImageEncodeOptions options); +}; diff --git a/components/script/dom/webidls/OffscreenCanvasRenderingContext2D.webidl b/components/script/dom/webidls/OffscreenCanvasRenderingContext2D.webidl new file mode 100644 index 00000000000..ce113586970 --- /dev/null +++ b/components/script/dom/webidls/OffscreenCanvasRenderingContext2D.webidl @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// https://html.spec.whatwg.org/multipage/#the-offscreen-2d-rendering-context +[Exposed=(Window,Worker), Pref="dom.offscreen_canvas.enabled"] +interface OffscreenCanvasRenderingContext2D { + //void commit(); + readonly attribute OffscreenCanvas canvas; +}; + +//OffscreenCanvasRenderingContext2D includes CanvasState; +//OffscreenCanvasRenderingContext2D includes CanvasTransform; +//OffscreenCanvasRenderingContext2D includes CanvasCompositing; +//OffscreenCanvasRenderingContext2D includes CanvasImageSmoothing; +//OffscreenCanvasRenderingContext2D includes CanvasFillStrokeStyles; +//OffscreenCanvasRenderingContext2D includes CanvasShadowStyles; +//OffscreenCanvasRenderingContext2D includes CanvasFilters; +//OffscreenCanvasRenderingContext2D includes CanvasRect; +//OffscreenCanvasRenderingContext2D includes CanvasDrawPath; +//OffscreenCanvasRenderingContext2D includes CanvasText; +//OffscreenCanvasRenderingContext2D includes CanvasDrawImage; +//OffscreenCanvasRenderingContext2D includes CanvasImageData; +//OffscreenCanvasRenderingContext2D includes CanvasPathDrawingStyles; +//OffscreenCanvasRenderingContext2D includes CanvasTextDrawingStyles; +//OffscreenCanvasRenderingContext2D includes CanvasPath; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 3f702cbeabd..dce37b4f0bc 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -69,7 +69,7 @@ use base64; use bluetooth_traits::BluetoothRequest; use canvas_traits::webgl::WebGLChan; use crossbeam_channel::{unbounded, Sender, TryRecvError}; -use cssparser::{Parser, ParserInput}; +use cssparser::{Parser, ParserInput, SourceLocation}; use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType}; use dom_struct::dom_struct; use embedder_traits::EmbedderMsg; @@ -94,7 +94,6 @@ use profile_traits::ipc as ProfiledIpc; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; use script_layout_interface::message::{Msg, QueryMsg, Reflow, ReflowGoal, ScriptReflow}; -use script_layout_interface::reporter::CSSErrorReporter; use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC}; use script_layout_interface::rpc::{ NodeScrollIdResponse, ResolvedStyleResponse, TextIndexResponse, @@ -103,7 +102,7 @@ use script_layout_interface::{PendingImageState, TrustedNodeAddress}; use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; use script_traits::{ConstellationControlMsg, DocumentState, LoadData}; use script_traits::{ScriptMsg, ScriptToConstellationChan, ScrollState, TimerEvent, TimerEventId}; -use script_traits::{TimerSchedulerMsg, UntrustedNodeAddress, WindowSizeData, WindowSizeType}; +use script_traits::{TimerSchedulerMsg, WindowSizeData, WindowSizeType}; use selectors::attr::CaseSensitivity; use servo_config::opts; use servo_geometry::{f32_rect_to_au_rect, MaxRect}; @@ -120,7 +119,8 @@ use std::mem; use std::rc::Rc; use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; -use style::error_reporting::ParseErrorReporter; +use style::dom::OpaqueNode; +use style::error_reporting::{ContextualParseError, ParseErrorReporter}; use style::media_queries; use style::parser::ParserContext as CssParserContext; use style::properties::{ComputedValues, PropertyId}; @@ -248,7 +248,7 @@ pub struct Window { error_reporter: CSSErrorReporter, /// A list of scroll offsets for each scrollable element. - scroll_offsets: DomRefCell<HashMap<UntrustedNodeAddress, Vector2D<f32>>>, + scroll_offsets: DomRefCell<HashMap<OpaqueNode, Vector2D<f32>>>, /// All the MediaQueryLists we need to update media_query_lists: DOMTracker<MediaQueryList>, @@ -390,7 +390,7 @@ impl Window { /// Sets a new list of scroll offsets. /// /// This is called when layout gives us new ones and WebRender is in use. - pub fn set_scroll_offsets(&self, offsets: HashMap<UntrustedNodeAddress, Vector2D<f32>>) { + pub fn set_scroll_offsets(&self, offsets: HashMap<OpaqueNode, Vector2D<f32>>) { *self.scroll_offsets.borrow_mut() = offsets } @@ -1602,11 +1602,7 @@ impl Window { } pub fn scroll_offset_query(&self, node: &Node) -> Vector2D<f32> { - if let Some(scroll_offset) = self - .scroll_offsets - .borrow() - .get(&node.to_untrusted_node_address()) - { + if let Some(scroll_offset) = self.scroll_offsets.borrow().get(&node.to_opaque()) { return *scroll_offset; } Vector2D::new(0.0, 0.0) @@ -1621,10 +1617,9 @@ impl Window { // The scroll offsets are immediatly updated since later calls // to topScroll and others may access the properties before // webrender has a chance to update the offsets. - self.scroll_offsets.borrow_mut().insert( - node.to_untrusted_node_address(), - Vector2D::new(x_ as f32, y_ as f32), - ); + self.scroll_offsets + .borrow_mut() + .insert(node.to_opaque(), Vector2D::new(x_ as f32, y_ as f32)); let NodeScrollIdResponse(scroll_id) = self.layout_rpc.node_scroll_id(); @@ -2242,3 +2237,41 @@ impl Window { )); } } + +#[derive(Clone, MallocSizeOf)] +pub struct CSSErrorReporter { + pub pipelineid: PipelineId, + // Arc+Mutex combo is necessary to make this struct Sync, + // which is necessary to fulfill the bounds required by the + // uses of the ParseErrorReporter trait. + #[ignore_malloc_size_of = "Arc is defined in libstd"] + pub script_chan: Arc<Mutex<IpcSender<ConstellationControlMsg>>>, +} +unsafe_no_jsmanaged_fields!(CSSErrorReporter); + +impl ParseErrorReporter for CSSErrorReporter { + fn report_error(&self, url: &ServoUrl, location: SourceLocation, error: ContextualParseError) { + if log_enabled!(log::Level::Info) { + info!( + "Url:\t{}\n{}:{} {}", + url.as_str(), + location.line, + location.column, + error + ) + } + + //TODO: report a real filename + let _ = self + .script_chan + .lock() + .unwrap() + .send(ConstellationControlMsg::ReportCSSError( + self.pipelineid, + url.to_string(), + location.line, + location.column, + error.to_string(), + )); + } +} diff --git a/components/script/image_listener.rs b/components/script/image_listener.rs new file mode 100644 index 00000000000..fffaf3acc03 --- /dev/null +++ b/components/script/image_listener.rs @@ -0,0 +1,53 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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 crate::dom::bindings::conversions::DerivedFrom; +use crate::dom::bindings::refcounted::Trusted; +use crate::dom::bindings::reflector::DomObject; +use crate::dom::node::{window_from_node, Node}; +use crate::task_source::TaskSource; +use ipc_channel::ipc; +use ipc_channel::router::ROUTER; +use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse, PendingImageId}; +use std::sync::Arc; + +pub trait ImageCacheListener { + fn generation_id(&self) -> u32; + fn process_image_response(&self, response: ImageResponse); +} + +pub fn add_cache_listener_for_element<T: ImageCacheListener + DerivedFrom<Node> + DomObject>( + image_cache: Arc<dyn ImageCache>, + id: PendingImageId, + elem: &T, +) { + let trusted_node = Trusted::new(elem); + let (responder_sender, responder_receiver) = ipc::channel().unwrap(); + + let window = window_from_node(elem); + let (task_source, canceller) = window + .task_manager() + .networking_task_source_with_canceller(); + let generation = elem.generation_id(); + ROUTER.add_route( + responder_receiver.to_opaque(), + Box::new(move |message| { + let element = trusted_node.clone(); + let image = message.to().unwrap(); + debug!("Got image {:?}", image); + let _ = task_source.queue_with_canceller( + task!(process_image_response: move || { + let element = element.root(); + // Ignore any image response for a previous request that has been discarded. + if generation == element.generation_id() { + element.process_image_response(image); + } + }), + &canceller, + ); + }), + ); + + image_cache.add_listener(id, ImageResponder::new(responder_sender, id)); +} diff --git a/components/script/lib.rs b/components/script/lib.rs index 2454fff090e..ce427aa3a56 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -56,6 +56,7 @@ pub mod document_loader; #[macro_use] mod dom; pub mod fetch; +mod image_listener; mod layout_image; mod mem; mod microtask; diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 2053b10fa88..4076a56f9ca 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -119,7 +119,7 @@ use net_traits::{ }; use profile_traits::mem::{self as profile_mem, OpaqueSender, ReportsChan}; use profile_traits::time::{self as profile_time, profile, ProfilerCategory}; -use script_layout_interface::message::{self, Msg, NewLayoutThreadInfo, ReflowGoal}; +use script_layout_interface::message::{self, LayoutThreadInit, Msg, ReflowGoal}; use script_traits::webdriver_msg::WebDriverScriptCommand; use script_traits::CompositorEvent::{ CompositionEvent, KeyboardEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent, TouchEvent, @@ -147,6 +147,7 @@ use std::result::Result; use std::sync::Arc; use std::thread; use std::time::{Duration, SystemTime}; +use style::dom::OpaqueNode; use style::thread_state::{self, ThreadState}; use time::{at_utc, get_time, precise_time_ns, Timespec}; use url::percent_encoding::percent_decode; @@ -1922,7 +1923,7 @@ impl ScriptThread { if node_address == UntrustedNodeAddress(ptr::null()) { window.update_viewport_for_scroll(-scroll_offset.x, -scroll_offset.y); } else { - scroll_offsets.insert(node_address, -*scroll_offset); + scroll_offsets.insert(OpaqueNode(node_address.0 as usize), -*scroll_offset); } } window.set_scroll_offsets(scroll_offsets) @@ -1944,7 +1945,7 @@ impl ScriptThread { let layout_pair = unbounded(); let layout_chan = layout_pair.0.clone(); - let msg = message::Msg::CreateLayoutThread(NewLayoutThreadInfo { + let msg = message::Msg::CreateLayoutThread(LayoutThreadInit { id: new_pipeline_id, url: load_data.url.clone(), is_parent: false, |