diff options
Diffstat (limited to 'components')
38 files changed, 532 insertions, 351 deletions
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 270a6b656d0..8041fd7f1df 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -701,9 +701,9 @@ impl<Window: WindowMethods> IOCompositor<Window> { self.composition_request = CompositionRequest::CompositeNow(reason) } - (Msg::KeyEvent(key, state, modified), ShutdownState::NotShuttingDown) => { + (Msg::KeyEvent(ch, key, state, modified), ShutdownState::NotShuttingDown) => { if state == KeyState::Pressed { - self.window.handle_key(key, modified); + self.window.handle_key(ch, key, modified); } } @@ -1348,8 +1348,8 @@ impl<Window: WindowMethods> IOCompositor<Window> { self.on_touchpad_pressure_event(cursor, pressure, stage); } - WindowEvent::KeyEvent(key, state, modifiers) => { - self.on_key_event(key, state, modifiers); + WindowEvent::KeyEvent(ch, key, state, modifiers) => { + self.on_key_event(ch, key, state, modifiers); } WindowEvent::Quit => { @@ -1880,8 +1880,8 @@ impl<Window: WindowMethods> IOCompositor<Window> { } } - fn on_key_event(&self, key: Key, state: KeyState, modifiers: KeyModifiers) { - let msg = ConstellationMsg::KeyEvent(key, state, modifiers); + fn on_key_event(&self, ch: Option<char>, key: Key, state: KeyState, modifiers: KeyModifiers) { + let msg = ConstellationMsg::KeyEvent(ch, key, state, modifiers); if let Err(e) = self.constellation_chan.send(msg) { warn!("Sending key event to constellation failed ({}).", e); } diff --git a/components/compositing/compositor_thread.rs b/components/compositing/compositor_thread.rs index 24b9caba0a7..49d0692e6c8 100644 --- a/components/compositing/compositor_thread.rs +++ b/components/compositing/compositor_thread.rs @@ -151,7 +151,7 @@ pub enum Msg { /// Composite. Recomposite(CompositingReason), /// Sends an unconsumed key event back to the compositor. - KeyEvent(Key, KeyState, KeyModifiers), + KeyEvent(Option<char>, Key, KeyState, KeyModifiers), /// Script has handled a touch event, and either prevented or allowed default actions. TouchEventProcessed(EventResult), /// Changes the cursor. diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index b67803b07c6..2da658a9661 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -76,7 +76,7 @@ pub enum WindowEvent { /// Sent when the user quits the application Quit, /// Sent when a key input state changes - KeyEvent(Key, KeyState, KeyModifiers), + KeyEvent(Option<char>, Key, KeyState, KeyModifiers), /// Sent when Ctr+R/Apple+R is called to reload the current page. Reload, } @@ -159,7 +159,7 @@ pub trait WindowMethods { fn set_cursor(&self, cursor: Cursor); /// Process a key event. - fn handle_key(&self, key: Key, mods: KeyModifiers); + fn handle_key(&self, ch: Option<char>, key: Key, mods: KeyModifiers); /// Does this window support a clipboard fn supports_clipboard(&self) -> bool; diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 7c3a95dbc54..0920a4a523f 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -574,9 +574,9 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> debug!("constellation got get-pipeline-title message"); self.handle_get_pipeline_title_msg(pipeline_id); } - FromCompositorMsg::KeyEvent(key, state, modifiers) => { + FromCompositorMsg::KeyEvent(ch, key, state, modifiers) => { debug!("constellation got key event message"); - self.handle_key_msg(key, state, modifiers); + self.handle_key_msg(ch, key, state, modifiers); } // Load a new page from a typed url // If there is already a pending page (self.pending_frames), it will not be overridden; @@ -803,8 +803,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> self.compositor_proxy.send(ToCompositorMsg::ChangePageTitle(pipeline_id, title)) } - FromScriptMsg::SendKeyEvent(key, key_state, key_modifiers) => { - self.compositor_proxy.send(ToCompositorMsg::KeyEvent(key, key_state, key_modifiers)) + FromScriptMsg::SendKeyEvent(ch, key, key_state, key_modifiers) => { + self.compositor_proxy.send(ToCompositorMsg::KeyEvent(ch, key, key_state, key_modifiers)) } FromScriptMsg::TouchEventProcessed(result) => { @@ -1396,7 +1396,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } } - fn handle_key_msg(&mut self, key: Key, state: KeyState, mods: KeyModifiers) { + fn handle_key_msg(&mut self, ch: Option<char>, key: Key, state: KeyState, mods: KeyModifiers) { // Send to the explicitly focused pipeline (if it exists), or the root // frame's current pipeline. If neither exist, fall back to sending to // the compositor below. @@ -1407,7 +1407,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> match pipeline_id { Some(pipeline_id) => { - let event = CompositorEvent::KeyEvent(key, state, mods); + let event = CompositorEvent::KeyEvent(ch, key, state, mods); let msg = ConstellationControlMsg::SendEvent(pipeline_id, event); let result = match self.pipelines.get(&pipeline_id) { Some(pipeline) => pipeline.script_chan.send(msg), @@ -1418,7 +1418,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } }, None => { - let event = ToCompositorMsg::KeyEvent(key, state, mods); + let event = ToCompositorMsg::KeyEvent(ch, key, state, mods); self.compositor_proxy.clone_compositor_proxy().send(event); } } @@ -1629,7 +1629,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> None => return warn!("Pipeline {:?} SendKeys after closure.", pipeline_id), }; for (key, mods, state) in cmd { - let event = CompositorEvent::KeyEvent(key, state, mods); + let event = CompositorEvent::KeyEvent(None, key, state, mods); let control_msg = ConstellationControlMsg::SendEvent(pipeline_id, event); if let Err(e) = script_channel.send(control_msg) { return self.handle_send_error(pipeline_id, e); diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml index 188b39e9207..05f9fcda82c 100644 --- a/components/layout/Cargo.toml +++ b/components/layout/Cargo.toml @@ -31,7 +31,7 @@ range = {path = "../range"} rustc-serialize = "0.3" script_layout_interface = {path = "../script_layout_interface"} script_traits = {path = "../script_traits"} -selectors = {version = "0.6", features = ["heap_size"]} +selectors = {version = "0.7", features = ["heap_size"]} serde_macros = "0.7.11" smallvec = "0.1" string_cache = {version = "0.2.20", features = ["heap_size"]} diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 173a19196a5..4bc969b2e74 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -43,6 +43,7 @@ use std::default::Default; use std::sync::Arc; use std::{cmp, f32}; use style::computed_values::filter::Filter; +use style::computed_values::text_shadow::TextShadow; use style::computed_values::{_servo_overflow_clip_box as overflow_clip_box}; use style::computed_values::{background_attachment, background_clip, background_origin}; use style::computed_values::{background_repeat, background_size, border_style}; @@ -258,14 +259,12 @@ pub trait FragmentDisplayListBuilding { /// Creates the text display item for one text fragment. This can be called multiple times for /// one fragment if there are text shadows. /// - /// `shadow_blur_radius` will be `Some` if this is a shadow, even if the blur radius is zero. + /// `text_shadow` will be `Some` if this is rendering a shadow. fn build_display_list_for_text_fragment(&self, state: &mut DisplayListBuildState, text_fragment: &ScannedTextFragmentInfo, - text_color: RGBA, stacking_relative_content_box: &Rect<Au>, - shadow_blur_radius: Option<Au>, - offset: &Point2D<Au>, + text_shadow: Option<&TextShadow>, clip: &ClippingRegion); /// Creates the display item for a text decoration: underline, overline, or line-through. @@ -1150,32 +1149,19 @@ impl FragmentDisplayListBuilding for Fragment { // NB: According to CSS-BACKGROUNDS, text shadows render in *reverse* order (front // to back). - // TODO(emilio): Allow changing more properties by ::selection - let text_color = if text_fragment.selected() { - self.selected_style().get_color().color - } else { - self.style().get_color().color - }; - for text_shadow in self.style.get_inheritedtext().text_shadow.0.iter().rev() { - let offset = &Point2D::new(text_shadow.offset_x, text_shadow.offset_y); - let color = self.style().resolve_color(text_shadow.color); self.build_display_list_for_text_fragment(state, &**text_fragment, - color, &stacking_relative_content_box, - Some(text_shadow.blur_radius), - offset, + Some(text_shadow), clip); } // Create the main text display item. self.build_display_list_for_text_fragment(state, &**text_fragment, - text_color, &stacking_relative_content_box, None, - &Point2D::new(Au(0), Au(0)), clip); if opts::get().show_debug_fragment_borders { @@ -1539,11 +1525,22 @@ impl FragmentDisplayListBuilding for Fragment { fn build_display_list_for_text_fragment(&self, state: &mut DisplayListBuildState, text_fragment: &ScannedTextFragmentInfo, - text_color: RGBA, stacking_relative_content_box: &Rect<Au>, - shadow_blur_radius: Option<Au>, - offset: &Point2D<Au>, + text_shadow: Option<&TextShadow>, clip: &ClippingRegion) { + // TODO(emilio): Allow changing more properties by ::selection + let text_color = if let Some(shadow) = text_shadow { + // If we're painting a shadow, paint the text the same color as the shadow. + self.style().resolve_color(shadow.color) + } else if text_fragment.selected() { + // Otherwise, paint the text with the color as described in its styling. + self.selected_style().get_color().color + } else { + self.style().get_color().color + }; + let offset = text_shadow.map(|s| Point2D::new(s.offset_x, s.offset_y)).unwrap_or_else(Point2D::zero); + let shadow_blur_radius = text_shadow.map(|s| s.blur_radius).unwrap_or(Au(0)); + // Determine the orientation and cursor to use. let (orientation, cursor) = if self.style.writing_mode.is_vertical() { if self.style.writing_mode.is_sideways_left() { @@ -1560,7 +1557,7 @@ impl FragmentDisplayListBuilding for Fragment { // FIXME(pcwalton): Get the real container size. let container_size = Size2D::zero(); let metrics = &text_fragment.run.font_metrics; - let stacking_relative_content_box = stacking_relative_content_box.translate(offset); + let stacking_relative_content_box = stacking_relative_content_box.translate(&offset); let baseline_origin = stacking_relative_content_box.origin + LogicalPoint::new(self.style.writing_mode, Au(0), @@ -1580,19 +1577,17 @@ impl FragmentDisplayListBuilding for Fragment { text_color: text_color.to_gfx_color(), orientation: orientation, baseline_origin: baseline_origin, - blur_radius: shadow_blur_radius.unwrap_or(Au(0)), + blur_radius: shadow_blur_radius, })); // Create display items for text decorations. let mut text_decorations = self.style() .get_inheritedtext() ._servo_text_decorations_in_effect; - if shadow_blur_radius.is_some() { - // If we're painting a shadow, paint the decorations the same color as the shadow. - text_decorations.underline = text_decorations.underline.map(|_| text_color); - text_decorations.overline = text_decorations.overline.map(|_| text_color); - text_decorations.line_through = text_decorations.line_through.map(|_| text_color); - } + // Note that the text decoration colors are always the same as the text color. + text_decorations.underline = text_decorations.underline.map(|_| text_color); + text_decorations.overline = text_decorations.overline.map(|_| text_color); + text_decorations.line_through = text_decorations.line_through.map(|_| text_color); let stacking_relative_content_box = LogicalRect::from_physical(self.style.writing_mode, @@ -1607,7 +1602,7 @@ impl FragmentDisplayListBuilding for Fragment { underline_color, &stacking_relative_box, clip, - shadow_blur_radius.unwrap_or(Au(0))); + shadow_blur_radius); } if let Some(ref overline_color) = text_decorations.overline { @@ -1617,7 +1612,7 @@ impl FragmentDisplayListBuilding for Fragment { overline_color, &stacking_relative_box, clip, - shadow_blur_radius.unwrap_or(Au(0))); + shadow_blur_radius); } if let Some(ref line_through_color) = text_decorations.line_through { @@ -1629,7 +1624,7 @@ impl FragmentDisplayListBuilding for Fragment { line_through_color, &stacking_relative_box, clip, - shadow_blur_radius.unwrap_or(Au(0))); + shadow_blur_radius); } } diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index 5db766f68b5..581fee5e03d 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -28,7 +28,7 @@ pub struct RecalcStyleAndConstructFlows<'lc> { impl<'lc, N> DomTraversalContext<N> for RecalcStyleAndConstructFlows<'lc> where N: LayoutNode + TNode<ConcreteComputedValues=ServoComputedValues>, - N::ConcreteElement: ::selectors::Element<Impl=ServoSelectorImpl> + N::ConcreteElement: ::selectors::Element<Impl=ServoSelectorImpl, AttrString=String> { type SharedContext = SharedLayoutContext; diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index d9bdeac843b..fa2d75c633d 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -57,7 +57,7 @@ regex = "0.1.43" rustc-serialize = "0.3" script_layout_interface = {path = "../script_layout_interface"} script_traits = {path = "../script_traits"} -selectors = {version = "0.6", features = ["heap_size"]} +selectors = {version = "0.7", features = ["heap_size"]} serde = "0.7.11" smallvec = "0.1" string_cache = {version = "0.2.20", features = ["heap_size", "unstable"]} diff --git a/components/script/dom/bindings/str.rs b/components/script/dom/bindings/str.rs index 69565520899..310285a8bcd 100644 --- a/components/script/dom/bindings/str.rs +++ b/components/script/dom/bindings/str.rs @@ -5,7 +5,7 @@ //! The `ByteString` struct. use std::ascii::AsciiExt; -use std::borrow::ToOwned; +use std::borrow::{ToOwned, Cow}; use std::fmt; use std::hash::{Hash, Hasher}; use std::ops; @@ -204,6 +204,15 @@ impl<'a> From<&'a str> for DOMString { } } +impl<'a> From<Cow<'a, str>> for DOMString { + fn from(contents: Cow<'a, str>) -> DOMString { + match contents { + Cow::Owned(s) => DOMString::from(s), + Cow::Borrowed(s) => DOMString::from(s), + } + } +} + impl From<DOMString> for Atom { fn from(contents: DOMString) -> Atom { Atom::from(contents.0) diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 7404e96b95d..e2c6e540a3d 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -276,7 +276,7 @@ impl<A: JSTraceable, B: JSTraceable, C: JSTraceable> JSTraceable for (A, B, C) { } } -no_jsmanaged_fields!(bool, f32, f64, String, Url, AtomicBool, AtomicUsize, UrlOrigin, Uuid); +no_jsmanaged_fields!(bool, f32, f64, String, Url, AtomicBool, AtomicUsize, UrlOrigin, Uuid, char); no_jsmanaged_fields!(usize, u8, u16, u32, u64); no_jsmanaged_fields!(isize, i8, i16, i32, i64); no_jsmanaged_fields!(Sender<T>); diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 2160a3a4556..43a4473689e 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -1044,6 +1044,7 @@ impl Document { /// The entry point for all key processing for web content pub fn dispatch_key_event(&self, + ch: Option<char>, key: Key, state: KeyState, modifiers: KeyModifiers, @@ -1070,7 +1071,7 @@ impl Document { } .to_owned()); - let props = KeyboardEvent::key_properties(key, modifiers); + let props = KeyboardEvent::key_properties(ch, key, modifiers); let keyevent = KeyboardEvent::new(&self.window, ev_type, @@ -1078,8 +1079,9 @@ impl Document { true, Some(&self.window), 0, + ch, Some(key), - DOMString::from(props.key_string), + DOMString::from(props.key_string.clone()), DOMString::from(props.code), props.location, is_repeating, @@ -1103,6 +1105,7 @@ impl Document { true, Some(&self.window), 0, + ch, Some(key), DOMString::from(props.key_string), DOMString::from(props.code), @@ -1122,7 +1125,7 @@ impl Document { } if !prevented { - constellation.send(ConstellationMsg::SendKeyEvent(key, state, modifiers)).unwrap(); + constellation.send(ConstellationMsg::SendKeyEvent(ch, key, state, modifiers)).unwrap(); } // This behavior is unspecced diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index d9535663d46..e53f7dbb77b 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -2217,6 +2217,34 @@ impl VirtualMethods for Element { } } +impl<'a> ::selectors::MatchAttrGeneric for Root<Element> { + fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool + where F: Fn(&str) -> bool + { + use ::selectors::Element; + let local_name = { + if self.is_html_element_in_html_document() { + &attr.lower_name + } else { + &attr.name + } + }; + match attr.namespace { + NamespaceConstraint::Specific(ref ns) => { + self.get_attribute(ns, local_name) + .map_or(false, |attr| { + test(&attr.value()) + }) + }, + NamespaceConstraint::Any => { + self.attrs.borrow().iter().any(|attr| { + attr.local_name() == local_name && test(&attr.value()) + }) + } + } + } +} + impl<'a> ::selectors::Element for Root<Element> { type Impl = ServoSelectorImpl; @@ -2317,31 +2345,6 @@ impl<'a> ::selectors::Element for Root<Element> { } } - fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool - where F: Fn(&str) -> bool - { - let local_name = { - if self.is_html_element_in_html_document() { - &attr.lower_name - } else { - &attr.name - } - }; - match attr.namespace { - NamespaceConstraint::Specific(ref ns) => { - self.get_attribute(ns, local_name) - .map_or(false, |attr| { - test(&attr.value()) - }) - }, - NamespaceConstraint::Any => { - self.attrs.borrow().iter().any(|attr| { - attr.local_name() == local_name && test(&attr.value()) - }) - } - } - } - fn is_html_element_in_html_document(&self) -> bool { self.html_element_in_html_document() } diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 06a5bf93abf..2b61a800a1a 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -31,6 +31,8 @@ use script_thread::Runnable; use std::sync::Arc; use string_cache::Atom; use style::attr::{AttrValue, LengthOrPercentageOrAuto}; +use task_source::TaskSource; +use task_source::dom_manipulation::DOMManipulationTask; use url::Url; #[derive(JSTraceable, HeapSizeOf)] @@ -44,7 +46,8 @@ enum State { #[derive(JSTraceable, HeapSizeOf)] struct ImageRequest { state: State, - url: Option<Url>, + parsed_url: Option<Url>, + source_url: Option<DOMString>, image: Option<Arc<Image>>, metadata: Option<ImageMetadata>, } @@ -56,8 +59,8 @@ pub struct HTMLImageElement { } impl HTMLImageElement { - pub fn get_url(&self) -> Option<Url>{ - self.current_request.borrow().url.clone() + pub fn get_url(&self) -> Option<Url> { + self.current_request.borrow().parsed_url.clone() } } @@ -118,33 +121,70 @@ impl HTMLImageElement { let image_cache = window.image_cache_thread(); match value { None => { - self.current_request.borrow_mut().url = None; + self.current_request.borrow_mut().parsed_url = None; + self.current_request.borrow_mut().source_url = None; self.current_request.borrow_mut().image = None; } Some((src, base_url)) => { let img_url = base_url.join(&src); - // FIXME: handle URL parse errors more gracefully. - let img_url = img_url.unwrap(); - self.current_request.borrow_mut().url = Some(img_url.clone()); - - let trusted_node = Trusted::new(self); - let (responder_sender, responder_receiver) = ipc::channel().unwrap(); - let script_chan = window.networking_task_source(); - let wrapper = window.get_runnable_wrapper(); - ROUTER.add_route(responder_receiver.to_opaque(), box move |message| { - // Return the image via a message to the script thread, which marks the element - // as dirty and triggers a reflow. - let image_response = message.to().unwrap(); - let runnable = ImageResponseHandlerRunnable::new( - trusted_node.clone(), image_response); - let runnable = wrapper.wrap_runnable(runnable); - let _ = script_chan.send(CommonScriptMsg::RunnableMsg( - UpdateReplacedElement, runnable)); - }); - - image_cache.request_image_and_metadata(img_url, - window.image_cache_chan(), - Some(ImageResponder::new(responder_sender))); + if let Ok(img_url) = img_url { + self.current_request.borrow_mut().parsed_url = Some(img_url.clone()); + self.current_request.borrow_mut().source_url = Some(src); + + let trusted_node = Trusted::new(self); + let (responder_sender, responder_receiver) = ipc::channel().unwrap(); + let script_chan = window.networking_task_source(); + let wrapper = window.get_runnable_wrapper(); + ROUTER.add_route(responder_receiver.to_opaque(), box move |message| { + // Return the image via a message to the script thread, which marks the element + // as dirty and triggers a reflow. + let image_response = message.to().unwrap(); + let runnable = ImageResponseHandlerRunnable::new( + trusted_node.clone(), image_response); + let runnable = wrapper.wrap_runnable(runnable); + let _ = script_chan.send(CommonScriptMsg::RunnableMsg( + UpdateReplacedElement, runnable)); + }); + + image_cache.request_image_and_metadata(img_url, + window.image_cache_chan(), + Some(ImageResponder::new(responder_sender))); + } else { + // https://html.spec.whatwg.org/multipage/#update-the-image-data + // Step 11 (error substeps) + debug!("Failed to parse URL {} with base {}", src, base_url); + let mut req = self.current_request.borrow_mut(); + + // Substeps 1,2 + req.image = None; + req.parsed_url = None; + req.state = State::Broken; + // todo: set pending request to null + // (pending requests aren't being used yet) + + + struct ImgParseErrorRunnable { + img: Trusted<HTMLImageElement>, + src: String, + } + impl Runnable for ImgParseErrorRunnable { + fn handler(self: Box<Self>) { + // https://html.spec.whatwg.org/multipage/#update-the-image-data + // Step 11, substep 5 + let img = self.img.root(); + img.current_request.borrow_mut().source_url = Some(self.src.into()); + img.upcast::<EventTarget>().fire_simple_event("error"); + img.upcast::<EventTarget>().fire_simple_event("loadend"); + } + } + + let runnable = Box::new(ImgParseErrorRunnable { + img: Trusted::new(self), + src: src.into(), + }); + let task = window.dom_manipulation_task_source(); + let _ = task.queue(DOMManipulationTask::Miscellaneous(runnable)); + } } } } @@ -153,13 +193,15 @@ impl HTMLImageElement { htmlelement: HTMLElement::new_inherited(localName, prefix, document), current_request: DOMRefCell::new(ImageRequest { state: State::Unavailable, - url: None, + parsed_url: None, + source_url: None, image: None, metadata: None }), pending_request: DOMRefCell::new(ImageRequest { state: State::Unavailable, - url: None, + parsed_url: None, + source_url: None, image: None, metadata: None }), @@ -209,7 +251,7 @@ impl LayoutHTMLImageElementHelpers for LayoutJS<HTMLImageElement> { #[allow(unsafe_code)] unsafe fn image_url(&self) -> Option<Url> { - (*self.unsafe_get()).current_request.borrow_for_layout().url.clone() + (*self.unsafe_get()).current_request.borrow_for_layout().parsed_url.clone() } #[allow(unsafe_code)] @@ -313,9 +355,9 @@ impl HTMLImageElementMethods for HTMLImageElement { // https://html.spec.whatwg.org/multipage/#dom-img-currentsrc fn CurrentSrc(&self) -> DOMString { - let ref url = self.current_request.borrow().url; + let ref url = self.current_request.borrow().source_url; match *url { - Some(ref url) => DOMString::from(url.as_str()), + Some(ref url) => url.clone(), None => DOMString::from(""), } } diff --git a/components/script/dom/keyboardevent.rs b/components/script/dom/keyboardevent.rs index 4cfa1a61011..71b00767a8e 100644 --- a/components/script/dom/keyboardevent.rs +++ b/components/script/dom/keyboardevent.rs @@ -17,6 +17,7 @@ use dom::uievent::UIEvent; use dom::window::Window; use msg::constellation_msg; use msg::constellation_msg::{Key, KeyModifiers}; +use std::borrow::Cow; use std::cell::Cell; no_jsmanaged_fields!(Key); @@ -36,6 +37,7 @@ pub struct KeyboardEvent { is_composing: Cell<bool>, char_code: Cell<Option<u32>>, key_code: Cell<u32>, + printable: Cell<Option<char>>, } impl KeyboardEvent { @@ -54,6 +56,7 @@ impl KeyboardEvent { is_composing: Cell::new(false), char_code: Cell::new(None), key_code: Cell::new(0), + printable: Cell::new(None), } } @@ -69,6 +72,7 @@ impl KeyboardEvent { cancelable: bool, view: Option<&Window>, _detail: i32, + ch: Option<char>, key: Option<Key>, key_string: DOMString, code: DOMString, @@ -91,6 +95,7 @@ impl KeyboardEvent { ev.shift.set(shiftKey); ev.meta.set(metaKey); ev.char_code.set(char_code); + ev.printable.set(ch); ev.key_code.set(key_code); ev.is_composing.set(isComposing); ev @@ -103,7 +108,9 @@ impl KeyboardEvent { init.parent.parent.parent.bubbles, init.parent.parent.parent.cancelable, init.parent.parent.view.r(), - init.parent.parent.detail, key_from_string(&init.key, init.location), + init.parent.parent.detail, + None, + key_from_string(&init.key, init.location), init.key.clone(), init.code.clone(), init.location, init.repeat, init.isComposing, init.parent.ctrlKey, init.parent.altKey, init.parent.shiftKey, init.parent.metaKey, @@ -111,13 +118,13 @@ impl KeyboardEvent { Ok(event) } - pub fn key_properties(key: Key, mods: KeyModifiers) + pub fn key_properties(ch: Option<char>, key: Key, mods: KeyModifiers) -> KeyEventProperties { KeyEventProperties { - key_string: key_value(key, mods), + key_string: key_value(ch, key, mods), code: code_value(key), location: key_location(key), - char_code: key_charcode(key, mods), + char_code: ch.map(|ch| ch as u32), key_code: key_keycode(key), } } @@ -125,6 +132,10 @@ impl KeyboardEvent { impl KeyboardEvent { + pub fn printable(&self) -> Option<char> { + self.printable.get() + } + pub fn get_key(&self) -> Option<Key> { self.key.get().clone() } @@ -147,11 +158,14 @@ impl KeyboardEvent { } } - // https://w3c.github.io/uievents-key/#key-value-tables -pub fn key_value(key: Key, mods: KeyModifiers) -> &'static str { +pub fn key_value(ch: Option<char>, key: Key, mods: KeyModifiers) -> Cow<'static, str> { + if let Some(ch) = ch { + return Cow::from(format!("{}", ch)); + } + let shift = mods.contains(constellation_msg::SHIFT); - match key { + Cow::from(match key { Key::Space => " ", Key::Apostrophe if shift => "\"", Key::Apostrophe => "'", @@ -321,7 +335,7 @@ pub fn key_value(key: Key, mods: KeyModifiers) -> &'static str { Key::Menu => "ContextMenu", Key::NavigateForward => "BrowserForward", Key::NavigateBackward => "BrowserBack", - } + }) } fn key_from_string(key_string: &str, location: u32) -> Option<Key> { @@ -647,16 +661,6 @@ fn key_location(key: Key) -> u32 { } } -// https://w3c.github.io/uievents/#dom-keyboardevent-charcode -fn key_charcode(key: Key, mods: KeyModifiers) -> Option<u32> { - let key_string = key_value(key, mods); - if key_string.len() == 1 { - Some(key_string.chars().next().unwrap() as u32) - } else { - None - } -} - // https://w3c.github.io/uievents/#legacy-key-models fn key_keycode(key: Key) -> u32 { match key { @@ -739,7 +743,7 @@ fn key_keycode(key: Key) -> u32 { #[derive(HeapSizeOf)] pub struct KeyEventProperties { - pub key_string: &'static str, + pub key_string: Cow<'static, str>, pub code: &'static str, pub location: u32, pub char_code: Option<u32>, diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl index 8d587b9760a..02864d39a77 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -308,6 +308,9 @@ partial interface CSSStyleDeclaration { [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transitionDelay; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transition-delay; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString flex; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString flexFlow; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString flex-flow; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString flexDirection; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString flex-direction; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString flexWrap; diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index 7085fcae002..ebc2da9c529 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -387,17 +387,13 @@ impl<'le> TElement for ServoLayoutElement<'le> { } #[inline] - fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&str> { - unsafe { - (*self.element.unsafe_get()).get_attr_val_for_layout(namespace, name) - } + fn has_attr(&self, namespace: &Namespace, attr: &Atom) -> bool { + self.get_attr(namespace, attr).is_some() } #[inline] - fn get_attrs(&self, name: &Atom) -> Vec<&str> { - unsafe { - (*self.element.unsafe_get()).get_attr_vals_for_layout(name) - } + fn attr_equals(&self, namespace: &Namespace, attr: &Atom, val: &Atom) -> bool { + self.get_attr(namespace, attr).map_or(false, |x| x == val) } } @@ -409,12 +405,41 @@ impl<'le> ServoLayoutElement<'le> { chain: PhantomData, } } + + #[inline] + fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&str> { + unsafe { + (*self.element.unsafe_get()).get_attr_val_for_layout(namespace, name) + } + } } fn as_element<'le>(node: LayoutJS<Node>) -> Option<ServoLayoutElement<'le>> { node.downcast().map(ServoLayoutElement::from_layout_js) } +impl<'le> ::selectors::MatchAttrGeneric for ServoLayoutElement<'le> { + fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool where F: Fn(&str) -> bool { + use ::selectors::Element; + let name = if self.is_html_element_in_html_document() { + &attr.lower_name + } else { + &attr.name + }; + match attr.namespace { + NamespaceConstraint::Specific(ref ns) => { + self.get_attr(ns, name).map_or(false, |attr| test(attr)) + }, + NamespaceConstraint::Any => { + let attrs = unsafe { + (*self.element.unsafe_get()).get_attr_vals_for_layout(name) + }; + attrs.iter().any(|attr| test(*attr)) + } + } + } +} + impl<'le> ::selectors::Element for ServoLayoutElement<'le> { type Impl = ServoSelectorImpl; @@ -550,22 +575,6 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { } } - fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool where F: Fn(&str) -> bool { - let name = if self.is_html_element_in_html_document() { - &attr.lower_name - } else { - &attr.name - }; - match attr.namespace { - NamespaceConstraint::Specific(ref ns) => { - self.get_attr(ns, name).map_or(false, |attr| test(attr)) - }, - NamespaceConstraint::Any => { - self.get_attrs(name).iter().any(|attr| test(*attr)) - } - } - } - fn is_html_element_in_html_document(&self) -> bool { unsafe { self.element.html_element_in_html_document_for_layout() @@ -900,7 +909,23 @@ impl<'le> ThreadSafeLayoutElement for ServoThreadSafeLayoutElement<'le> { /// /// Note that the element implementation is needed only for selector matching, /// not for inheritance (styles are inherited appropiately). -impl <'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { +impl<'le> ::selectors::MatchAttrGeneric for ServoThreadSafeLayoutElement<'le> { + fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool + where F: Fn(&str) -> bool { + match attr.namespace { + NamespaceConstraint::Specific(ref ns) => { + self.get_attr(ns, &attr.name).map_or(false, |attr| test(attr)) + }, + NamespaceConstraint::Any => { + unsafe { + self.element.get_attr_vals_for_layout(&attr.name).iter() + .any(|attr| test(*attr)) + } + } + } + } +} +impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { type Impl = ServoSelectorImpl; fn parent_element(&self) -> Option<Self> { @@ -962,21 +987,6 @@ impl <'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { false } - fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool - where F: Fn(&str) -> bool { - match attr.namespace { - NamespaceConstraint::Specific(ref ns) => { - self.get_attr(ns, &attr.name).map_or(false, |attr| test(attr)) - }, - NamespaceConstraint::Any => { - unsafe { - self.element.get_attr_vals_for_layout(&attr.name).iter() - .any(|attr| test(*attr)) - } - } - } - } - fn is_empty(&self) -> bool { warn!("ServoThreadSafeLayoutElement::is_empty called"); false diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 2367ecc0fd9..05689baac39 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -1937,12 +1937,12 @@ impl ScriptThread { document.r().handle_touchpad_pressure_event(self.js_runtime.rt(), point, pressure, phase); } - KeyEvent(key, state, modifiers) => { + KeyEvent(ch, key, state, modifiers) => { let document = match self.root_browsing_context().find(pipeline_id) { Some(browsing_context) => browsing_context.active_document(), None => return warn!("Message sent to closed pipeline {}.", pipeline_id), }; - document.dispatch_key_event(key, state, modifiers, &self.constellation_chan); + document.dispatch_key_event(ch, key, state, modifiers, &self.constellation_chan); } } } diff --git a/components/script/task_source/dom_manipulation.rs b/components/script/task_source/dom_manipulation.rs index daf212d8c52..fbe864a9005 100644 --- a/components/script/task_source/dom_manipulation.rs +++ b/components/script/task_source/dom_manipulation.rs @@ -52,7 +52,8 @@ pub enum DOMManipulationTask { // https://html.spec.whatwg.org/multipage/#planned-navigation PlannedNavigation(Box<Runnable + Send>), // https://html.spec.whatwg.org/multipage/#send-a-storage-notification - SendStorageNotification(Box<MainThreadRunnable + Send>) + SendStorageNotification(Box<MainThreadRunnable + Send>), + Miscellaneous(Box<Runnable + Send>), } impl DOMManipulationTask { @@ -72,7 +73,8 @@ impl DOMManipulationTask { FireToggleEvent(runnable) => runnable.handler(), MediaTask(runnable) => runnable.handler(), PlannedNavigation(runnable) => runnable.handler(), - SendStorageNotification(runnable) => runnable.handler(script_thread) + SendStorageNotification(runnable) => runnable.handler(script_thread), + Miscellaneous(runnable) => runnable.handler(), } } } diff --git a/components/script/textinput.rs b/components/script/textinput.rs index a56bd983f34..871af9637c0 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -6,7 +6,7 @@ use clipboard_provider::ClipboardProvider; use dom::bindings::str::DOMString; -use dom::keyboardevent::{KeyboardEvent, key_value}; +use dom::keyboardevent::KeyboardEvent; use msg::constellation_msg::{ALT, CONTROL, SHIFT, SUPER}; use msg::constellation_msg::{Key, KeyModifiers}; use std::borrow::ToOwned; @@ -120,24 +120,6 @@ fn is_control_key(mods: KeyModifiers) -> bool { mods.contains(CONTROL) && !mods.contains(SUPER | ALT) } -fn is_printable_key(key: Key) -> bool { - match key { - Key::Space | Key::Apostrophe | Key::Comma | Key::Minus | - Key::Period | Key::Slash | Key::GraveAccent | Key::Num0 | - Key::Num1 | Key::Num2 | Key::Num3 | Key::Num4 | Key::Num5 | - Key::Num6 | Key::Num7 | Key::Num8 | Key::Num9 | Key::Semicolon | - Key::Equal | Key::A | Key::B | Key::C | Key::D | Key::E | Key::F | - Key::G | Key::H | Key::I | Key::J | Key::K | Key::L | Key::M | Key::N | - Key::O | Key::P | Key::Q | Key::R | Key::S | Key::T | Key::U | Key::V | - Key::W | Key::X | Key::Y | Key::Z | Key::LeftBracket | Key::Backslash | - Key::RightBracket | Key::Kp0 | Key::Kp1 | Key::Kp2 | Key::Kp3 | - Key::Kp4 | Key::Kp5 | Key::Kp6 | Key::Kp7 | Key::Kp8 | Key::Kp9 | - Key::KpDecimal | Key::KpDivide | Key::KpMultiply | Key::KpSubtract | - Key::KpAdd | Key::KpEqual => true, - _ => false, - } -} - /// The length in bytes of the first n characters in a UTF-8 string. /// /// If the string has fewer than n characters, returns the length of the whole string. @@ -486,80 +468,80 @@ impl<T: ClipboardProvider> TextInput<T> { /// Process a given `KeyboardEvent` and return an action for the caller to execute. pub fn handle_keydown(&mut self, event: &KeyboardEvent) -> KeyReaction { if let Some(key) = event.get_key() { - self.handle_keydown_aux(key, event.get_key_modifiers()) + self.handle_keydown_aux(event.printable(), key, event.get_key_modifiers()) } else { KeyReaction::Nothing } } - pub fn handle_keydown_aux(&mut self, key: Key, mods: KeyModifiers) -> KeyReaction { + + pub fn handle_keydown_aux(&mut self, + printable: Option<char>, + key: Key, + mods: KeyModifiers) -> KeyReaction { let maybe_select = if mods.contains(SHIFT) { Selection::Selected } else { Selection::NotSelected }; - match key { - Key::A if is_control_key(mods) => { + match (printable, key) { + (Some('a'), _) if is_control_key(mods) => { self.select_all(); KeyReaction::RedrawSelection }, - Key::C if is_control_key(mods) => { + (Some('c'), _) if is_control_key(mods) => { if let Some(text) = self.get_selection_text() { self.clipboard_provider.set_clipboard_contents(text); } KeyReaction::DispatchInput }, - Key::V if is_control_key(mods) => { + (Some('v'), _) if is_control_key(mods) => { let contents = self.clipboard_provider.clipboard_contents(); self.insert_string(contents); KeyReaction::DispatchInput }, - _ if is_printable_key(key) => { - self.insert_string(key_value(key, mods)); - KeyReaction::DispatchInput - } - Key::Space => { - self.insert_char(' '); + (Some(c), _) => { + self.insert_char(c); KeyReaction::DispatchInput } - Key::Delete => { + (None, Key::Delete) => { self.delete_char(Direction::Forward); KeyReaction::DispatchInput } - Key::Backspace => { + (None, Key::Backspace) => { self.delete_char(Direction::Backward); KeyReaction::DispatchInput } - Key::Left => { + (None, Key::Left) => { self.adjust_horizontal_by_one(Direction::Backward, maybe_select); KeyReaction::RedrawSelection } - Key::Right => { + (None, Key::Right) => { self.adjust_horizontal_by_one(Direction::Forward, maybe_select); KeyReaction::RedrawSelection } - Key::Up => { + (None, Key::Up) => { self.adjust_vertical(-1, maybe_select); KeyReaction::RedrawSelection } - Key::Down => { + (None, Key::Down) => { self.adjust_vertical(1, maybe_select); KeyReaction::RedrawSelection } - Key::Enter | Key::KpEnter => self.handle_return(), - Key::Home => { + (None, Key::Enter) | (None, Key::KpEnter) => self.handle_return(), + (None, Key::Home) => { self.edit_point.index = 0; KeyReaction::RedrawSelection } - Key::End => { + (None, Key::End) => { self.edit_point.index = self.current_line_length(); self.assert_ok_selection(); KeyReaction::RedrawSelection } - Key::PageUp => { + (None, Key::PageUp) => { self.adjust_vertical(-28, maybe_select); KeyReaction::RedrawSelection } - Key::PageDown => { + (None, Key::PageDown) => { self.adjust_vertical(28, maybe_select); KeyReaction::RedrawSelection } - Key::Tab => KeyReaction::TriggerDefaultAction, + (None, Key::Tab) => KeyReaction::TriggerDefaultAction, _ => KeyReaction::Nothing, } } diff --git a/components/script_layout_interface/Cargo.toml b/components/script_layout_interface/Cargo.toml index 51b767c753f..41c1f6e2598 100644 --- a/components/script_layout_interface/Cargo.toml +++ b/components/script_layout_interface/Cargo.toml @@ -26,7 +26,7 @@ plugins = {path = "../plugins"} profile_traits = {path = "../profile_traits"} range = {path = "../range"} script_traits = {path = "../script_traits"} -selectors = {version = "0.6", features = ["heap_size"]} +selectors = {version = "0.7", features = ["heap_size"]} string_cache = {version = "0.2.20", features = ["heap_size"]} style = {path = "../style"} url = {version = "1.0.0", features = ["heap_size"]} diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs index a816321ec28..bdbf31c37c8 100644 --- a/components/script_layout_interface/wrapper_traits.rs +++ b/components/script_layout_interface/wrapper_traits.rs @@ -86,7 +86,7 @@ pub trait LayoutNode: TNode { pub trait ThreadSafeLayoutNode: Clone + Copy + Sized + PartialEq { type ConcreteThreadSafeLayoutElement: ThreadSafeLayoutElement<ConcreteThreadSafeLayoutNode = Self> - + ::selectors::Element<Impl=ServoSelectorImpl>; + + ::selectors::Element<Impl=ServoSelectorImpl, AttrString=String>; type ChildrenIterator: Iterator<Item = Self> + Sized; /// Creates a new `ThreadSafeLayoutNode` for the same `LayoutNode` @@ -351,7 +351,7 @@ pub trait DangerousThreadSafeLayoutNode: ThreadSafeLayoutNode { } pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + - ::selectors::Element<Impl=ServoSelectorImpl> + + ::selectors::Element<Impl=ServoSelectorImpl, AttrString=String> + PresentationalHintsSynthetizer { type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<ConcreteThreadSafeLayoutElement = Self>; diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index bd82a498d46..5c6064a3d3e 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -282,7 +282,7 @@ pub enum CompositorEvent { /// Touchpad pressure event TouchpadPressureEvent(Point2D<f32>, f32, TouchpadPressurePhase), /// A key was pressed. - KeyEvent(Key, KeyState, KeyModifiers), + KeyEvent(Option<char>, Key, KeyState, KeyModifiers), } /// Touchpad pressure phase for TouchpadPressureEvent. @@ -586,7 +586,7 @@ pub enum ConstellationMsg { /// Query the constellation to see if the current compositor output is stable IsReadyToSaveImage(HashMap<PipelineId, Epoch>), /// Inform the constellation of a key event. - KeyEvent(Key, KeyState, KeyModifiers), + KeyEvent(Option<char>, Key, KeyState, KeyModifiers), /// Request to load a page. LoadUrl(PipelineId, LoadData), /// Request to navigate a frame. diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index 7fc871eb4cc..0ac5aeae4b4 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -103,7 +103,7 @@ pub enum ScriptMsg { /// https://html.spec.whatwg.org/multipage/#document.title SetTitle(PipelineId, Option<String>), /// Send a key event - SendKeyEvent(Key, KeyState, KeyModifiers), + SendKeyEvent(Option<char>, Key, KeyState, KeyModifiers), /// Get Window Informations size and position GetClientWindow(IpcSender<(Size2D<u32>, Point2D<i32>)>), /// Move the window to a point diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 3424416cead..030f19a5038 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -146,7 +146,7 @@ dependencies = [ [[package]] name = "bincode" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1061,7 +1061,7 @@ name = "ipc-channel" version = "0.2.3" source = "git+https://github.com/servo/ipc-channel#48137d69955f5460da586c552de275ecdc3f4efe" dependencies = [ - "bincode 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1159,7 +1159,7 @@ dependencies = [ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "script_layout_interface 0.0.1", "script_traits 0.0.1", - "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "selectors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_macros 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1942,7 +1942,7 @@ dependencies = [ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "script_layout_interface 0.0.1", "script_traits 0.0.1", - "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "selectors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1978,7 +1978,7 @@ dependencies = [ "profile_traits 0.0.1", "range 0.0.1", "script_traits 0.0.1", - "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "selectors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "style 0.0.1", "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2026,7 +2026,7 @@ dependencies = [ [[package]] name = "selectors" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2243,7 +2243,7 @@ dependencies = [ "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "selectors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "serde_macros 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2263,7 +2263,7 @@ dependencies = [ "cssparser 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "selectors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "selectors 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "style 0.0.1", "style_traits 0.0.1", diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index a90673b9e40..049dd560eeb 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -11,7 +11,7 @@ name = "style" path = "lib.rs" [features] -gecko = ["gecko_bindings"] +gecko = ["gecko_bindings", "selectors/gecko"] servo = ["serde", "serde/nightly", "serde_macros", "heapsize", "heapsize_plugin", "style_traits/servo", "app_units/plugins", "euclid/plugins", "cssparser/heap_size", "cssparser/serde-serialization", @@ -36,7 +36,7 @@ matches = "0.1" num-traits = "0.1.32" rand = "0.3" rustc-serialize = "0.3" -selectors = "0.6" +selectors = "0.7" serde = {version = "0.7.11", optional = true} serde_macros = {version = "0.7.11", optional = true} smallvec = "0.1" diff --git a/components/style/dom.rs b/components/style/dom.rs index 11cc5f93a0e..0e94c2c5c1d 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -207,8 +207,8 @@ pub trait TElement : Sized + Copy + Clone + ElementExt + PresentationalHintsSynt fn get_state(&self) -> ElementState; - fn get_attr<'a>(&'a self, namespace: &Namespace, attr: &Atom) -> Option<&'a str>; - fn get_attrs<'a>(&'a self, attr: &Atom) -> Vec<&'a str>; + fn has_attr(&self, namespace: &Namespace, attr: &Atom) -> bool; + fn attr_equals(&self, namespace: &Namespace, attr: &Atom, value: &Atom) -> bool; /// Properly marks nodes as dirty in response to restyle hints. fn note_restyle_hint(&self, mut hint: RestyleHint) { diff --git a/components/style/keyframes.rs b/components/style/keyframes.rs index 7d219758848..82dc4c9200f 100644 --- a/components/style/keyframes.rs +++ b/components/style/keyframes.rs @@ -10,7 +10,8 @@ use std::sync::Arc; /// A number from 1 to 100, indicating the percentage of the animation where /// this keyframe should run. -#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, HeapSizeOf)] +#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct KeyframePercentage(pub f32); impl ::std::cmp::Ord for KeyframePercentage { @@ -49,7 +50,8 @@ impl KeyframePercentage { /// A keyframes selector is a list of percentages or from/to symbols, which are /// converted at parse time to percentages. -#[derive(Debug, Clone, PartialEq, HeapSizeOf)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct KeyframeSelector(Vec<KeyframePercentage>); impl KeyframeSelector { #[inline] @@ -64,7 +66,8 @@ impl KeyframeSelector { } /// A keyframe. -#[derive(Debug, Clone, PartialEq, HeapSizeOf)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct Keyframe { pub selector: KeyframeSelector, pub declarations: Arc<Vec<PropertyDeclaration>>, @@ -95,14 +98,16 @@ impl Keyframe { /// is, one autogenerated from the current computed values, or a list of /// declarations to apply. // TODO: Find a better name for this? -#[derive(Debug, Clone, PartialEq, HeapSizeOf)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum KeyframesStepValue { Declarations(Arc<Vec<PropertyDeclaration>>), ComputedValues, } /// A single step from a keyframe animation. -#[derive(Debug, Clone, PartialEq, HeapSizeOf)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct KeyframesStep { /// The percentage of the animation duration when this step starts. pub start_percentage: KeyframePercentage, @@ -126,7 +131,8 @@ impl KeyframesStep { /// of keyframes, in order. /// /// It only takes into account animable properties. -#[derive(Debug, Clone, PartialEq, HeapSizeOf)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct KeyframesAnimation { pub steps: Vec<KeyframesStep>, /// The properties that change in this animation. diff --git a/components/style/matching.rs b/components/style/matching.rs index d3dd3742d3d..5d4fa82b8ce 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -34,16 +34,13 @@ fn create_common_style_affecting_attributes_from_element<E: TElement>(element: & for attribute_info in &common_style_affecting_attributes() { match attribute_info.mode { CommonStyleAffectingAttributeMode::IsPresent(flag) => { - if element.get_attr(&ns!(), &attribute_info.atom).is_some() { + if element.has_attr(&ns!(), &attribute_info.atom) { flags.insert(flag) } } - CommonStyleAffectingAttributeMode::IsEqual(target_value, flag) => { - match element.get_attr(&ns!(), &attribute_info.atom) { - Some(element_value) if element_value == target_value => { - flags.insert(flag) - } - _ => {} + CommonStyleAffectingAttributeMode::IsEqual(ref target_value, flag) => { + if element.attr_equals(&ns!(), &attribute_info.atom, target_value) { + flags.insert(flag) } } } @@ -189,8 +186,7 @@ pub struct StyleSharingCandidate<C: ComputedValues> { pub style: Arc<C>, pub parent_style: Arc<C>, pub local_name: Atom, - // FIXME(pcwalton): Should be a list of atoms instead. - pub class: Option<String>, + pub classes: Vec<Atom>, pub namespace: Namespace, pub common_style_affecting_attributes: CommonStyleAffectingAttributes, pub link: bool, @@ -201,7 +197,7 @@ impl<C: ComputedValues> PartialEq for StyleSharingCandidate<C> { arc_ptr_eq(&self.style, &other.style) && arc_ptr_eq(&self.parent_style, &other.parent_style) && self.local_name == other.local_name && - self.class == other.class && + self.classes == other.classes && self.link == other.link && self.namespace == other.namespace && self.common_style_affecting_attributes == other.common_style_affecting_attributes @@ -246,12 +242,13 @@ impl<C: ComputedValues> StyleSharingCandidate<C> { return None } + let mut classes = Vec::new(); + element.each_class(|c| classes.push(c.clone())); Some(StyleSharingCandidate { style: style, parent_style: parent_style, local_name: element.get_local_name().clone(), - class: element.get_attr(&ns!(), &atom!("class")) - .map(|string| string.to_owned()), + classes: classes, link: element.is_link(), namespace: (*element.get_namespace()).clone(), common_style_affecting_attributes: @@ -264,14 +261,19 @@ impl<C: ComputedValues> StyleSharingCandidate<C> { return false } - // FIXME(pcwalton): Use `each_class` here instead of slow string comparison. - match (&self.class, element.get_attr(&ns!(), &atom!("class"))) { - (&None, Some(_)) | (&Some(_), None) => return false, - (&Some(ref this_class), Some(element_class)) if - element_class != &**this_class => { - return false + let mut num_classes = 0; + let mut classes_match = true; + element.each_class(|c| { + num_classes += 1; + // Note that we could do this check more cheaply if we decided to + // only consider class lists as equal if the orders match, since + // we could then index by num_classes instead of using .contains(). + if classes_match && !self.classes.contains(c) { + classes_match = false; } - (&Some(_), Some(_)) | (&None, None) => {} + }); + if !classes_match || num_classes != self.classes.len() { + return false; } if *element.get_namespace() != self.namespace { @@ -291,31 +293,25 @@ impl<C: ComputedValues> StyleSharingCandidate<C> { match attribute_info.mode { CommonStyleAffectingAttributeMode::IsPresent(flag) => { if self.common_style_affecting_attributes.contains(flag) != - element.get_attr(&ns!(), &attribute_info.atom).is_some() { + element.has_attr(&ns!(), &attribute_info.atom) { return false } } - CommonStyleAffectingAttributeMode::IsEqual(target_value, flag) => { - match element.get_attr(&ns!(), &attribute_info.atom) { - Some(ref element_value) if self.common_style_affecting_attributes - .contains(flag) && - *element_value != target_value => { + CommonStyleAffectingAttributeMode::IsEqual(ref target_value, flag) => { + let contains = self.common_style_affecting_attributes.contains(flag); + if element.has_attr(&ns!(), &attribute_info.atom) { + if !contains || !element.attr_equals(&ns!(), &attribute_info.atom, target_value) { return false } - Some(_) if !self.common_style_affecting_attributes.contains(flag) => { - return false - } - None if self.common_style_affecting_attributes.contains(flag) => { - return false - } - _ => {} + } else if contains { + return false } } } } for attribute_name in &rare_style_affecting_attributes() { - if element.get_attr(&ns!(), attribute_name).is_some() { + if element.has_attr(&ns!(), attribute_name) { return false } } @@ -601,7 +597,7 @@ pub trait ElementMatchMethods : TElement if self.style_attribute().is_some() { return StyleSharingResult::CannotShare } - if self.get_attr(&ns!(), &atom!("id")).is_some() { + if self.has_attr(&ns!(), &atom!("id")) { return StyleSharingResult::CannotShare } diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index cd85567c856..81dff023605 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -206,7 +206,8 @@ use cssparser::ToCss; use std::fmt; - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + #[derive(Debug, Clone, PartialEq)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct T(pub Vec<${to_camel_case(name)}>); impl ToCss for T { diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 504a72cf498..404698968b8 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -34,7 +34,8 @@ use values::computed::{CalcLengthOrPercentage, LengthOrPercentage}; // NB: This needs to be here because it needs all the longhands generated // beforehand. -#[derive(Copy, Clone, Debug, PartialEq, HeapSizeOf)] +#[derive(Copy, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum TransitionProperty { All, % for prop in data.longhands: @@ -92,7 +93,8 @@ impl ToCss for TransitionProperty { } } -#[derive(Clone, Debug, PartialEq, HeapSizeOf)] +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum AnimatedProperty { % for prop in data.longhands: % if prop.animatable: diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs index d9195d00df2..381ca4df1d9 100644 --- a/components/style/properties/longhand/box.mako.rs +++ b/components/style/properties/longhand/box.mako.rs @@ -94,8 +94,7 @@ ${helpers.single_keyword("position", "static absolute relative fixed", <%helpers:single_keyword_computed name="float" values="none left right" animatable="False" - need_clone="True" - gecko_ffi_name="mFloats"> + need_clone="True"> impl ToComputedValue for SpecifiedValue { type ComputedValue = computed_value::T; @@ -618,7 +617,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", pub use string_cache::Atom as SingleComputedValue; - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + #[derive(Debug, Clone, PartialEq)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct T(pub Vec<Atom>); impl ToCss for T { @@ -683,7 +683,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", pub use self::AnimationIterationCount as SingleComputedValue; - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + #[derive(Debug, Clone, PartialEq)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub enum AnimationIterationCount { Number(u32), Infinite, @@ -698,7 +699,8 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", } } - #[derive(Debug, Clone, PartialEq, HeapSizeOf)] + #[derive(Debug, Clone, PartialEq)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct T(pub Vec<AnimationIterationCount>); impl ToCss for T { diff --git a/components/style/properties/longhand/position.mako.rs b/components/style/properties/longhand/position.mako.rs index a143dab44c5..3ad07a9c2c3 100644 --- a/components/style/properties/longhand/position.mako.rs +++ b/components/style/properties/longhand/position.mako.rs @@ -77,11 +77,14 @@ ${helpers.single_keyword("justify-content", "flex-start flex-end center space-be products="servo", animatable=False)} +// FIXME(heycam): Disable align-items in geckolib since we don't support the Gecko initial value +// 'normal' yet. ${helpers.single_keyword("align-items", "stretch flex-start flex-end center baseline", experimental=True, need_clone=True, gecko_constant_prefix="NS_STYLE_ALIGN", - animatable=False)} + animatable=False, + products="servo")} ${helpers.single_keyword("align-content", "stretch flex-start flex-end center space-between space-around", experimental=True, diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index d2d07c79a73..9bb65979f4e 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -129,6 +129,7 @@ pub mod shorthands { <%include file="/shorthand/margin.mako.rs" /> <%include file="/shorthand/outline.mako.rs" /> <%include file="/shorthand/padding.mako.rs" /> + <%include file="/shorthand/position.mako.rs" /> <%include file="/shorthand/text.mako.rs" /> } @@ -1921,6 +1922,7 @@ pub fn cascade<C: ComputedValues>( } } + % if "align-items" in data.longhands_by_name: { use self::style_struct_traits::Position; use computed_values::align_self::T as align_self; @@ -1937,6 +1939,7 @@ pub fn cascade<C: ComputedValues>( style.mutate_position().set_align_self(self_align); } } + % endif // The initial value of border-*-width may be changed at computed value time. % for side in ["top", "right", "bottom", "left"]: diff --git a/components/style/properties/shorthand/position.mako.rs b/components/style/properties/shorthand/position.mako.rs new file mode 100644 index 00000000000..4710afd121e --- /dev/null +++ b/components/style/properties/shorthand/position.mako.rs @@ -0,0 +1,88 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +// https://drafts.csswg.org/css-flexbox/#flex-flow-property +<%helpers:shorthand name="flex-flow" sub_properties="flex-direction flex-wrap" + experimental="True"> + use properties::longhands::{flex_direction, flex_wrap}; + + let mut direction = None; + let mut wrap = None; + loop { + if direction.is_none() { + if let Ok(value) = input.try(|input| flex_direction::parse(context, input)) { + direction = Some(value); + continue + } + } + if wrap.is_none() { + if let Ok(value) = input.try(|input| flex_wrap::parse(context, input)) { + wrap = Some(value); + continue + } + } + break + } + + if direction.is_none() && wrap.is_none() { + return Err(()) + } + Ok(Longhands { + flex_direction: direction, + flex_wrap: wrap, + }) +</%helpers:shorthand> + +// https://drafts.csswg.org/css-flexbox/#flex-property +<%helpers:shorthand name="flex" sub_properties="flex-grow flex-shrink flex-basis" + experimental="True"> + use app_units::Au; + use values::specified::{Number, Length, LengthOrPercentageOrAutoOrContent}; + + pub fn parse_flexibility(input: &mut Parser) + -> Result<(Number, Option<Number>),()> { + let grow = try!(Number::parse_non_negative(input)); + let shrink = input.try(Number::parse_non_negative).ok(); + Ok((grow, shrink)) + } + + let mut grow = None; + let mut shrink = None; + let mut basis = None; + + if input.try(|input| input.expect_ident_matching("none")).is_ok() { + return Ok(Longhands { + flex_grow: Some(Number(0.0)), + flex_shrink: Some(Number(0.0)), + flex_basis: Some(LengthOrPercentageOrAutoOrContent::Auto) + }) + } + loop { + if grow.is_none() { + if let Ok((flex_grow, flex_shrink)) = input.try(parse_flexibility) { + grow = Some(flex_grow); + shrink = flex_shrink; + continue + } + } + if basis.is_none() { + if let Ok(value) = input.try(LengthOrPercentageOrAutoOrContent::parse) { + basis = Some(value); + continue + } + } + break + } + + if grow.is_none() && basis.is_none() { + return Err(()) + } + Ok(Longhands { + flex_grow: grow.or(Some(Number(1.0))), + flex_shrink: shrink.or(Some(Number(1.0))), + flex_basis: basis.or(Some(LengthOrPercentageOrAutoOrContent::Length(Length::Absolute(Au(0))))) + }) +</%helpers:shorthand> diff --git a/components/style/restyle_hints.rs b/components/style/restyle_hints.rs index c517a6f1a7b..cd9957322e4 100644 --- a/components/style/restyle_hints.rs +++ b/components/style/restyle_hints.rs @@ -7,9 +7,9 @@ use attr::{AttrIdentifier, AttrValue}; use element_state::*; use selector_impl::SelectorImplExt; -use selectors::Element; use selectors::matching::matches_compound_selector; use selectors::parser::{AttrSelector, Combinator, CompoundSelector, SelectorImpl, SimpleSelector}; +use selectors::{Element, MatchAttrGeneric}; use std::clone::Clone; use std::sync::Arc; use string_cache::{Atom, BorrowedAtom, BorrowedNamespace, Namespace}; @@ -81,15 +81,21 @@ impl ElementSnapshot { static EMPTY_SNAPSHOT: ElementSnapshot = ElementSnapshot { state: None, attrs: None }; +// FIXME(bholley): This implementation isn't going to work for geckolib, because +// it's fundamentally based on get_attr/match_attr, which we don't want to support +// that configuration due to the overhead of converting between UTF-16 and UTF-8. +// We'll need to figure something out when we start using restyle hints with +// geckolib, but in the mean time we can just use the trait parameters to +// specialize it to the Servo configuration. struct ElementWrapper<'a, E> - where E: Element, + where E: Element<AttrString=String>, E::Impl: SelectorImplExt { element: E, snapshot: &'a ElementSnapshot, } impl<'a, E> ElementWrapper<'a, E> - where E: Element, + where E: Element<AttrString=String>, E::Impl: SelectorImplExt { pub fn new(el: E) -> ElementWrapper<'a, E> { ElementWrapper { element: el, snapshot: &EMPTY_SNAPSHOT } @@ -100,8 +106,42 @@ impl<'a, E> ElementWrapper<'a, E> } } +#[cfg(not(feature = "gecko"))] +impl<'a, E> MatchAttrGeneric for ElementWrapper<'a, E> + where E: Element<AttrString=String>, + E: MatchAttrGeneric, + E::Impl: SelectorImplExt { + fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool + where F: Fn(&str) -> bool { + use selectors::parser::NamespaceConstraint; + match self.snapshot.attrs { + Some(_) => { + let html = self.is_html_element_in_html_document(); + let local_name = if html { &attr.lower_name } else { &attr.name }; + match attr.namespace { + NamespaceConstraint::Specific(ref ns) => self.snapshot.get_attr(ns, local_name), + NamespaceConstraint::Any => self.snapshot.get_attr_ignore_ns(local_name), + }.map_or(false, |v| test(v)) + }, + None => self.element.match_attr(attr, test) + } + } +} + +#[cfg(feature = "gecko")] +impl<'a, E> MatchAttrGeneric for ElementWrapper<'a, E> + where E: Element<AttrString=String>, + E: MatchAttrGeneric, + E::Impl: SelectorImplExt { + fn match_attr<F>(&self, _: &AttrSelector, _: F) -> bool + where F: Fn(&str) -> bool { + panic!("Not implemented for Gecko - this system will need to be redesigned"); + } +} + impl<'a, E> Element for ElementWrapper<'a, E> - where E: Element, + where E: Element<AttrString=String>, + E: MatchAttrGeneric, E::Impl: SelectorImplExt { type Impl = E::Impl; @@ -155,27 +195,6 @@ impl<'a, E> Element for ElementWrapper<'a, E> None => self.element.has_class(name), } } - #[cfg(feature = "gecko")] - fn match_attr<F>(&self, _: &AttrSelector, _: F) -> bool - where F: Fn(&str) -> bool { - panic!("Gecko can't borrow atoms as UTF-8."); - } - #[cfg(not(feature = "gecko"))] - fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool - where F: Fn(&str) -> bool { - use selectors::parser::NamespaceConstraint; - match self.snapshot.attrs { - Some(_) => { - let html = self.is_html_element_in_html_document(); - let local_name = if html { &attr.lower_name } else { &attr.name }; - match attr.namespace { - NamespaceConstraint::Specific(ref ns) => self.snapshot.get_attr(ns, local_name), - NamespaceConstraint::Any => self.snapshot.get_attr_ignore_ns(local_name), - }.map_or(false, |v| test(v)) - }, - None => self.element.match_attr(attr, test) - } - } fn is_empty(&self) -> bool { self.element.is_empty() } @@ -208,7 +227,7 @@ fn is_attr_selector<Impl: SelectorImpl>(sel: &SimpleSelector<Impl>) -> bool { SimpleSelector::AttrExists(_) | SimpleSelector::AttrEqual(_, _, _) | SimpleSelector::AttrIncludes(_, _) | - SimpleSelector::AttrDashMatch(_, _, _) | + SimpleSelector::AttrDashMatch(_, _) | SimpleSelector::AttrPrefixMatch(_, _) | SimpleSelector::AttrSubstringMatch(_, _) | SimpleSelector::AttrSuffixMatch(_, _) => true, @@ -285,28 +304,6 @@ impl<Impl: SelectorImplExt> DependencySet<Impl> { DependencySet { deps: Vec::new() } } - pub fn compute_hint<E>(&self, el: &E, snapshot: &ElementSnapshot, current_state: ElementState) - -> RestyleHint - where E: Element<Impl=Impl> + Clone { - let state_changes = snapshot.state.map_or(ElementState::empty(), |old_state| current_state ^ old_state); - let attrs_changed = snapshot.attrs.is_some(); - let mut hint = RestyleHint::empty(); - for dep in &self.deps { - if state_changes.intersects(dep.sensitivities.states) || (attrs_changed && dep.sensitivities.attrs) { - let old_el: ElementWrapper<E> = ElementWrapper::new_with_snapshot(el.clone(), snapshot); - let matched_then = matches_compound_selector(&*dep.selector, &old_el, None, &mut false); - let matches_now = matches_compound_selector(&*dep.selector, el, None, &mut false); - if matched_then != matches_now { - hint.insert(combinator_to_restyle_hint(dep.combinator)); - if hint.is_all() { - break - } - } - } - } - hint - } - pub fn note_selector(&mut self, selector: Arc<CompoundSelector<Impl>>) { let mut cur = selector; let mut combinator: Option<Combinator> = None; @@ -340,3 +337,27 @@ impl<Impl: SelectorImplExt> DependencySet<Impl> { self.deps.clear(); } } + +impl<Impl: SelectorImplExt<AttrString=String>> DependencySet<Impl> { + pub fn compute_hint<E>(&self, el: &E, snapshot: &ElementSnapshot, current_state: ElementState) + -> RestyleHint + where E: Element<Impl=Impl, AttrString=Impl::AttrString> + Clone + MatchAttrGeneric { + let state_changes = snapshot.state.map_or(ElementState::empty(), |old_state| current_state ^ old_state); + let attrs_changed = snapshot.attrs.is_some(); + let mut hint = RestyleHint::empty(); + for dep in &self.deps { + if state_changes.intersects(dep.sensitivities.states) || (attrs_changed && dep.sensitivities.attrs) { + let old_el: ElementWrapper<E> = ElementWrapper::new_with_snapshot(el.clone(), snapshot); + let matched_then = matches_compound_selector(&*dep.selector, &old_el, None, &mut false); + let matches_now = matches_compound_selector(&*dep.selector, el, None, &mut false); + if matched_then != matches_now { + hint.insert(combinator_to_restyle_hint(dep.combinator)); + if hint.is_all() { + break + } + } + } + } + hint + } +} diff --git a/components/style/selector_impl.rs b/components/style/selector_impl.rs index cf95f4a9048..cd47d0dc7f5 100644 --- a/components/style/selector_impl.rs +++ b/components/style/selector_impl.rs @@ -181,6 +181,7 @@ impl NonTSPseudoClass { pub struct ServoSelectorImpl; impl SelectorImpl for ServoSelectorImpl { + type AttrString = String; type PseudoElement = PseudoElement; type NonTSPseudoClass = NonTSPseudoClass; @@ -278,7 +279,7 @@ impl SelectorImplExt for ServoSelectorImpl { } } -impl<E: Element<Impl=ServoSelectorImpl>> ElementExt for E { +impl<E: Element<Impl=ServoSelectorImpl, AttrString=String>> ElementExt for E { fn is_link(&self) -> bool { self.match_non_ts_pseudo_class(NonTSPseudoClass::AnyLink) } diff --git a/components/style/selector_matching.rs b/components/style/selector_matching.rs index 74718d4b69b..b632d783e1e 100644 --- a/components/style/selector_matching.rs +++ b/components/style/selector_matching.rs @@ -13,11 +13,11 @@ use parser::ParserContextExtraData; use properties::{self, PropertyDeclaration, PropertyDeclarationBlock}; use restyle_hints::{ElementSnapshot, RestyleHint, DependencySet}; use selector_impl::{SelectorImplExt, ServoSelectorImpl}; -use selectors::Element; use selectors::bloom::BloomFilter; use selectors::matching::DeclarationBlock as GenericDeclarationBlock; use selectors::matching::{Rule, SelectorMap}; use selectors::parser::SelectorImpl; +use selectors::{Element, MatchAttrGeneric}; use sink::Push; use smallvec::VecLike; use std::collections::HashMap; @@ -311,7 +311,7 @@ impl<Impl: SelectorImplExt> Stylist<Impl> { pseudo: &Impl::PseudoElement, parent: &Arc<Impl::ComputedValues>) -> Option<Arc<Impl::ComputedValues>> - where E: Element<Impl=Impl> + + where E: Element<Impl=Impl, AttrString=Impl::AttrString> + PresentationalHintsSynthetizer { debug_assert!(Impl::pseudo_element_cascade_type(pseudo).is_lazy()); if self.pseudos_map.get(pseudo).is_none() { @@ -336,18 +336,6 @@ impl<Impl: SelectorImplExt> Stylist<Impl> { Some(Arc::new(computed)) } - pub fn compute_restyle_hint<E>(&self, element: &E, - snapshot: &ElementSnapshot, - // NB: We need to pass current_state as an argument because - // selectors::Element doesn't provide access to ElementState - // directly, and computing it from the ElementState would be - // more expensive than getting it directly from the caller. - current_state: ElementState) - -> RestyleHint - where E: Element<Impl=Impl> + Clone { - self.state_deps.compute_hint(element, snapshot, current_state) - } - pub fn set_device(&mut self, mut device: Device, stylesheets: &[Arc<Stylesheet<Impl>>]) { let cascaded_rule = stylesheets.iter() .flat_map(|s| s.effective_rules(&self.device).viewport()) @@ -389,7 +377,8 @@ impl<Impl: SelectorImplExt> Stylist<Impl> { pseudo_element: Option<&Impl::PseudoElement>, applicable_declarations: &mut V) -> bool - where E: Element<Impl=Impl> + PresentationalHintsSynthetizer, + where E: Element<Impl=Impl, AttrString=Impl::AttrString> + + PresentationalHintsSynthetizer, V: Push<DeclarationBlock> + VecLike<DeclarationBlock> { assert!(!self.is_device_dirty); assert!(style_attribute.is_none() || pseudo_element.is_none(), @@ -474,6 +463,21 @@ impl<Impl: SelectorImplExt> Stylist<Impl> { } } +impl<Impl: SelectorImplExt<AttrString=String>> Stylist<Impl> { + pub fn compute_restyle_hint<E>(&self, element: &E, + snapshot: &ElementSnapshot, + // NB: We need to pass current_state as an argument because + // selectors::Element doesn't provide access to ElementState + // directly, and computing it from the ElementState would be + // more expensive than getting it directly from the caller. + current_state: ElementState) + -> RestyleHint + where E: Element<Impl=Impl, AttrString=String> + Clone + MatchAttrGeneric { + self.state_deps.compute_hint(element, snapshot, current_state) + } +} + + /// Map that contains the CSS rules for a given origin. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] struct PerOriginSelectorMap<Impl: SelectorImpl> { diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index 5307f226f4b..d371bf4144f 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -67,7 +67,8 @@ pub enum CSSRule<Impl: SelectorImpl> { } -#[derive(Debug, HeapSizeOf, PartialEq)] +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] pub struct KeyframesRule { pub name: Atom, pub keyframes: Vec<Keyframe>, |