diff options
134 files changed, 3160 insertions, 1251 deletions
diff --git a/components/canvas/webgl_paint_task.rs b/components/canvas/webgl_paint_task.rs index b434f468711..aad45c916aa 100644 --- a/components/canvas/webgl_paint_task.rs +++ b/components/canvas/webgl_paint_task.rs @@ -112,14 +112,12 @@ impl WebGLPaintTask { gl::enable_vertex_attrib_array(attrib_id), CanvasWebGLMsg::GetAttribLocation(program_id, name, chan) => self.get_attrib_location(program_id, name, chan), - CanvasWebGLMsg::GetShaderInfoLog(shader_id, chan) => - self.get_shader_info_log(shader_id, chan), CanvasWebGLMsg::GetShaderParameter(shader_id, param_id, chan) => self.get_shader_parameter(shader_id, param_id, chan), CanvasWebGLMsg::GetUniformLocation(program_id, name, chan) => self.get_uniform_location(program_id, name, chan), - CanvasWebGLMsg::CompileShader(shader_id) => - self.compile_shader(shader_id), + CanvasWebGLMsg::CompileShader(shader_id, source) => + self.compile_shader(shader_id, source), CanvasWebGLMsg::CreateBuffer(chan) => self.create_buffer(chan), CanvasWebGLMsg::CreateFramebuffer(chan) => @@ -154,8 +152,6 @@ impl WebGLPaintTask { gl::bind_texture(target, id), CanvasWebGLMsg::LinkProgram(program_id) => gl::link_program(program_id), - CanvasWebGLMsg::ShaderSource(shader_id, source) => - gl::shader_source(shader_id, &[source.as_bytes()]), CanvasWebGLMsg::Uniform4fv(uniform_id, data) => gl::uniform_4f(uniform_id, data[0], data[1], data[2], data[3]), CanvasWebGLMsg::UseProgram(program_id) => @@ -303,11 +299,9 @@ impl WebGLPaintTask { gl::bind_framebuffer(target, id); } - // TODO(ecoal95): This is not spec-compliant, we must check - // the version of GLSL used. This functionality should probably - // be in the WebGLShader object #[inline] - fn compile_shader(&self, shader_id: u32) { + fn compile_shader(&self, shader_id: u32, source: String) { + gl::shader_source(shader_id, &[source.as_bytes()]); gl::compile_shader(shader_id); } @@ -323,13 +317,6 @@ impl WebGLPaintTask { chan.send(attrib_location).unwrap(); } - fn get_shader_info_log(&self, shader_id: u32, chan: IpcSender<Option<String>>) { - // TODO(ecoal95): Right now we always return a value, we should - // check for gl errors and return None there - let info = gl::get_shader_info_log(shader_id); - chan.send(Some(info)).unwrap(); - } - fn get_shader_parameter(&self, shader_id: u32, param_id: u32, diff --git a/components/canvas_traits/lib.rs b/components/canvas_traits/lib.rs index 068eada3d9d..6f2924d2171 100644 --- a/components/canvas_traits/lib.rs +++ b/components/canvas_traits/lib.rs @@ -138,7 +138,7 @@ pub enum CanvasWebGLMsg { DepthRange(f64, f64), Enable(u32), Disable(u32), - CompileShader(u32), + CompileShader(u32, String), CreateBuffer(IpcSender<Option<NonZero<u32>>>), CreateFramebuffer(IpcSender<Option<NonZero<u32>>>), CreateRenderbuffer(IpcSender<Option<NonZero<u32>>>), @@ -157,7 +157,6 @@ pub enum CanvasWebGLMsg { BindTexture(u32, u32), DrawArrays(u32, i32, i32), EnableVertexAttribArray(u32), - GetShaderInfoLog(u32, IpcSender<Option<String>>), GetShaderParameter(u32, u32, IpcSender<WebGLShaderParameter>), GetAttribLocation(u32, String, IpcSender<Option<i32>>), GetUniformLocation(u32, String, IpcSender<Option<i32>>), @@ -166,7 +165,6 @@ pub enum CanvasWebGLMsg { LineWidth(f32), PixelStorei(u32, i32), LinkProgram(u32), - ShaderSource(u32, String), Uniform4fv(i32, Vec<f32>), UseProgram(u32), VertexAttribPointer2f(u32, i32, bool, i32, u32), diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs index 95eba39c8e7..92a633674b4 100644 --- a/components/compositing/lib.rs +++ b/components/compositing/lib.rs @@ -39,9 +39,9 @@ extern crate clipboard; extern crate time; extern crate url; -#[cfg(target_os="macos")] +#[cfg(target_os = "macos")] extern crate core_graphics; -#[cfg(target_os="macos")] +#[cfg(target_os = "macos")] extern crate core_text; pub use compositor_task::{CompositorEventListener, CompositorProxy, CompositorTask}; diff --git a/components/devtools/actor.rs b/components/devtools/actor.rs index c640822d732..ca09e831dbb 100644 --- a/components/devtools/actor.rs +++ b/components/devtools/actor.rs @@ -6,13 +6,11 @@ use devtools_traits::PreciseTime; use rustc_serialize::json; -use std::any::{Any, TypeId}; +use std::any::Any; use std::cell::{Cell, RefCell}; use std::collections::HashMap; -use std::marker::Reflect; -use std::mem::{replace, transmute}; +use std::mem::replace; use std::net::TcpStream; -use std::raw::TraitObject; use std::sync::{Arc, Mutex}; #[derive(PartialEq)] @@ -24,7 +22,7 @@ pub enum ActorMessageStatus { /// A common trait for all devtools actors that encompasses an immutable name /// and the ability to process messages that are directed to particular actors. /// TODO: ensure the name is immutable -pub trait Actor: Any { +pub trait Actor: Any + ActorAsAny { fn handle_message(&self, registry: &ActorRegistry, msg_type: &str, @@ -33,55 +31,14 @@ pub trait Actor: Any { fn name(&self) -> String; } -impl Actor + Send { - /// Returns true if the boxed type is the same as `T` - #[inline] - pub fn is<T: Reflect + 'static>(&self) -> bool { - // Get TypeId of the type this function is instantiated with - let t = TypeId::of::<T>(); - - // Get TypeId of the type in the trait object - let boxed = self.get_type_id(); - - // Compare both TypeIds on equality - t == boxed - } - - /// Returns some reference to the boxed value if it is of type `T`, or - /// `None` if it isn't. - #[inline] - #[allow(unsafe_code)] - pub fn downcast_ref<T: Reflect + 'static>(&self) -> Option<&T> { - if self.is::<T>() { - unsafe { - // Get the raw representation of the trait object - let to: TraitObject = transmute(self); - - // Extract the data pointer - Some(transmute(to.data)) - } - } else { - None - } - } - - /// Returns some mutable reference to the boxed value if it is of type `T`, or - /// `None` if it isn't. - #[inline] - #[allow(unsafe_code)] - pub fn downcast_mut<T: Reflect + 'static>(&mut self) -> Option<&mut T> { - if self.is::<T>() { - unsafe { - // Get the raw representation of the trait object - let to: TraitObject = transmute(self); +trait ActorAsAny { + fn actor_as_any(&self) -> &Any; + fn actor_as_any_mut(&mut self) -> &mut Any; +} - // Extract the data pointer - Some(transmute(to.data)) - } - } else { - None - } - } +impl<T: Actor> ActorAsAny for T { + fn actor_as_any(&self) -> &Any { self } + fn actor_as_any_mut(&mut self) -> &mut Any { self } } /// A list of known, owned actors. @@ -179,15 +136,15 @@ impl ActorRegistry { } /// Find an actor by registered name - pub fn find<'a, T: Reflect + 'static>(&'a self, name: &str) -> &'a T { + pub fn find<'a, T: Any>(&'a self, name: &str) -> &'a T { let actor = self.actors.get(&name.to_string()).unwrap(); - actor.downcast_ref::<T>().unwrap() + actor.actor_as_any().downcast_ref::<T>().unwrap() } /// Find an actor by registered name - pub fn find_mut<'a, T: Reflect + 'static>(&'a mut self, name: &str) -> &'a mut T { + pub fn find_mut<'a, T: Any>(&'a mut self, name: &str) -> &'a mut T { let actor = self.actors.get_mut(&name.to_string()).unwrap(); - actor.downcast_mut::<T>().unwrap() + actor.actor_as_any_mut().downcast_mut::<T>().unwrap() } /// Attempt to process a message as directed by its `to` property. If the actor is not diff --git a/components/devtools/actors/console.rs b/components/devtools/actors/console.rs index 7c859369274..30c0b98d77a 100644 --- a/components/devtools/actors/console.rs +++ b/components/devtools/actors/console.rs @@ -17,9 +17,9 @@ use devtools_traits::EvaluateJSReply::{StringValue, BooleanValue, ActorValue}; use devtools_traits::{CachedConsoleMessageTypes, DevtoolScriptControlMsg, PAGE_ERROR, CONSOLE_API}; use msg::constellation_msg::PipelineId; -use core::cell::RefCell; use ipc_channel::ipc::{self, IpcSender}; use rustc_serialize::json::{self, Json, ToJson}; +use std::cell::RefCell; use std::collections::BTreeMap; use std::net::TcpStream; use std::sync::mpsc::channel; diff --git a/components/devtools/actors/timeline.rs b/components/devtools/actors/timeline.rs index 17327b1043e..b38b85a97b5 100644 --- a/components/devtools/actors/timeline.rs +++ b/components/devtools/actors/timeline.rs @@ -6,7 +6,6 @@ use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use msg::constellation_msg::PipelineId; use rustc_serialize::{json, Encoder, Encodable}; use std::cell::RefCell; -use std::collections::{HashMap, VecDeque}; use std::net::TcpStream; use std::sync::mpsc::channel; use std::sync::{Arc, Mutex}; @@ -17,7 +16,7 @@ use actors::framerate::FramerateActor; use actors::memory::{MemoryActor, TimelineMemoryReply}; use devtools_traits::DevtoolScriptControlMsg; use devtools_traits::DevtoolScriptControlMsg::{SetTimelineMarkers, DropTimelineMarkers}; -use devtools_traits::{PreciseTime, TimelineMarker, TracingMetadata, TimelineMarkerType}; +use devtools_traits::{PreciseTime, TimelineMarker, TimelineMarkerType}; use protocol::JsonPacketStream; use util::task; @@ -148,72 +147,15 @@ impl TimelineActor { return; } - /// Select root(with depth 0) TimelineMarker pair (IntervalStart + IntervalEnd) - /// from queue and add marker to emitter - /// Return true if closed (IntervalStart + IntervalEnd) pair was founded - fn group(queue: &mut VecDeque<TimelineMarker>, depth: usize, - start_payload: Option<TimelineMarker>, emitter: &Emitter, - markers: &mut Vec<TimelineMarkerReply>) -> bool { - - if let Some(start_payload) = start_payload { - if start_payload.metadata != TracingMetadata::IntervalStart { - panic!("Start payload doesn't have metadata IntervalStart"); - } - - if let Some(end_payload) = queue.pop_front() { - match end_payload.metadata { - TracingMetadata::IntervalEnd => { - if depth == 0 { - // Emit TimelineMarkerReply, pair was found - markers.push(emitter.marker(start_payload, end_payload)); - } - return true; - } - TracingMetadata::IntervalStart => { - if group(queue, depth + 1, Some(end_payload), emitter, markers) { - return group(queue, depth, Some(start_payload), emitter, markers); - } else { - queue.push_front(start_payload); - } - } - _ => panic!("Unknown tracingMetadata") - } - } else { - queue.push_front(start_payload); - } - } - - false - } - task::spawn_named("PullTimelineMarkers".to_string(), move || { - let mut queues = HashMap::new(); - queues.insert("Reflow".to_string(), VecDeque::new()); - queues.insert("DOMEvent".to_string(), VecDeque::new()); - loop { if !*is_recording.lock().unwrap() { break; } - // Creating queues by marker.name - loop { - match receiver.try_recv() { - Ok(marker) => { - if let Some(list) = queues.get_mut(&marker.name) { - list.push_back(marker); - } - } - - Err(_) => break - } - } - - // Emit all markers let mut markers = vec![]; - for (_, queue) in &mut queues { - let start_payload = queue.pop_front(); - group(queue, 0, start_payload, &emitter, &mut markers); + while let Ok(marker) = receiver.try_recv() { + markers.push(emitter.marker(marker)); } emitter.send(markers); @@ -336,14 +278,13 @@ impl Emitter { } } - fn marker(&self, start_payload: TimelineMarker, end_payload: TimelineMarker) - -> TimelineMarkerReply { + fn marker(&self, payload: TimelineMarker) -> TimelineMarkerReply { TimelineMarkerReply { - name: start_payload.name, - start: HighResolutionStamp::new(self.start_stamp, start_payload.time), - end: HighResolutionStamp::new(self.start_stamp, end_payload.time), - stack: start_payload.stack, - endStack: end_payload.stack, + name: payload.name, + start: HighResolutionStamp::new(self.start_stamp, payload.start_time), + end: HighResolutionStamp::new(self.start_stamp, payload.end_time), + stack: payload.start_stack, + endStack: payload.end_stack, } } diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs index 07a3889d699..4be05c919d1 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -11,12 +11,8 @@ #![crate_type = "rlib"] #![feature(box_syntax)] -#![feature(core)] #![feature(custom_derive)] -#![feature(get_type_id)] #![feature(plugin)] -#![feature(raw)] -#![feature(reflect_marker)] #![plugin(serde_macros)] #![allow(non_snake_case)] @@ -25,7 +21,6 @@ #[macro_use] extern crate log; -extern crate core; extern crate devtools_traits; extern crate rustc_serialize; extern crate ipc_channel; diff --git a/components/devtools_traits/lib.rs b/components/devtools_traits/lib.rs index 8df97634fad..c8889fcd5fb 100644 --- a/components/devtools_traits/lib.rs +++ b/components/devtools_traits/lib.rs @@ -124,21 +124,19 @@ pub struct NodeInfo { pub incompleteValue: bool, } -#[derive(PartialEq, Eq, Deserialize, Serialize)] -pub enum TracingMetadata { - Default, - IntervalStart, - IntervalEnd, - Event, - EventBacktrace, +pub struct StartedTimelineMarker { + name: String, + start_time: PreciseTime, + start_stack: Option<Vec<()>>, } #[derive(Deserialize, Serialize)] pub struct TimelineMarker { pub name: String, - pub metadata: TracingMetadata, - pub time: PreciseTime, - pub stack: Option<Vec<()>>, + pub start_time: PreciseTime, + pub start_stack: Option<Vec<()>>, + pub end_time: PreciseTime, + pub end_stack: Option<Vec<()>>, } #[derive(PartialEq, Eq, Hash, Clone, Deserialize, Serialize)] @@ -270,12 +268,23 @@ pub enum NetworkEvent { } impl TimelineMarker { - pub fn new(name: String, metadata: TracingMetadata) -> TimelineMarker { - TimelineMarker { + pub fn start(name: String) -> StartedTimelineMarker { + StartedTimelineMarker { name: name, - metadata: metadata, - time: PreciseTime::now(), - stack: None, + start_time: PreciseTime::now(), + start_stack: None, + } + } +} + +impl StartedTimelineMarker { + pub fn end(self) -> TimelineMarker { + TimelineMarker { + name: self.name, + start_time: self.start_time, + start_stack: self.start_stack, + end_time: PreciseTime::now(), + end_stack: None, } } } diff --git a/components/gfx/font.rs b/components/gfx/font.rs index fd17fa4e467..ba11c2a63cc 100644 --- a/components/gfx/font.rs +++ b/components/gfx/font.rs @@ -101,13 +101,13 @@ pub struct Font { bitflags! { flags ShapingFlags: u8 { - #[doc="Set if the text is entirely whitespace."] + #[doc = "Set if the text is entirely whitespace."] const IS_WHITESPACE_SHAPING_FLAG = 0x01, - #[doc="Set if we are to ignore ligatures."] + #[doc = "Set if we are to ignore ligatures."] const IGNORE_LIGATURES_SHAPING_FLAG = 0x02, - #[doc="Set if we are to disable kerning."] + #[doc = "Set if we are to disable kerning."] const DISABLE_KERNING_SHAPING_FLAG = 0x04, - #[doc="Text direction is right-to-left."] + #[doc = "Text direction is right-to-left."] const RTL_FLAG = 0x08, } } diff --git a/components/gfx/font_context.rs b/components/gfx/font_context.rs index 18ee5e9ff42..640b2b8250c 100644 --- a/components/gfx/font_context.rs +++ b/components/gfx/font_context.rs @@ -31,16 +31,16 @@ use std::sync::Arc; use azure::azure_hl::BackendType; use azure::scaled_font::ScaledFont; -#[cfg(any(target_os="linux", target_os = "android"))] +#[cfg(any(target_os = "linux", target_os = "android"))] use azure::scaled_font::FontInfo; -#[cfg(any(target_os="linux", target_os = "android"))] +#[cfg(any(target_os = "linux", target_os = "android"))] fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: Au) -> ScaledFont { ScaledFont::new(BackendType::Skia, FontInfo::FontData(&template.bytes), pt_size.to_f32_px()) } -#[cfg(target_os="macos")] +#[cfg(target_os = "macos")] fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: Au) -> ScaledFont { let cgfont = template.ctfont().as_ref().unwrap().copy_to_CGFont(); ScaledFont::new(BackendType::Skia, &cgfont, pt_size.to_f32_px()) diff --git a/components/gfx/lib.rs b/components/gfx/lib.rs index 6ffbe36fb5b..db73b9b200d 100644 --- a/components/gfx/lib.rs +++ b/components/gfx/lib.rs @@ -3,12 +3,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #![feature(arc_weak)] -#![cfg_attr(any(target_os="linux", target_os = "android"), feature(box_raw))] +#![cfg_attr(any(target_os = "linux", target_os = "android"), feature(box_raw))] #![feature(box_syntax)] #![feature(custom_attribute)] #![feature(custom_derive)] #![feature(hashmap_hasher)] -#![cfg_attr(any(target_os="linux", target_os = "android"), feature(heap_api))] +#![cfg_attr(any(target_os = "linux", target_os = "android"), feature(heap_api))] #![feature(mpsc_select)] #![feature(plugin)] #![feature(str_char)] @@ -55,23 +55,23 @@ extern crate canvas_traits; extern crate harfbuzz; // Linux and Android-specific library dependencies -#[cfg(any(target_os="linux", target_os = "android"))] +#[cfg(any(target_os = "linux", target_os = "android"))] extern crate fontconfig; -#[cfg(any(target_os="linux", target_os = "android"))] +#[cfg(any(target_os = "linux", target_os = "android"))] extern crate freetype; // Mac OS-specific library dependencies -#[cfg(target_os="macos")] extern crate core_foundation; -#[cfg(target_os="macos")] extern crate core_graphics; -#[cfg(target_os="macos")] extern crate core_text; +#[cfg(target_os = "macos")] extern crate core_foundation; +#[cfg(target_os = "macos")] extern crate core_graphics; +#[cfg(target_os = "macos")] extern crate core_text; pub use paint_context::PaintContext; // Private painting modules mod paint_context; -#[path="display_list/mod.rs"] +#[path = "display_list/mod.rs"] pub mod display_list; pub mod paint_task; @@ -85,7 +85,7 @@ pub mod font_template; mod filters; // Platform-specific implementations. -#[path="platform/mod.rs"] +#[path = "platform/mod.rs"] pub mod platform; // Text diff --git a/components/gfx/paint_context.rs b/components/gfx/paint_context.rs index a96d413cafc..2c38e0e6fea 100644 --- a/components/gfx/paint_context.rs +++ b/components/gfx/paint_context.rs @@ -72,6 +72,19 @@ enum DashSize { DashedBorder = 3 } +#[derive(Copy, Clone, Debug)] +struct Ellipse { + origin: Point2D<f32>, + width: f32, + height: f32, +} + +#[derive(Copy, Clone, Debug)] +struct Line { + start: Point2D<f32>, + end: Point2D<f32>, +} + impl<'a> PaintContext<'a> { pub fn draw_target(&self) -> &DrawTarget { &self.draw_target @@ -355,6 +368,104 @@ impl<'a> PaintContext<'a> { self.draw_target.push_clip(&path_builder.finish()); } + fn solve_quadratic(a: f32, b: f32, c: f32) -> (Option<f32>, Option<f32>) { + let discriminant = b * b - 4. * a * c; + if discriminant < 0. { + return (None, None); + } + let x1 = (-b + discriminant.sqrt())/(2. * a); + let x2 = (-b - discriminant.sqrt())/(2. * a); + if discriminant == 0. { + return (Some(x1), None); + } + return (Some(x1), Some(x2)); + } + + fn intersect_ellipse_line(e: Ellipse, l: Line) -> (Option<Point2D<f32>>, Option<Point2D<f32>>) { + debug_assert!(l.end.x - l.start.x > f32::EPSILON, "Error line segment end.x > start.x!!"); + // shift the origin to center of the ellipse. + let line = Line { start: l.start - e.origin, + end: l.end - e.origin }; + + let a = (line.end.y - line.start.y)/(line.end.x - line.start.x); + let b = line.start.y - (a * line.start.x); + // given the equation of a line, + // y = a * x + b, + // and the equation of an ellipse, + // x^2/w^2 + y^2/h^2 = 1, + // substitute y = a * x + b, giving + // x^2/w^2 + (a^2x^2 + 2abx + b^2)/h^2 = 1 + // then simplify to + // (h^2 + w^2a^2)x^2 + 2abw^2x + (b^2w^2 - w^2h^2) = 0 + // finally solve for w using the quadratic equation. + let w = e.width; + let h = e.height; + let quad_a = h * h + w * w * a * a; + let quad_b = 2. * a * b * w * w; + let quad_c = b * b * w * w - w * w * h * h; + let intersections = PaintContext::solve_quadratic(quad_a, quad_b, quad_c); + match intersections { + (Some(x0), Some(x1)) => { + let mut p0 = Point2D::new(x0, a * x0 + b) + e.origin; + let mut p1 = Point2D::new(x1, a * x1 + b) + e.origin; + if x0 > x1 { + mem::swap(&mut p0, &mut p1); + } + (Some(p0), Some(p1)) + }, + (Some(x0), None) => { + let p = Point2D::new(x0, a * x0 + b) + e.origin; + (Some(p), None) + }, + (None, Some(x1)) => { + let p = Point2D::new(x1, a * x1 + b) + e.origin; + (Some(p), None) + }, + (None, None) => (None, None), + } + } + + // Given an ellipse and line segment, the line segment may intersect the + // ellipse at 0, 1, or 2 points. We compute those intersection points. + // For each intersection point the angle of the point on the ellipse relative to + // the top|bottom of the ellipse is computed. + // Examples: + // - intersection at ellipse.center + (0, ellipse.height), the angle is 0 rad. + // - intersection at ellipse.center + (0, -ellipse.height), the angle is 0 rad. + // - intersection at ellipse.center + (+-ellipse.width, 0), the angle is pi/2. + fn ellipse_line_intersection_angles(e: Ellipse, l: Line) + -> (Option<(Point2D<f32>, f32)>, Option<(Point2D<f32>, f32)>) { + fn point_angle(e: Ellipse, intersect_point: Point2D<f32>) -> f32 { + ((intersect_point.y - e.origin.y).abs() / e.height).asin() + } + + let intersection = PaintContext::intersect_ellipse_line(e, l); + match intersection { + (Some(p0), Some(p1)) => (Some((p0, point_angle(e, p0))), Some((p1, point_angle(e, p1)))), + (Some(p0), None) => (Some((p0, point_angle(e, p0))), None), + (None, Some(p1)) => (None, Some((p1, point_angle(e, p1)))), + (None, None) => (None, None), + } + } + + fn ellipse_rightmost_line_intersection_angle(e: Ellipse, l: Line) -> Option<f32> { + match PaintContext::ellipse_line_intersection_angles(e, l) { + (Some((p0, angle0)), Some((p1, _))) if p0.x > p1.x => Some(angle0), + (_, Some((_, angle1))) => Some(angle1), + (Some((_, angle0)), None) => Some(angle0), + (None, None) => None, + } + } + + fn ellipse_leftmost_line_intersection_angle(e: Ellipse, l: Line) -> Option<f32> { + match PaintContext::ellipse_line_intersection_angles(e, l) { + (Some((p0, angle0)), Some((p1, _))) if p0.x < p1.x => Some(angle0), + (_, Some((_, angle1))) => Some(angle1), + (Some((_, angle0)), None) => Some(angle0), + (None, None) => None, + } + } + // The following comment is wonderful, and stolen from // gecko:gfx/thebes/gfxContext.cpp:RoundedRectangle for reference. // @@ -450,6 +561,11 @@ impl<'a> PaintContext<'a> { let box_BL = box_TL + Point2D::new(0.0, bounds.size.height); let box_BR = box_TL + Point2D::new(bounds.size.width, bounds.size.height); + let inner_TL = box_TL + Point2D::new(border.left, border.top); + let inner_TR = box_TR + Point2D::new(-border.right, border.top); + let inner_BR = box_BR + Point2D::new(-border.right, -border.bottom); + let inner_BL = box_BL + Point2D::new(border.left, -border.bottom); + let rad_R: AzFloat = 0.; let rad_BR = rad_R + f32::consts::FRAC_PI_4; let rad_B = rad_BR + f32::consts::FRAC_PI_4; @@ -457,7 +573,6 @@ impl<'a> PaintContext<'a> { let rad_L = rad_BL + f32::consts::FRAC_PI_4; let rad_TL = rad_L + f32::consts::FRAC_PI_4; let rad_T = rad_TL + f32::consts::FRAC_PI_4; - let rad_TR = rad_T + f32::consts::FRAC_PI_4; fn dx(x: AzFloat) -> Point2D<AzFloat> { Point2D::new(x, 0.) @@ -475,12 +590,41 @@ impl<'a> PaintContext<'a> { Point2D::new(0., if cond { dy } else { 0. }) } + fn compatible_borders_corner(border1_width: f32, border2_width: f32) -> bool { + (border1_width - border2_width).abs() <= f32::EPSILON + } + + let distance_to_elbow_TL = + if border.top == border.left { + (radius.top_left - border.top).max(0.) + } else { + 0. + }; + let distance_to_elbow_TR = + if border.top == border.right { + (radius.top_right - border.top).max(0.) + } else { + 0. + }; + let distance_to_elbow_BR = + if border.right == border.bottom { + (radius.bottom_right - border.bottom).max(0.) + } else { + 0. + }; + let distance_to_elbow_BL = + if border.left == border.bottom { + (radius.bottom_left - border.bottom).max(0.) + } else { + 0. + }; + match direction { Direction::Top => { let edge_TL = box_TL + dx(radius.top_left.max(border.left)); let edge_TR = box_TR + dx(-radius.top_right.max(border.right)); - let edge_BR = edge_TR + dy(border.top); - let edge_BL = edge_TL + dy(border.top); + let edge_BR = box_TR + dx(-border.right - distance_to_elbow_TR) + dy(border.top); + let edge_BL = box_TL + dx(border.left + distance_to_elbow_TL) + dy(border.top); let corner_TL = edge_TL + dx_if(radius.top_left == 0., -border.left); let corner_TR = edge_TR + dx_if(radius.top_right == 0., border.right); @@ -497,11 +641,18 @@ impl<'a> PaintContext<'a> { // the origin is the center of the arcs we're about to draw. let origin = edge_TR + Point2D::new((border.right - radius.top_right).max(0.), radius.top_right); - // the elbow is the inside of the border's curve. - let distance_to_elbow = (radius.top_right - border.top).max(0.); - - path_builder.arc(origin, radius.top_right, rad_T, rad_TR, false); - path_builder.arc(origin, distance_to_elbow, rad_TR, rad_T, true); + let angle = if compatible_borders_corner(border.top, border.right) { + f32::consts::FRAC_PI_4 + } else { + let line = Line { start: inner_TR, end: box_TR }; + let ellipse = Ellipse { origin: origin, width: radius.top_right, height: radius.top_right }; + PaintContext::ellipse_rightmost_line_intersection_angle(ellipse, line).unwrap() + }; + + path_builder.arc(origin, radius.top_right, rad_T, rad_R - angle, false); + if distance_to_elbow_TR != 0. { + path_builder.arc(origin, distance_to_elbow_TR, rad_R - angle, rad_T, true); + } } match mode { @@ -514,18 +665,26 @@ impl<'a> PaintContext<'a> { if radius.top_left != 0. { let origin = edge_TL + Point2D::new(-(border.left - radius.top_left).max(0.), - radius.top_left); - let distance_to_elbow = (radius.top_left - border.top).max(0.); + radius.top_left); + let angle = if compatible_borders_corner(border.top, border.left) { + f32::consts::FRAC_PI_4 + } else { + let line = Line { start: box_TL, end: inner_TL }; + let ellipse = Ellipse { origin: origin, width: radius.top_left, height: radius.top_left }; + PaintContext::ellipse_leftmost_line_intersection_angle(ellipse, line).unwrap() + }; - path_builder.arc(origin, distance_to_elbow, rad_T, rad_TL, true); - path_builder.arc(origin, radius.top_left, rad_TL, rad_T, false); + if distance_to_elbow_TL != 0. { + path_builder.arc(origin, distance_to_elbow_TL, rad_T, rad_L + angle, true); + } + path_builder.arc(origin, radius.top_left, rad_L + angle, rad_T, false); } } Direction::Left => { let edge_TL = box_TL + dy(radius.top_left.max(border.top)); let edge_BL = box_BL + dy(-radius.bottom_left.max(border.bottom)); - let edge_TR = edge_TL + dx(border.left); - let edge_BR = edge_BL + dx(border.left); + let edge_TR = box_TL + dx(border.left) + dy(border.top + distance_to_elbow_TL); + let edge_BR = box_BL + dx(border.left) + dy(-border.bottom - distance_to_elbow_BL); let corner_TL = edge_TL + dy_if(radius.top_left == 0., -border.top); let corner_BL = edge_BL + dy_if(radius.bottom_left == 0., border.bottom); @@ -540,11 +699,20 @@ impl<'a> PaintContext<'a> { if radius.top_left != 0. { let origin = edge_TL + Point2D::new(radius.top_left, - -(border.top - radius.top_left).max(0.)); - let distance_to_elbow = (radius.top_left - border.left).max(0.); + -(border.top - radius.top_left).max(0.)); - path_builder.arc(origin, radius.top_left, rad_L, rad_TL, false); - path_builder.arc(origin, distance_to_elbow, rad_TL, rad_L, true); + let angle = if compatible_borders_corner(border.top, border.left) { + f32::consts::FRAC_PI_4 + } else { + let line = Line { start: box_TL, end: inner_TL }; + let ellipse = Ellipse { origin: origin, width: radius.top_left, height: radius.top_left }; + PaintContext::ellipse_leftmost_line_intersection_angle(ellipse, line).unwrap() + }; + + path_builder.arc(origin, radius.top_left, rad_L, rad_L + angle, false); + if distance_to_elbow_TL != 0. { + path_builder.arc(origin, distance_to_elbow_TL, rad_L + angle, rad_L, true); + } } match mode { @@ -558,18 +726,28 @@ impl<'a> PaintContext<'a> { if radius.bottom_left != 0. { let origin = edge_BL + Point2D::new(radius.bottom_left, - (border.bottom - radius.bottom_left).max(0.)); - let distance_to_elbow = (radius.bottom_left - border.left).max(0.); - - path_builder.arc(origin, distance_to_elbow, rad_L, rad_BL, true); - path_builder.arc(origin, radius.bottom_left, rad_BL, rad_L, false); + (border.bottom - radius.bottom_left).max(0.)); + let angle = if compatible_borders_corner(border.bottom, border.left) { + f32::consts::FRAC_PI_4 + } else { + let line = Line { start: box_BL, end: inner_BL }; + let ellipse = Ellipse { origin: origin, + width: radius.bottom_left, + height: radius.bottom_left }; + PaintContext::ellipse_leftmost_line_intersection_angle(ellipse, line).unwrap() + }; + + if distance_to_elbow_BL != 0. { + path_builder.arc(origin, distance_to_elbow_BL, rad_L, rad_L - angle, true); + } + path_builder.arc(origin, radius.bottom_left, rad_L - angle, rad_L, false); } } Direction::Right => { let edge_TR = box_TR + dy(radius.top_right.max(border.top)); let edge_BR = box_BR + dy(-radius.bottom_right.max(border.bottom)); - let edge_TL = edge_TR + dx(-border.right); - let edge_BL = edge_BR + dx(-border.right); + let edge_TL = box_TR + dx(-border.right) + dy(border.top + distance_to_elbow_TR); + let edge_BL = box_BR + dx(-border.right) + dy(-border.bottom - distance_to_elbow_BR); let corner_TR = edge_TR + dy_if(radius.top_right == 0., -border.top); let corner_BR = edge_BR + dy_if(radius.bottom_right == 0., border.bottom); @@ -585,10 +763,18 @@ impl<'a> PaintContext<'a> { if radius.top_right != 0. { let origin = edge_TR + Point2D::new(-radius.top_right, -(border.top - radius.top_right).max(0.)); - let distance_to_elbow = (radius.top_right - border.right).max(0.); + let angle = if compatible_borders_corner(border.top, border.right) { + f32::consts::FRAC_PI_4 + } else { + let line = Line { start: inner_TR, end: box_TR }; + let ellipse = Ellipse { origin: origin, width: radius.top_right, height: radius.top_right }; + PaintContext::ellipse_rightmost_line_intersection_angle(ellipse, line).unwrap() + }; - path_builder.arc(origin, distance_to_elbow, rad_R, rad_TR, true); - path_builder.arc(origin, radius.top_right, rad_TR, rad_R, false); + if distance_to_elbow_TR != 0. { + path_builder.arc(origin, distance_to_elbow_TR, rad_R, rad_R - angle, true); + } + path_builder.arc(origin, radius.top_right, rad_R - angle, rad_R, false); } match mode { @@ -602,18 +788,28 @@ impl<'a> PaintContext<'a> { if radius.bottom_right != 0. { let origin = edge_BR + Point2D::new(-radius.bottom_right, - (border.bottom - radius.bottom_right).max(0.)); - let distance_to_elbow = (radius.bottom_right - border.right).max(0.); - - path_builder.arc(origin, radius.bottom_right, rad_R, rad_BR, false); - path_builder.arc(origin, distance_to_elbow, rad_BR, rad_R, true); + (border.bottom - radius.bottom_right).max(0.)); + let angle = if compatible_borders_corner(border.bottom, border.right) { + f32::consts::FRAC_PI_4 + } else { + let line = Line { start: inner_BR, end: box_BR }; + let ellipse = Ellipse { origin: origin, + width: radius.bottom_right, + height: radius.bottom_right }; + PaintContext::ellipse_rightmost_line_intersection_angle(ellipse, line).unwrap() + }; + + path_builder.arc(origin, radius.bottom_right, rad_R, rad_R + angle, false); + if distance_to_elbow_BR != 0. { + path_builder.arc(origin, distance_to_elbow_BR, rad_R + angle, rad_R, true); + } } } Direction::Bottom => { let edge_BL = box_BL + dx(radius.bottom_left.max(border.left)); let edge_BR = box_BR + dx(-radius.bottom_right.max(border.right)); - let edge_TL = edge_BL + dy(-border.bottom); - let edge_TR = edge_BR + dy(-border.bottom); + let edge_TL = box_BL + dy(-border.bottom) + dx(border.left + distance_to_elbow_BL); + let edge_TR = box_BR + dy(-border.bottom) + dx(-border.right - distance_to_elbow_BR); let corner_BR = edge_BR + dx_if(radius.bottom_right == 0., border.right); let corner_BL = edge_BL + dx_if(radius.bottom_left == 0., -border.left); @@ -629,10 +825,20 @@ impl<'a> PaintContext<'a> { if radius.bottom_right != 0. { let origin = edge_BR + Point2D::new((border.right - radius.bottom_right).max(0.), -radius.bottom_right); - let distance_to_elbow = (radius.bottom_right - border.bottom).max(0.); - - path_builder.arc(origin, distance_to_elbow, rad_B, rad_BR, true); - path_builder.arc(origin, radius.bottom_right, rad_BR, rad_B, false); + let angle = if compatible_borders_corner(border.bottom, border.right) { + f32::consts::FRAC_PI_4 + } else { + let line = Line { start: inner_BR, end: box_BR }; + let ellipse = Ellipse { origin: origin, + width: radius.bottom_right, + height: radius.bottom_right }; + PaintContext::ellipse_rightmost_line_intersection_angle(ellipse, line).unwrap() + }; + + if distance_to_elbow_BR != 0. { + path_builder.arc(origin, distance_to_elbow_BR, rad_B, rad_R + angle, true); + } + path_builder.arc(origin, radius.bottom_right, rad_R + angle, rad_B, false); } match mode { @@ -645,11 +851,21 @@ impl<'a> PaintContext<'a> { if radius.bottom_left != 0. { let origin = edge_BL - Point2D::new((border.left - radius.bottom_left).max(0.), - radius.bottom_left); - let distance_to_elbow = (radius.bottom_left - border.bottom).max(0.); - - path_builder.arc(origin, radius.bottom_left, rad_B, rad_BL, false); - path_builder.arc(origin, distance_to_elbow, rad_BL, rad_B, true); + radius.bottom_left); + let angle = if compatible_borders_corner(border.bottom, border.left) { + f32::consts::FRAC_PI_4 + } else { + let line = Line { start: box_BL, end: inner_BL }; + let ellipse = Ellipse { origin: origin, + width: radius.bottom_left, + height: radius.bottom_left }; + PaintContext::ellipse_leftmost_line_intersection_angle(ellipse, line).unwrap() + }; + + path_builder.arc(origin, radius.bottom_left, rad_B, rad_L - angle, false); + if distance_to_elbow_BL != 0. { + path_builder.arc(origin, distance_to_elbow_BL, rad_L - angle, rad_B, true); + } } } } @@ -960,14 +1176,13 @@ impl<'a> PaintContext<'a> { // FIXME(https://github.com/rust-lang/rust/issues/23338) let font = self.font_context.get_paint_font_from_template( &text.text_run.font_template, text.text_run.actual_pt_size); - font - .borrow() - .draw_text(&temporary_draw_target.draw_target, - &*text.text_run, - &text.range, - baseline_origin, - text.text_color, - opts::get().enable_text_antialiasing); + font.borrow() + .draw_text(&temporary_draw_target.draw_target, + &*text.text_run, + &text.range, + baseline_origin, + text.text_color, + opts::get().enable_text_antialiasing); } // Blur, if necessary. @@ -1029,8 +1244,9 @@ impl<'a> PaintContext<'a> { // Calculate the transform matrix. let old_transform = self.draw_target.get_transform(); - let inflated_size = Rect::new(Point2D::new(0.0, 0.0), Size2D::new(size.width as AzFloat, - size.height as AzFloat)); + let inflated_size = Rect::new(Point2D::new(0.0, 0.0), + Size2D::new(size.width as AzFloat, + size.height as AzFloat)); let temporary_draw_target_bounds = old_transform.transform_rect(&inflated_size); matrix = Matrix2D::identity().translate( -temporary_draw_target_bounds.origin.x as AzFloat, @@ -1060,7 +1276,8 @@ impl<'a> PaintContext<'a> { self.draw_target.set_transform(&Matrix2D::identity()); let rect = Rect::new(Point2D::new(0.0, 0.0), self.draw_target.get_size().to_azure_size()); - let rect_temporary = Rect::new(Point2D::new(0.0, 0.0), temporary_draw_target.get_size().to_azure_size()); + let rect_temporary = Rect::new(Point2D::new(0.0, 0.0), + temporary_draw_target.get_size().to_azure_size()); // Create the Azure filter pipeline. let mut accum_blur = Au(0); @@ -1081,7 +1298,10 @@ impl<'a> PaintContext<'a> { self.pop_clip_if_applicable(); debug!("######### use expanded Rect."); - self.draw_target.draw_filter(&filter_node, &rect_temporary, &rect_temporary.origin, draw_options); + self.draw_target.draw_filter(&filter_node, + &rect_temporary, + &rect_temporary.origin, + draw_options); self.push_clip_if_applicable(); } else { debug!("######### use regular Rect."); @@ -1132,9 +1352,10 @@ impl<'a> PaintContext<'a> { } // Draw the shadow, and blur if we need to. - temporary_draw_target.draw_target.fill(&path, - Pattern::Color(ColorPattern::new(color)).to_pattern_ref(), - &DrawOptions::new(1.0, CompositionOp::Over, AntialiasMode::None)); + temporary_draw_target.draw_target.fill( + &path, + Pattern::Color(ColorPattern::new(color)).to_pattern_ref(), + &DrawOptions::new(1.0, CompositionOp::Over, AntialiasMode::None)); self.blur_if_necessary(temporary_draw_target, blur_radius); // Undo the draw target's clip if we need to, and push back the stacking context clip. diff --git a/components/gfx/platform/freetype/font_list.rs b/components/gfx/platform/freetype/font_list.rs index f3557d7dd2f..9e0a5177312 100644 --- a/components/gfx/platform/freetype/font_list.rs +++ b/components/gfx/platform/freetype/font_list.rs @@ -139,7 +139,7 @@ pub fn get_system_default_family(generic_name: &str) -> Option<String> { } } -#[cfg(target_os="linux")] +#[cfg(target_os = "linux")] pub fn get_last_resort_font_families() -> Vec<String> { vec!( "Fira Sans".to_owned(), @@ -148,7 +148,7 @@ pub fn get_last_resort_font_families() -> Vec<String> { ) } -#[cfg(target_os="android")] +#[cfg(target_os = "android")] pub fn get_last_resort_font_families() -> Vec<String> { vec!("Roboto".to_owned()) } diff --git a/components/gfx/platform/macos/font.rs b/components/gfx/platform/macos/font.rs index b96f217e0c9..eb33e326230 100644 --- a/components/gfx/platform/macos/font.rs +++ b/components/gfx/platform/macos/font.rs @@ -33,11 +33,6 @@ pub struct FontTable { data: CFData, } -// Noncopyable. -impl Drop for FontTable { - fn drop(&mut self) {} -} - impl FontTable { pub fn wrap(data: CFData) -> FontTable { FontTable { data: data } diff --git a/components/gfx/platform/mod.rs b/components/gfx/platform/mod.rs index f3bc3f6ec08..085da7cc932 100644 --- a/components/gfx/platform/mod.rs +++ b/components/gfx/platform/mod.rs @@ -2,13 +2,13 @@ * 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/. */ -#[cfg(any(target_os="linux", target_os = "android"))] +#[cfg(any(target_os = "linux", target_os = "android"))] pub use platform::freetype::{font, font_context, font_list, font_template}; -#[cfg(target_os="macos")] +#[cfg(target_os = "macos")] pub use platform::macos::{font, font_context, font_list, font_template}; -#[cfg(any(target_os="linux", target_os = "android"))] +#[cfg(any(target_os = "linux", target_os = "android"))] pub mod freetype { pub mod font; pub mod font_context; @@ -16,7 +16,7 @@ pub mod freetype { pub mod font_template; } -#[cfg(target_os="macos")] +#[cfg(target_os = "macos")] pub mod macos { pub mod font; pub mod font_context; diff --git a/components/gfx/text/glyph.rs b/components/gfx/text/glyph.rs index 29918e5d2d9..d27128014b5 100644 --- a/components/gfx/text/glyph.rs +++ b/components/gfx/text/glyph.rs @@ -58,17 +58,7 @@ impl GlyphEntry { starts_ligature, glyph_count); - let mut val = FLAG_NOT_MISSING; - - if !starts_cluster { - val |= FLAG_NOT_CLUSTER_START; - } - if !starts_ligature { - val |= FLAG_NOT_LIGATURE_GROUP_START; - } - val |= (glyph_count as u32) << GLYPH_COUNT_SHIFT; - - GlyphEntry::new(val) + GlyphEntry::new(glyph_count as u32) } /// Create a GlyphEntry for the case where glyphs couldn't be found for the specified @@ -76,55 +66,22 @@ impl GlyphEntry { fn missing(glyph_count: usize) -> GlyphEntry { assert!(glyph_count <= u16::MAX as usize); - GlyphEntry::new((glyph_count as u32) << GLYPH_COUNT_SHIFT) + GlyphEntry::new(glyph_count as u32) } } /// The id of a particular glyph within a font pub type GlyphId = u32; -// TODO: unify with bit flags? -#[derive(PartialEq, Copy, Clone)] -pub enum BreakType { - None, - Normal, - Hyphen, -} - -static BREAK_TYPE_NONE: u8 = 0x0; -static BREAK_TYPE_NORMAL: u8 = 0x1; -static BREAK_TYPE_HYPHEN: u8 = 0x2; - -fn break_flag_to_enum(flag: u8) -> BreakType { - if (flag & BREAK_TYPE_NORMAL) != 0 { - BreakType::Normal - } else if (flag & BREAK_TYPE_HYPHEN) != 0 { - BreakType::Hyphen - } else { - BreakType::None - } -} - -fn break_enum_to_flag(e: BreakType) -> u8 { - match e { - BreakType::None => BREAK_TYPE_NONE, - BreakType::Normal => BREAK_TYPE_NORMAL, - BreakType::Hyphen => BREAK_TYPE_HYPHEN, - } -} - // TODO: make this more type-safe. -static FLAG_CHAR_IS_SPACE: u32 = 0x10000000; -// These two bits store some BREAK_TYPE_* flags -static FLAG_CAN_BREAK_MASK: u32 = 0x60000000; -static FLAG_CAN_BREAK_SHIFT: u32 = 29; -static FLAG_IS_SIMPLE_GLYPH: u32 = 0x80000000; +const FLAG_CHAR_IS_SPACE: u32 = 0x40000000; +const FLAG_IS_SIMPLE_GLYPH: u32 = 0x80000000; // glyph advance; in Au's. -static GLYPH_ADVANCE_MASK: u32 = 0x0FFF0000; -static GLYPH_ADVANCE_SHIFT: u32 = 16; -static GLYPH_ID_MASK: u32 = 0x0000FFFF; +const GLYPH_ADVANCE_MASK: u32 = 0x3FFF0000; +const GLYPH_ADVANCE_SHIFT: u32 = 16; +const GLYPH_ID_MASK: u32 = 0x0000FFFF; // Non-simple glyphs (more than one glyph per char; missing glyph, // newline, tab, large advance, or nonzero x/y offsets) may have one @@ -132,21 +89,8 @@ static GLYPH_ID_MASK: u32 = 0x0000FFFF; // side array so that there is a 1:1 mapping of GlyphEntry to // unicode char. -// The number of detailed glyphs for this char. If the char couldn't -// be mapped to a glyph (!FLAG_NOT_MISSING), then this actually holds -// the UTF8 code point instead. -static GLYPH_COUNT_MASK: u32 = 0x00FFFF00; -static GLYPH_COUNT_SHIFT: u32 = 8; -// N.B. following Gecko, these are all inverted so that a lot of -// missing chars can be memset with zeros in one fell swoop. -static FLAG_NOT_MISSING: u32 = 0x00000001; -static FLAG_NOT_CLUSTER_START: u32 = 0x00000002; -static FLAG_NOT_LIGATURE_GROUP_START: u32 = 0x00000004; - -static FLAG_CHAR_IS_TAB: u32 = 0x00000008; -static FLAG_CHAR_IS_NEWLINE: u32 = 0x00000010; -//static FLAG_CHAR_IS_LOW_SURROGATE: u32 = 0x00000020; -//static CHAR_IDENTITY_FLAGS_MASK: u32 = 0x00000038; +// The number of detailed glyphs for this char. +const GLYPH_COUNT_MASK: u32 = 0x0000FFFF; fn is_simple_glyph_id(id: GlyphId) -> bool { ((id as u32) & GLYPH_ID_MASK) == id @@ -164,7 +108,6 @@ type DetailedGlyphCount = u16; // Getters and setters for GlyphEntry. Setter methods are functional, // because GlyphEntry is immutable and only a u32 in size. impl GlyphEntry { - // getter methods #[inline(always)] fn advance(&self) -> Au { Au(((self.value & GLYPH_ADVANCE_MASK) >> GLYPH_ADVANCE_SHIFT) as i32) @@ -174,62 +117,20 @@ impl GlyphEntry { self.value & GLYPH_ID_MASK } - fn is_ligature_start(&self) -> bool { - self.has_flag(!FLAG_NOT_LIGATURE_GROUP_START) - } - - fn is_cluster_start(&self) -> bool { - self.has_flag(!FLAG_NOT_CLUSTER_START) - } - - // True if original char was normal (U+0020) space. Other chars may - // map to space glyph, but this does not account for them. + /// True if original char was normal (U+0020) space. Other chars may + /// map to space glyph, but this does not account for them. fn char_is_space(&self) -> bool { self.has_flag(FLAG_CHAR_IS_SPACE) } - fn char_is_tab(&self) -> bool { - !self.is_simple() && self.has_flag(FLAG_CHAR_IS_TAB) - } - - fn char_is_newline(&self) -> bool { - !self.is_simple() && self.has_flag(FLAG_CHAR_IS_NEWLINE) - } - - fn can_break_before(&self) -> BreakType { - let flag = ((self.value & FLAG_CAN_BREAK_MASK) >> FLAG_CAN_BREAK_SHIFT) as u8; - break_flag_to_enum(flag) - } - - // setter methods #[inline(always)] fn set_char_is_space(&self) -> GlyphEntry { GlyphEntry::new(self.value | FLAG_CHAR_IS_SPACE) } - #[inline(always)] - fn set_char_is_tab(&self) -> GlyphEntry { - assert!(!self.is_simple()); - GlyphEntry::new(self.value | FLAG_CHAR_IS_TAB) - } - - #[inline(always)] - fn set_char_is_newline(&self) -> GlyphEntry { - assert!(!self.is_simple()); - GlyphEntry::new(self.value | FLAG_CHAR_IS_NEWLINE) - } - - #[inline(always)] - fn set_can_break_before(&self, e: BreakType) -> GlyphEntry { - let flag = (break_enum_to_flag(e) as u32) << FLAG_CAN_BREAK_SHIFT; - GlyphEntry::new(self.value | flag) - } - - // helper methods - fn glyph_count(&self) -> u16 { assert!(!self.is_simple()); - ((self.value & GLYPH_COUNT_MASK) >> GLYPH_COUNT_SHIFT) as u16 + (self.value & GLYPH_COUNT_MASK) as u16 } #[inline(always)] @@ -576,9 +477,6 @@ impl<'a> GlyphStore { } }; - // FIXME(pcwalton): Is this necessary? I think it's a no-op. - entry = entry.adapt_character_flags_of_entry(self.entry_buffer[i.to_usize()]); - if character == Some(' ') { entry = entry.set_char_is_space() } @@ -647,62 +545,11 @@ impl<'a> GlyphStore { .fold(Au(0), |advance, (_, glyph)| advance + glyph.advance()) } - // getter methods pub fn char_is_space(&self, i: CharIndex) -> bool { assert!(i < self.char_len()); self.entry_buffer[i.to_usize()].char_is_space() } - pub fn char_is_tab(&self, i: CharIndex) -> bool { - assert!(i < self.char_len()); - self.entry_buffer[i.to_usize()].char_is_tab() - } - - pub fn char_is_newline(&self, i: CharIndex) -> bool { - assert!(i < self.char_len()); - self.entry_buffer[i.to_usize()].char_is_newline() - } - - pub fn is_ligature_start(&self, i: CharIndex) -> bool { - assert!(i < self.char_len()); - self.entry_buffer[i.to_usize()].is_ligature_start() - } - - pub fn is_cluster_start(&self, i: CharIndex) -> bool { - assert!(i < self.char_len()); - self.entry_buffer[i.to_usize()].is_cluster_start() - } - - pub fn can_break_before(&self, i: CharIndex) -> BreakType { - assert!(i < self.char_len()); - self.entry_buffer[i.to_usize()].can_break_before() - } - - // setter methods - pub fn set_char_is_space(&mut self, i: CharIndex) { - assert!(i < self.char_len()); - let entry = self.entry_buffer[i.to_usize()]; - self.entry_buffer[i.to_usize()] = entry.set_char_is_space(); - } - - pub fn set_char_is_tab(&mut self, i: CharIndex) { - assert!(i < self.char_len()); - let entry = self.entry_buffer[i.to_usize()]; - self.entry_buffer[i.to_usize()] = entry.set_char_is_tab(); - } - - pub fn set_char_is_newline(&mut self, i: CharIndex) { - assert!(i < self.char_len()); - let entry = self.entry_buffer[i.to_usize()]; - self.entry_buffer[i.to_usize()] = entry.set_char_is_newline(); - } - - pub fn set_can_break_before(&mut self, i: CharIndex, t: BreakType) { - assert!(i < self.char_len()); - let entry = self.entry_buffer[i.to_usize()]; - self.entry_buffer[i.to_usize()] = entry.set_can_break_before(t); - } - pub fn space_count_in_range(&self, range: &Range<CharIndex>) -> u32 { let mut spaces = 0; for index in range.each_index() { diff --git a/components/gfx/text/shaping/harfbuzz.rs b/components/gfx/text/shaping/harfbuzz.rs index 462d5bf485c..426068a4106 100644 --- a/components/gfx/text/shaping/harfbuzz.rs +++ b/components/gfx/text/shaping/harfbuzz.rs @@ -576,13 +576,13 @@ extern fn glyph_h_advance_func(_: *mut hb_font_t, fn glyph_space_advance(font: *mut Font) -> f64 { let space_unicode = ' '; let space_glyph: hb_codepoint_t; - match unsafe {(*font).glyph_index(space_unicode)} { + match unsafe { (*font).glyph_index(space_unicode) } { Some(g) => { space_glyph = g as hb_codepoint_t; } None => panic!("No space info") } - let space_advance = unsafe {(*font).glyph_h_advance(space_glyph as GlyphId)}; + let space_advance = unsafe { (*font).glyph_h_advance(space_glyph as GlyphId) }; space_advance } diff --git a/components/layout/block.rs b/components/layout/block.rs index d697cad97a9..3926384379a 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -36,10 +36,10 @@ use flow::{CLEARS_LEFT, CLEARS_RIGHT}; use flow::{HAS_LEFT_FLOATED_DESCENDANTS, HAS_RIGHT_FLOATED_DESCENDANTS}; use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS, INLINE_POSITION_IS_STATIC}; use flow::{IS_ABSOLUTELY_POSITIONED}; -use flow::{ImmutableFlowUtils, MutableFlowUtils, OpaqueFlow, PreorderFlowTraversal}; +use flow::{ImmutableFlowUtils, LateAbsolutePositionInfo, MutableFlowUtils, OpaqueFlow}; use flow::{LAYERS_NEEDED_FOR_DESCENDANTS, NEEDS_LAYER}; -use flow::{PostorderFlowTraversal, mut_base}; -use flow::{self, AbsolutePositionInfo, BaseFlow, ForceNonfloatedFlag, FlowClass, Flow}; +use flow::{PostorderFlowTraversal, PreorderFlowTraversal, mut_base}; +use flow::{self, BaseFlow, EarlyAbsolutePositionInfo, ForceNonfloatedFlag, FlowClass, Flow}; use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, HAS_LAYER}; use fragment::{SpecificFragmentInfo}; use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; @@ -485,7 +485,7 @@ impl<'a> PostorderFlowTraversal for AbsoluteStoreOverflowTraversal<'a> { } } - flow.store_overflow(self.layout_context); + flow.early_store_overflow(self.layout_context); } } @@ -561,7 +561,7 @@ pub struct BlockFlow { bitflags! { flags BlockFlowFlags: u8 { - #[doc="If this is set, then this block flow is the root flow."] + #[doc = "If this is set, then this block flow is the root flow."] const IS_ROOT = 0x01, } } @@ -959,6 +959,19 @@ impl BlockFlow { // // FIXME(pcwalton): This looks not idempotent. Is it? self.fragment.border_box.size.block = block_size; + } + + // Write in the size of the relative containing block for children. (This information + // is also needed to handle RTL.) + for kid in self.base.child_iter() { + flow::mut_base(kid).early_absolute_position_info = EarlyAbsolutePositionInfo { + relative_containing_block_size: self.fragment.content_box().size, + relative_containing_block_mode: self.fragment.style().writing_mode, + }; + kid.late_store_overflow(layout_context) + } + + if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { return } @@ -1730,7 +1743,7 @@ impl Flow for BlockFlow { self.base.thread_id = parent_thread_id; if self.base.restyle_damage.intersects(REFLOW_OUT_OF_FLOW | REFLOW) { self.assign_block_size(layout_context); - self.store_overflow(layout_context); + (self as &mut Flow).early_store_overflow(layout_context); // Don't remove the restyle damage; `assign_block_size` decides whether that is // appropriate (which in the case of e.g. absolutely-positioned flows, it is not). } @@ -1775,7 +1788,7 @@ impl Flow for BlockFlow { fn compute_absolute_position(&mut self, layout_context: &LayoutContext) { if (self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) && - self.base.absolute_position_info.layers_needed_for_positioned_flows) || + self.base.late_absolute_position_info.layers_needed_for_positioned_flows) || self.base.flags.contains(NEEDS_LAYER) { self.fragment.flags.insert(HAS_LAYER) } @@ -1816,7 +1829,7 @@ impl Flow for BlockFlow { // Absolute position of the containing block + position of absolute // flow w.r.t. the containing block. self.base - .absolute_position_info + .late_absolute_position_info .stacking_relative_position_of_absolute_containing_block + position_start }; @@ -1842,13 +1855,13 @@ impl Flow for BlockFlow { // other hand, is only established if we are positioned. let relative_offset = self.fragment.relative_position(&self.base - .absolute_position_info + .early_absolute_position_info .relative_containing_block_size); if self.contains_positioned_fragments() { let border_box_origin = (self.fragment.border_box - self.fragment.style.logical_border_width()).start; self.base - .absolute_position_info + .late_absolute_position_info .stacking_relative_position_of_absolute_containing_block = self.base.stacking_relative_position + (border_box_origin + relative_offset).to_physical(self.base.writing_mode, @@ -1875,14 +1888,12 @@ impl Flow for BlockFlow { } } else { self.base - .absolute_position_info + .late_absolute_position_info .stacking_relative_position_of_absolute_containing_block }; - let absolute_position_info_for_children = AbsolutePositionInfo { + let late_absolute_position_info_for_children = LateAbsolutePositionInfo { stacking_relative_position_of_absolute_containing_block: stacking_relative_position_of_absolute_containing_block_for_children, - relative_containing_block_size: self.fragment.content_box().size, - relative_containing_block_mode: self.base.writing_mode, layers_needed_for_positioned_flows: self.base .flags .contains(LAYERS_NEEDED_FOR_DESCENDANTS), @@ -1934,10 +1945,10 @@ impl Flow for BlockFlow { self.fragment .stacking_relative_border_box(&self.base.stacking_relative_position, &self.base - .absolute_position_info + .early_absolute_position_info .relative_containing_block_size, self.base - .absolute_position_info + .early_absolute_position_info .relative_containing_block_mode, CoordinateSystem::Own); let clip = self.fragment.clipping_region_for_children( @@ -1985,7 +1996,8 @@ impl Flow for BlockFlow { } } - flow::mut_base(kid).absolute_position_info = absolute_position_info_for_children; + flow::mut_base(kid).late_absolute_position_info = + late_absolute_position_info_for_children; flow::mut_base(kid).clip = clip.clone(); flow::mut_base(kid).stacking_relative_position_of_display_port = stacking_relative_position_of_display_port_for_children; @@ -2056,7 +2068,9 @@ impl Flow for BlockFlow { } fn compute_overflow(&self) -> Rect<Au> { - self.fragment.compute_overflow() + self.fragment.compute_overflow(&self.base + .early_absolute_position_info + .relative_containing_block_size) } fn iterate_through_fragment_border_boxes(&self, @@ -2072,10 +2086,10 @@ impl Flow for BlockFlow { &self.fragment .stacking_relative_border_box(&self.base.stacking_relative_position, &self.base - .absolute_position_info + .early_absolute_position_info .relative_containing_block_size, self.base - .absolute_position_info + .early_absolute_position_info .relative_containing_block_mode, CoordinateSystem::Own) .translate(stacking_context_position)); diff --git a/components/layout/data.rs b/components/layout/data.rs index 4b551e2fd9e..097852096fa 100644 --- a/components/layout/data.rs +++ b/components/layout/data.rs @@ -54,7 +54,7 @@ impl PrivateLayoutData { bitflags! { flags LayoutDataFlags: u8 { - #[doc="Whether a flow has been newly constructed."] + #[doc = "Whether a flow has been newly constructed."] const HAS_NEWLY_CONSTRUCTED_FLOW = 0x01 } } diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 0485d549c4a..19c466ee528 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -1152,9 +1152,9 @@ impl FragmentDisplayListBuilding for Fragment { StackingContextCreationMode::Normal | StackingContextCreationMode::OuterScrollWrapper => { self.stacking_relative_border_box(&base_flow.stacking_relative_position, - &base_flow.absolute_position_info + &base_flow.early_absolute_position_info .relative_containing_block_size, - base_flow.absolute_position_info + base_flow.early_absolute_position_info .relative_containing_block_mode, CoordinateSystem::Parent) } @@ -1533,8 +1533,12 @@ impl BlockFlowDisplayListBuilding for BlockFlow { .build_display_list(display_list, layout_context, &self.base.stacking_relative_position, - &self.base.absolute_position_info.relative_containing_block_size, - self.base.absolute_position_info.relative_containing_block_mode, + &self.base + .early_absolute_position_info + .relative_containing_block_size, + self.base + .early_absolute_position_info + .relative_containing_block_mode, border_painting_mode, background_border_level, &clip, @@ -1616,8 +1620,8 @@ impl BlockFlowDisplayListBuilding for BlockFlow { &mut outer_display_list_for_overflow_scroll, layout_context, &self.base.stacking_relative_position, - &self.base.absolute_position_info.relative_containing_block_size, - self.base.absolute_position_info.relative_containing_block_mode, + &self.base.early_absolute_position_info.relative_containing_block_size, + self.base.early_absolute_position_info.relative_containing_block_mode, border_painting_mode, BackgroundAndBorderLevel::RootOfStackingContext, &clip, @@ -1775,10 +1779,10 @@ impl InlineFlowDisplayListBuilding for InlineFlow { layout_context, &self.base.stacking_relative_position, &self.base - .absolute_position_info + .early_absolute_position_info .relative_containing_block_size, self.base - .absolute_position_info + .early_absolute_position_info .relative_containing_block_mode, BorderPaintingMode::Separate, BackgroundAndBorderLevel::Content, @@ -1857,11 +1861,11 @@ impl ListItemFlowDisplayListBuilding for ListItemFlow { &self.block_flow.base.stacking_relative_position, &self.block_flow .base - .absolute_position_info + .early_absolute_position_info .relative_containing_block_size, self.block_flow .base - .absolute_position_info + .early_absolute_position_info .relative_containing_block_mode, BorderPaintingMode::Separate, BackgroundAndBorderLevel::Content, diff --git a/components/layout/flex.rs b/components/layout/flex.rs index 3c34aeaf728..88fe929a9d3 100644 --- a/components/layout/flex.rs +++ b/components/layout/flex.rs @@ -312,6 +312,10 @@ impl Flow for FlexFlow { &mut self.block_flow } + fn mark_as_root(&mut self) { + self.block_flow.mark_as_root(); + } + fn bubble_inline_sizes(&mut self) { let _scope = layout_debug_scope!("flex::bubble_inline_sizes {:x}", self.block_flow.base.debug_id()); diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 6d319bdd991..4e2da89f513 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -222,7 +222,11 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static { if impacted { mut_base(self).thread_id = parent_thread_id; self.assign_block_size(layout_context); - self.store_overflow(layout_context); + // FIXME(pcwalton): Should use `early_store_overflow()` here but that fails due to a + // compiler bug (`Self` does not have a constant size). + if !self.contains_relatively_positioned_fragments() { + self.store_overflow(layout_context) + } mut_base(self).restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW); } impacted @@ -246,7 +250,7 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static { match self.class() { FlowClass::Block | FlowClass::TableCaption | - FlowClass::TableCell if !base(self).children.is_empty() => { + FlowClass::TableCell => { // FIXME(#2795): Get the real container size. let container_size = Size2D::zero(); for kid in mut_base(self).children.iter_mut() { @@ -310,13 +314,6 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static { // different behaviour for different types of Flow, so they can't go into // the Immutable / Mutable Flow Utils traits without additional casts. - /// Return true if store overflow is delayed for this flow. - /// - /// Currently happens only for absolutely positioned flows. - fn is_store_overflow_delayed(&mut self) -> bool { - false - } - fn is_root(&self) -> bool { false } @@ -459,6 +456,11 @@ pub trait ImmutableFlowUtils { /// Returns true if this flow is an inline flow. fn is_inline_flow(self) -> bool; + /// Returns true if this flow can have its overflow area calculated early (during its + /// block-size assignment) or false if it must have its overflow area calculated late (during + /// its parent's block-size assignment). + fn can_calculate_overflow_area_early(self) -> bool; + /// Dumps the flow tree for debugging. fn dump(self); @@ -495,6 +497,12 @@ pub trait MutableFlowUtils { /// Calls `repair_style` and `bubble_inline_sizes`. You should use this method instead of /// calling them individually, since there is no reason not to perform both operations. fn repair_style_and_bubble_inline_sizes(self, style: &Arc<ComputedValues>); + + /// Calls `store_overflow()` if the overflow can be calculated early. + fn early_store_overflow(self, layout_context: &LayoutContext); + + /// Calls `store_overflow()` if the overflow cannot be calculated early. + fn late_store_overflow(self, layout_context: &LayoutContext); } pub trait MutableOwnedFlowUtils { @@ -780,15 +788,30 @@ impl<'a> Iterator for AbsoluteDescendantIter<'a> { pub type AbsoluteDescendantOffsetIter<'a> = Zip<AbsoluteDescendantIter<'a>, IterMut<'a, Au>>; /// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be -/// confused with absolutely-positioned flows). -#[derive(RustcEncodable, Copy, Clone)] -pub struct AbsolutePositionInfo { +/// confused with absolutely-positioned flows) that is computed during block-size assignment. +pub struct EarlyAbsolutePositionInfo { /// The size of the containing block for relatively-positioned descendants. pub relative_containing_block_size: LogicalSize<Au>, /// The writing mode for `relative_containing_block_size`. pub relative_containing_block_mode: WritingMode, +} + +impl EarlyAbsolutePositionInfo { + pub fn new(writing_mode: WritingMode) -> EarlyAbsolutePositionInfo { + // FIXME(pcwalton): The initial relative containing block-size should be equal to the size + // of the root layer. + EarlyAbsolutePositionInfo { + relative_containing_block_size: LogicalSize::zero(writing_mode), + relative_containing_block_mode: writing_mode, + } + } +} +/// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be +/// confused with absolutely-positioned flows) that is computed during final position assignment. +#[derive(RustcEncodable, Copy, Clone)] +pub struct LateAbsolutePositionInfo { /// The position of the absolute containing block relative to the nearest ancestor stacking /// context. If the absolute containing block establishes the stacking context for this flow, /// and this flow is not itself absolutely-positioned, then this is (0, 0). @@ -800,13 +823,9 @@ pub struct AbsolutePositionInfo { pub layers_needed_for_positioned_flows: bool, } -impl AbsolutePositionInfo { - pub fn new(writing_mode: WritingMode) -> AbsolutePositionInfo { - // FIXME(pcwalton): The initial relative containing block-size should be equal to the size - // of the root layer. - AbsolutePositionInfo { - relative_containing_block_size: LogicalSize::zero(writing_mode), - relative_containing_block_mode: writing_mode, +impl LateAbsolutePositionInfo { + pub fn new() -> LateAbsolutePositionInfo { + LateAbsolutePositionInfo { stacking_relative_position_of_absolute_containing_block: Point2D::zero(), layers_needed_for_positioned_flows: false, } @@ -875,8 +894,13 @@ pub struct BaseFlow { pub absolute_cb: ContainingBlockLink, /// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be - /// confused with absolutely-positioned flows). - pub absolute_position_info: AbsolutePositionInfo, + /// confused with absolutely-positioned flows) that is computed during block-size assignment. + pub early_absolute_position_info: EarlyAbsolutePositionInfo, + + /// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be + /// confused with absolutely-positioned flows) that is computed during final position + /// assignment. + pub late_absolute_position_info: LateAbsolutePositionInfo, /// The clipping region for this flow and its descendants, in layer coordinates. pub clip: ClippingRegion, @@ -1038,7 +1062,8 @@ impl BaseFlow { block_container_explicit_block_size: None, absolute_cb: ContainingBlockLink::new(), display_list_building_result: DisplayListBuildingResult::None, - absolute_position_info: AbsolutePositionInfo::new(writing_mode), + early_absolute_position_info: EarlyAbsolutePositionInfo::new(writing_mode), + late_absolute_position_info: LateAbsolutePositionInfo::new(), clip: ClippingRegion::max(), stacking_relative_position_of_display_port: Rect::zero(), flags: flags, @@ -1276,6 +1301,13 @@ impl<'a> ImmutableFlowUtils for &'a Flow { } } + /// Returns true if this flow can have its overflow area calculated early (during its + /// block-size assignment) or false if it must have its overflow area calculated late (during + /// its parent's block-size assignment). + fn can_calculate_overflow_area_early(self) -> bool { + !self.contains_relatively_positioned_fragments() + } + /// Dumps the flow tree for debugging. fn dump(self) { self.dump_with_level(0) @@ -1354,6 +1386,20 @@ impl<'a> MutableFlowUtils for &'a mut Flow { traversal.process(*self) } + + /// Calls `store_overflow()` if the overflow can be calculated early. + fn early_store_overflow(self, layout_context: &LayoutContext) { + if self.can_calculate_overflow_area_early() { + self.store_overflow(layout_context) + } + } + + /// Calls `store_overflow()` if the overflow cannot be calculated early. + fn late_store_overflow(self, layout_context: &LayoutContext) { + if !self.can_calculate_overflow_area_early() { + self.store_overflow(layout_context) + } + } } impl MutableOwnedFlowUtils for FlowRef { diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 02af5569d84..3b9596d27b9 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -2049,7 +2049,7 @@ impl Fragment { } /// Computes the overflow rect of this fragment relative to the start of the flow. - pub fn compute_overflow(&self) -> Rect<Au> { + pub fn compute_overflow(&self, relative_containing_block_size: &LogicalSize<Au>) -> Rect<Au> { // FIXME(pcwalton, #2795): Get the real container size. let container_size = Size2D::zero(); let mut border_box = self.border_box.to_physical(self.style.writing_mode, container_size); @@ -2058,10 +2058,9 @@ impl Fragment { // // FIXME(pcwalton): I'm not a fan of the way this makes us crawl though so many styles all // the time. Can't we handle relative positioning by just adjusting `border_box`? - let relative_position = - self.relative_position(&LogicalSize::zero(self.style.writing_mode)); - border_box = border_box.translate_by_size(&relative_position.to_physical( - self.style.writing_mode)); + let relative_position = self.relative_position(relative_containing_block_size); + border_box = + border_box.translate_by_size(&relative_position.to_physical(self.style.writing_mode)); let mut overflow = border_box; // Box shadows cause us to draw outside our border box. @@ -2300,10 +2299,10 @@ bitflags! { // Various flags we can use when splitting fragments. See // `calculate_split_position_using_breaking_strategy()`. flags SplitOptions: u8 { - #[doc="True if this is the first fragment on the line."] + #[doc = "True if this is the first fragment on the line."] const STARTS_LINE = 0x01, - #[doc="True if we should attempt to split at character boundaries if this split fails. \ - This is used to implement `overflow-wrap: break-word`."] + #[doc = "True if we should attempt to split at character boundaries if this split fails. \ + This is used to implement `overflow-wrap: break-word`."] const RETRY_AT_CHARACTER_BOUNDARIES = 0x02, } } diff --git a/components/layout/incremental.rs b/components/layout/incremental.rs index 623fa4685fe..14205b32cb5 100644 --- a/components/layout/incremental.rs +++ b/components/layout/incremental.rs @@ -42,8 +42,8 @@ bitflags! { bitflags! { flags SpecialRestyleDamage: u8 { - #[doc="If this flag is set, we need to reflow the entire document. This is more or less a \ - temporary hack to deal with cases that we don't handle incrementally yet."] + #[doc = "If this flag is set, we need to reflow the entire document. This is more or less a \ + temporary hack to deal with cases that we don't handle incrementally yet."] const REFLOW_ENTIRE_DOCUMENT = 0x01, } } diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 4b5a6b985c7..17d6af7ccbc 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -183,9 +183,9 @@ int_range_index! { bitflags! { flags InlineReflowFlags: u8 { - #[doc="The `white-space: nowrap` property from CSS 2.1 § 16.6 is in effect."] + #[doc = "The `white-space: nowrap` property from CSS 2.1 § 16.6 is in effect."] const NO_WRAP_INLINE_REFLOW_FLAG = 0x01, - #[doc="The `white-space: pre` property from CSS 2.1 § 16.6 is in effect."] + #[doc = "The `white-space: pre` property from CSS 2.1 § 16.6 is in effect."] const WRAP_ON_NEWLINE_INLINE_REFLOW_FLAG = 0x02 } } @@ -1645,10 +1645,10 @@ impl Flow for InlineFlow { let stacking_relative_border_box = fragment.stacking_relative_border_box(&self.base.stacking_relative_position, &self.base - .absolute_position_info + .early_absolute_position_info .relative_containing_block_size, self.base - .absolute_position_info + .early_absolute_position_info .relative_containing_block_mode, CoordinateSystem::Parent); let clip = fragment.clipping_region_for_children(&self.base.clip, @@ -1661,13 +1661,14 @@ impl Flow for InlineFlow { flow::mut_base(flow).clip = clip; let block_flow = flow.as_mut_block(); - block_flow.base.absolute_position_info = self.base.absolute_position_info; + block_flow.base.late_absolute_position_info = + self.base.late_absolute_position_info; let stacking_relative_position = self.base.stacking_relative_position; if is_positioned { let padding_box_origin = containing_block_positions.next().unwrap(); block_flow.base - .absolute_position_info + .late_absolute_position_info .stacking_relative_position_of_absolute_containing_block = stacking_relative_position + *padding_box_origin; } @@ -1681,7 +1682,8 @@ impl Flow for InlineFlow { let flow = flow_ref::deref_mut(&mut info.flow_ref); flow::mut_base(flow).clip = clip; let block_flow = flow.as_mut_block(); - block_flow.base.absolute_position_info = self.base.absolute_position_info; + block_flow.base.late_absolute_position_info = + self.base.late_absolute_position_info; block_flow.base.stacking_relative_position = stacking_relative_border_box.origin; @@ -1693,12 +1695,13 @@ impl Flow for InlineFlow { flow::mut_base(flow).clip = clip; let block_flow = flow.as_mut_block(); - block_flow.base.absolute_position_info = self.base.absolute_position_info; + block_flow.base.late_absolute_position_info = + self.base.late_absolute_position_info; let stacking_relative_position = self.base.stacking_relative_position; let padding_box_origin = containing_block_positions.next().unwrap(); block_flow.base - .absolute_position_info + .late_absolute_position_info .stacking_relative_position_of_absolute_containing_block = stacking_relative_position + *padding_box_origin; @@ -1725,7 +1728,8 @@ impl Flow for InlineFlow { fn compute_overflow(&self) -> Rect<Au> { let mut overflow = ZERO_RECT; for fragment in &self.fragments.fragments { - overflow = overflow.union(&fragment.compute_overflow()) + overflow = overflow.union(&fragment.compute_overflow( + &self.base.early_absolute_position_info.relative_containing_block_size)) } overflow } @@ -1742,9 +1746,9 @@ impl Flow for InlineFlow { let stacking_relative_position = &self.base.stacking_relative_position; let relative_containing_block_size = - &self.base.absolute_position_info.relative_containing_block_size; + &self.base.early_absolute_position_info.relative_containing_block_size; let relative_containing_block_mode = - self.base.absolute_position_info.relative_containing_block_mode; + self.base.early_absolute_position_info.relative_containing_block_mode; iterator.process(fragment, level, &fragment.stacking_relative_border_box(stacking_relative_position, diff --git a/components/layout/list_item.rs b/components/layout/list_item.rs index b0c3702f2c2..7ba0786f906 100644 --- a/components/layout/list_item.rs +++ b/components/layout/list_item.rs @@ -184,11 +184,11 @@ impl Flow for ListItemFlow { .stacking_relative_position, &self.block_flow .base - .absolute_position_info + .early_absolute_position_info .relative_containing_block_size, self.block_flow .base - .absolute_position_info + .early_absolute_position_info .relative_containing_block_mode, CoordinateSystem::Own) .translate(stacking_context_position)); diff --git a/components/layout/table.rs b/components/layout/table.rs index f5ef95f1989..54e3f090b59 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -10,8 +10,8 @@ use block::{ISizeConstraintInput, ISizeConstraintSolution}; use block::{self, BlockFlow, CandidateBSizeIterator, ISizeAndMarginsComputer}; use context::LayoutContext; use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode}; -use flow::{ImmutableFlowUtils, OpaqueFlow}; -use flow::{self, Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS}; +use flow::{IMPACTED_BY_RIGHT_FLOATS, ImmutableFlowUtils, MutableFlowUtils, OpaqueFlow}; +use flow::{self, EarlyAbsolutePositionInfo, Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS}; use fragment::{Fragment, FragmentBorderBoxIterator}; use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; use layout_debug; @@ -761,7 +761,7 @@ pub trait TableLikeFlow { impl TableLikeFlow for BlockFlow { fn assign_block_size_for_table_like_flow<'a>(&mut self, - _: &'a LayoutContext<'a>, + layout_context: &'a LayoutContext<'a>, block_direction_spacing: Au) { debug_assert!(self.fragment.style.get_inheritedtable().border_collapse == border_collapse::T::separate || block_direction_spacing == Au(0)); @@ -838,6 +838,16 @@ impl TableLikeFlow for BlockFlow { self.fragment.border_box.size.block = current_block_offset; self.fragment.border_box.start.b = Au(0); self.base.position.size.block = current_block_offset; + + // Write in the size of the relative containing block for children. (This information + // is also needed to handle RTL.) + for kid in self.base.child_iter() { + flow::mut_base(kid).early_absolute_position_info = EarlyAbsolutePositionInfo { + relative_containing_block_size: self.fragment.content_box().size, + relative_containing_block_mode: self.fragment.style().writing_mode, + }; + kid.late_store_overflow(layout_context) + } } self.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW); diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs index e2b155bca78..4043363a23a 100644 --- a/components/layout/table_row.rs +++ b/components/layout/table_row.rs @@ -9,7 +9,7 @@ use block::{BlockFlow, ISizeAndMarginsComputer}; use context::LayoutContext; use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode}; -use flow::{self, FlowClass, Flow, ImmutableFlowUtils, OpaqueFlow}; +use flow::{self, EarlyAbsolutePositionInfo, FlowClass, Flow, ImmutableFlowUtils, OpaqueFlow}; use flow_list::MutFlowListIterator; use fragment::{Fragment, FragmentBorderBoxIterator}; use layout_debug; @@ -162,7 +162,15 @@ impl TableRowFlow { } // Assign the child's block size. - child_table_cell.block_flow.base.position.size.block = block_size + child_table_cell.block_flow.base.position.size.block = block_size; + + // Write in the size of the relative containing block for children. (This information + // is also needed to handle RTL.) + child_table_cell.block_flow.base.early_absolute_position_info = + EarlyAbsolutePositionInfo { + relative_containing_block_size: self.block_flow.fragment.content_box().size, + relative_containing_block_mode: self.block_flow.fragment.style().writing_mode, + }; } } diff --git a/components/layout/table_wrapper.rs b/components/layout/table_wrapper.rs index c3312bceba9..ebf96020634 100644 --- a/components/layout/table_wrapper.rs +++ b/components/layout/table_wrapper.rs @@ -13,12 +13,12 @@ #![deny(unsafe_code)] -use block::{BlockFlow, FloatNonReplaced, ISizeAndMarginsComputer, ISizeConstraintInput}; +use block::{BlockFlow, FloatNonReplaced, AbsoluteNonReplaced, ISizeAndMarginsComputer, ISizeConstraintInput}; use block::{ISizeConstraintSolution, MarginsMayCollapseFlag}; use context::LayoutContext; use floats::FloatKind; use flow::{FlowClass, Flow, ImmutableFlowUtils}; -use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS, OpaqueFlow}; +use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS, INLINE_POSITION_IS_STATIC, OpaqueFlow}; use fragment::{Fragment, FragmentBorderBoxIterator}; use model::MaybeAuto; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize}; @@ -242,6 +242,26 @@ impl TableWrapperFlow { return } + if !self.block_flow.base.flags.contains(INLINE_POSITION_IS_STATIC) { + let inline_size_computer = AbsoluteTable { + minimum_width_of_all_columns: minimum_width_of_all_columns, + preferred_width_of_all_columns: preferred_width_of_all_columns, + border_collapse: border_collapse, + }; + let input = + inline_size_computer.compute_inline_size_constraint_inputs(&mut self.block_flow, + parent_flow_inline_size, + layout_context); + + let solution = inline_size_computer.solve_inline_size_constraints(&mut self.block_flow, + &input); + inline_size_computer.set_inline_size_constraint_solutions(&mut self.block_flow, + solution); + inline_size_computer.set_inline_position_of_flow_if_necessary(&mut self.block_flow, + solution); + return + } + let inline_size_computer = Table { minimum_width_of_all_columns: minimum_width_of_all_columns, preferred_width_of_all_columns: preferred_width_of_all_columns, @@ -791,3 +811,54 @@ impl ISizeAndMarginsComputer for FloatedTable { FloatNonReplaced.solve_inline_size_constraints(block, input) } } + +struct AbsoluteTable { + minimum_width_of_all_columns: Au, + preferred_width_of_all_columns: Au, + border_collapse: border_collapse::T, +} + +impl ISizeAndMarginsComputer for AbsoluteTable { + fn compute_border_and_padding(&self, block: &mut BlockFlow, containing_block_inline_size: Au) { + block.fragment.compute_border_and_padding(containing_block_inline_size, + self.border_collapse) + } + + fn initial_computed_inline_size(&self, + block: &mut BlockFlow, + parent_flow_inline_size: Au, + layout_context: &LayoutContext) + -> MaybeAuto { + let containing_block_inline_size = + self.containing_block_inline_size(block, + parent_flow_inline_size, + layout_context); + initial_computed_inline_size(block, + containing_block_inline_size, + self.minimum_width_of_all_columns, + self.preferred_width_of_all_columns) + } + + fn containing_block_inline_size(&self, + block: &mut BlockFlow, + parent_flow_inline_size: Au, + layout_context: &LayoutContext) + -> Au { + AbsoluteNonReplaced.containing_block_inline_size(block, parent_flow_inline_size, layout_context) + } + + fn solve_inline_size_constraints(&self, + block: &mut BlockFlow, + input: &ISizeConstraintInput) + -> ISizeConstraintSolution { + AbsoluteNonReplaced.solve_inline_size_constraints(block, input) + } + + fn set_inline_position_of_flow_if_necessary(&self, + block: &mut BlockFlow, + solution: ISizeConstraintSolution) { + AbsoluteNonReplaced.set_inline_position_of_flow_if_necessary(block, solution); + } + +} + diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index 7c4687a1b00..ed8ae6ab76a 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -7,7 +7,7 @@ use construct::FlowConstructor; use context::LayoutContext; use css::matching::{ApplicableDeclarations, MatchMethods, StyleSharingResult}; -use flow::{PreorderFlowTraversal, PostorderFlowTraversal}; +use flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal}; use flow::{self, Flow}; use incremental::{self, BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, RestyleDamage}; use script::layout_interface::ReflowGoal; @@ -368,7 +368,7 @@ impl<'a> PostorderFlowTraversal for AssignBSizesAndStoreOverflow<'a> { } flow.assign_block_size(self.layout_context); - flow.store_overflow(self.layout_context); + flow.early_store_overflow(self.layout_context); } #[inline] diff --git a/components/msg/lib.rs b/components/msg/lib.rs index 7c03b73d591..34a87cfd805 100644 --- a/components/msg/lib.rs +++ b/components/msg/lib.rs @@ -20,9 +20,9 @@ extern crate util; extern crate url; extern crate style; -#[cfg(target_os="macos")] +#[cfg(target_os = "macos")] extern crate core_foundation; -#[cfg(target_os="macos")] +#[cfg(target_os = "macos")] extern crate io_surface; pub mod compositor_msg; diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index bc47b6aa709..4c257513526 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -2,49 +2,47 @@ * 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 devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg, NetworkEvent}; -use hsts::{HSTSList, secure_url}; -use mime_classifier::MIMEClassifier; -use net_traits::ProgressMsg::{Payload, Done}; -use net_traits::hosts::replace_hosts; -use net_traits::{ControlMsg, CookieSource, LoadData, Metadata, LoadConsumer, IncludeSubdomains}; -use resource_task::{start_sending_opt, start_sending_sniffed_opt}; +use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg, NetworkEvent}; use file_loader; use flate2::read::{DeflateDecoder, GzDecoder}; +use hsts::secure_url; use hyper::Error as HttpError; -use hyper::client::Request; -use hyper::header::StrictTransportSecurity; -use hyper::header::{AcceptEncoding, Accept, ContentLength, ContentType, Host, Location, qitem, Quality, QualityItem}; +use hyper::client::{Request, Response}; +use hyper::header::{AcceptEncoding, Accept, ContentLength, ContentType, Host}; +use hyper::header::{Location, qitem, StrictTransportSecurity}; +use hyper::header::{Quality, QualityItem, Headers, ContentEncoding, Encoding}; +use hyper::http::RawStatus; use hyper::method::Method; use hyper::mime::{Mime, TopLevel, SubLevel}; -use hyper::net::{HttpConnector, HttpsConnector, Openssl}; +use hyper::net::{Fresh, HttpsConnector, Openssl}; use hyper::status::{StatusCode, StatusClass}; use ipc_channel::ipc::{self, IpcSender}; use log; +use mime_classifier::MIMEClassifier; +use net_traits::ProgressMsg::{Payload, Done}; +use net_traits::hosts::replace_hosts; +use net_traits::{ControlMsg, CookieSource, LoadData, Metadata, LoadConsumer, IncludeSubdomains}; use openssl::ssl::{SslContext, SslMethod, SSL_VERIFY_PEER}; +use resource_task::{start_sending_opt, start_sending_sniffed_opt}; +use std::borrow::ToOwned; +use std::boxed::FnBox; use std::collections::HashSet; use std::error::Error; use std::io::{self, Read, Write}; use std::sync::Arc; -use std::sync::Mutex; use std::sync::mpsc::{Sender, channel}; use url::{Url, UrlParser}; -use util::opts; use util::resource_files::resources_dir_path; use util::task::spawn_named; - -use std::borrow::ToOwned; -use std::boxed::FnBox; use uuid; pub fn factory(resource_mgr_chan: IpcSender<ControlMsg>, - devtools_chan: Option<Sender<DevtoolsControlMsg>>, - hsts_list: Arc<Mutex<HSTSList>>) + devtools_chan: Option<Sender<DevtoolsControlMsg>>) -> Box<FnBox(LoadData, LoadConsumer, Arc<MIMEClassifier>) + Send> { - box move |load_data, senders, classifier| { - spawn_named("http_loader".to_owned(), - move || load(load_data, senders, classifier, resource_mgr_chan, devtools_chan, hsts_list)) + box move |load_data: LoadData, senders, classifier| { + spawn_named(format!("http_loader for {}", load_data.url.serialize()), + move || load_for_consumer(load_data, senders, classifier, resource_mgr_chan, devtools_chan)) } } @@ -76,21 +74,365 @@ fn read_block<R: Read>(reader: &mut R) -> Result<ReadResult, ()> { } } -fn request_must_be_secured(hsts_list: &HSTSList, url: &Url) -> bool { - match url.domain() { - Some(ref h) => { - hsts_list.is_host_secure(h) - }, - _ => false +fn inner_url(url: &Url) -> Url { + let inner_url = url.non_relative_scheme_data().unwrap(); + Url::parse(inner_url).unwrap() +} + +fn load_for_consumer(load_data: LoadData, + start_chan: LoadConsumer, + classifier: Arc<MIMEClassifier>, + resource_mgr_chan: IpcSender<ControlMsg>, + devtools_chan: Option<Sender<DevtoolsControlMsg>>) { + + match load::<WrappedHttpRequest>(load_data, resource_mgr_chan, devtools_chan, &NetworkHttpRequestFactory) { + Err(LoadError::UnsupportedScheme(url)) => { + let s = format!("{} request, but we don't support that scheme", &*url.scheme); + send_error(url, s, start_chan) + } + Err(LoadError::Connection(url, e)) => { + send_error(url, e, start_chan) + } + Err(LoadError::MaxRedirects(url)) => { + send_error(url, "too many redirects".to_string(), start_chan) + } + Err(LoadError::Cors(url, msg)) | + Err(LoadError::InvalidRedirect(url, msg)) | + Err(LoadError::Decoding(url, msg)) => { + send_error(url, msg, start_chan) + } + Err(LoadError::Ssl(url, msg)) => { + info!("ssl validation error {}, '{}'", url.serialize(), msg); + + let mut image = resources_dir_path(); + image.push("badcert.html"); + let load_data = LoadData::new(Url::from_file_path(&*image).unwrap(), None); + + file_loader::factory(load_data, start_chan, classifier) + + } + Ok(mut load_response) => { + let metadata = load_response.metadata.clone(); + send_data(&mut load_response, start_chan, metadata, classifier) + } + } +} + +pub trait HttpResponse: Read { + fn headers(&self) -> &Headers; + fn status(&self) -> StatusCode; + fn status_raw(&self) -> &RawStatus; + + fn content_encoding(&self) -> Option<Encoding> { + self.headers().get::<ContentEncoding>().and_then(|h| { + match h { + &ContentEncoding(ref encodings) => { + if encodings.contains(&Encoding::Gzip) { + Some(Encoding::Gzip) + } else if encodings.contains(&Encoding::Deflate) { + Some(Encoding::Deflate) + } else { + // TODO: Is this the correct behaviour? + None + } + } + } + }) + } +} + +struct WrappedHttpResponse { + response: Response +} + +impl Read for WrappedHttpResponse { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.response.read(buf) + } +} + +impl HttpResponse for WrappedHttpResponse { + fn headers(&self) -> &Headers { + &self.response.headers + } + + fn status(&self) -> StatusCode { + self.response.status + } + + fn status_raw(&self) -> &RawStatus { + self.response.status_raw() + } +} + +pub trait HttpRequestFactory { + type R: HttpRequest; + + fn create(&self, url: Url, method: Method) -> Result<Self::R, LoadError>; +} + +struct NetworkHttpRequestFactory; + +impl HttpRequestFactory for NetworkHttpRequestFactory { + type R = WrappedHttpRequest; + + fn create(&self, url: Url, method: Method) -> Result<WrappedHttpRequest, LoadError> { + let mut context = SslContext::new(SslMethod::Sslv23).unwrap(); + context.set_verify(SSL_VERIFY_PEER, None); + context.set_CA_file(&resources_dir_path().join("certs")).unwrap(); + + let connector = HttpsConnector::new(Openssl { context: Arc::new(context) }); + let connection = Request::with_connector(method, url.clone(), &connector); + + let ssl_err_string = "Some(OpenSslErrors([UnknownError { library: \"SSL routines\", \ + function: \"SSL3_GET_SERVER_CERTIFICATE\", \ + reason: \"certificate verify failed\" }]))"; + + let request = match connection { + Ok(req) => req, + + Err(HttpError::Io(ref io_error)) if ( + io_error.kind() == io::ErrorKind::Other && + io_error.description() == "Error in OpenSSL" && + // FIXME: This incredibly hacky. Make it more robust, and at least test it. + format!("{:?}", io_error.cause()) == ssl_err_string + ) => { + return Err( + LoadError::Ssl( + url, + format!("ssl error {:?}: {:?} {:?}", + io_error.kind(), + io_error.description(), + io_error.cause()) + ) + ) + }, + Err(e) => { + return Err(LoadError::Connection(url, e.description().to_string())) + } + }; + + Ok(WrappedHttpRequest { request: request }) + } +} + +pub trait HttpRequest { + type R: HttpResponse + 'static; + + fn headers_mut(&mut self) -> &mut Headers; + fn send(self, body: &Option<Vec<u8>>) -> Result<Self::R, LoadError>; +} + +struct WrappedHttpRequest { + request: Request<Fresh> +} + +impl HttpRequest for WrappedHttpRequest { + type R = WrappedHttpResponse; + + fn headers_mut(&mut self) -> &mut Headers { + self.request.headers_mut() + } + + fn send(self, body: &Option<Vec<u8>>) -> Result<WrappedHttpResponse, LoadError> { + let url = self.request.url.clone(); + let mut request_writer = match self.request.start() { + Ok(streaming) => streaming, + Err(e) => return Err(LoadError::Connection(url, e.description().to_string())) + }; + + if let Some(ref data) = *body { + match request_writer.write_all(&data) { + Err(e) => { + return Err(LoadError::Connection(url, e.description().to_string())) + } + _ => {} + } + } + + let response = match request_writer.send() { + Ok(w) => w, + Err(e) => return Err(LoadError::Connection(url, e.description().to_string())) + }; + + Ok(WrappedHttpResponse { response: response }) + } +} + +#[derive(Debug)] +pub enum LoadError { + UnsupportedScheme(Url), + Connection(Url, String), + Cors(Url, String), + Ssl(Url, String), + InvalidRedirect(Url, String), + Decoding(Url, String), + MaxRedirects(Url) +} + +fn set_default_accept_encoding(headers: &mut Headers) { + if headers.has::<AcceptEncoding>() { + return + } + + headers.set_raw("Accept-Encoding".to_owned(), vec![b"gzip, deflate".to_vec()]); +} + +fn set_default_accept(headers: &mut Headers) { + if !headers.has::<Accept>() { + let accept = Accept(vec![ + qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])), + qitem(Mime(TopLevel::Application, SubLevel::Ext("xhtml+xml".to_string()), vec![])), + QualityItem::new(Mime(TopLevel::Application, SubLevel::Xml, vec![]), Quality(900u16)), + QualityItem::new(Mime(TopLevel::Star, SubLevel::Star, vec![]), Quality(800u16)), + ]); + headers.set(accept); + } +} + +fn set_request_cookies(url: Url, headers: &mut Headers, resource_mgr_chan: &IpcSender<ControlMsg>) { + let (tx, rx) = ipc::channel().unwrap(); + resource_mgr_chan.send(ControlMsg::GetCookiesForUrl(url, tx, CookieSource::HTTP)).unwrap(); + if let Some(cookie_list) = rx.recv().unwrap() { + let mut v = Vec::new(); + v.push(cookie_list.into_bytes()); + headers.set_raw("Cookie".to_owned(), v); + } +} + +fn set_cookies_from_response(url: Url, response: &HttpResponse, resource_mgr_chan: &IpcSender<ControlMsg>) { + if let Some(cookies) = response.headers().get_raw("set-cookie") { + for cookie in cookies.iter() { + if let Ok(cookies) = String::from_utf8(cookie.clone()) { + resource_mgr_chan.send(ControlMsg::SetCookiesForUrl(url.clone(), + cookies, + CookieSource::HTTP)).unwrap(); + } + } + } +} + +fn request_must_be_secured(url: &Url, resource_mgr_chan: &IpcSender<ControlMsg>) -> bool { + let (tx, rx) = ipc::channel().unwrap(); + resource_mgr_chan.send( + ControlMsg::GetHostMustBeSecured(url.domain().unwrap().to_string(), tx) + ).unwrap(); + + rx.recv().unwrap() +} + +fn update_sts_list_from_response(url: &Url, response: &HttpResponse, resource_mgr_chan: &IpcSender<ControlMsg>) { + if url.scheme != "https" { + return; + } + + if let Some(header) = response.headers().get::<StrictTransportSecurity>() { + if let Some(host) = url.domain() { + info!("adding host {} to the strict transport security list", host); + info!("- max-age {}", header.max_age); + + let include_subdomains = if header.include_subdomains { + info!("- includeSubdomains"); + IncludeSubdomains::Included + } else { + IncludeSubdomains::NotIncluded + }; + + let msg = ControlMsg::SetHSTSEntryForHost( + host.to_string(), + include_subdomains, + header.max_age + ); + + resource_mgr_chan.send(msg).unwrap(); + } + } +} + +pub struct StreamedResponse<R: HttpResponse> { + decoder: Decoder<R>, + pub metadata: Metadata +} + + +impl<R: HttpResponse> Read for StreamedResponse<R> { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + match self.decoder { + Decoder::Gzip(ref mut d) => d.read(buf), + Decoder::Deflate(ref mut d) => d.read(buf), + Decoder::Plain(ref mut d) => d.read(buf) + } + } +} + +impl<R: HttpResponse> StreamedResponse<R> { + fn new(m: Metadata, d: Decoder<R>) -> StreamedResponse<R> { + StreamedResponse { metadata: m, decoder: d } + } + + fn from_http_response(response: R, m: Metadata) -> Result<StreamedResponse<R>, LoadError> { + match response.content_encoding() { + Some(Encoding::Gzip) => { + let result = GzDecoder::new(response); + match result { + Ok(response_decoding) => { + return Ok(StreamedResponse::new(m, Decoder::Gzip(response_decoding))); + } + Err(err) => { + return Err(LoadError::Decoding(m.final_url, err.to_string())); + } + } + } + Some(Encoding::Deflate) => { + let response_decoding = DeflateDecoder::new(response); + return Ok(StreamedResponse::new(m, Decoder::Deflate(response_decoding))); + } + _ => { + return Ok(StreamedResponse::new(m, Decoder::Plain(response))); + } + } } } -fn load(mut load_data: LoadData, - start_chan: LoadConsumer, - classifier: Arc<MIMEClassifier>, - resource_mgr_chan: IpcSender<ControlMsg>, - devtools_chan: Option<Sender<DevtoolsControlMsg>>, - hsts_list: Arc<Mutex<HSTSList>>) { +enum Decoder<R: Read> { + Gzip(GzDecoder<R>), + Deflate(DeflateDecoder<R>), + Plain(R) +} + +fn send_request_to_devtools(devtools_chan: Option<Sender<DevtoolsControlMsg>>, + request_id: String, + url: Url, + method: Method, + headers: Headers, + body: Option<Vec<u8>>) { + + if let Some(ref chan) = devtools_chan { + let net_event = NetworkEvent::HttpRequest(url, method, headers, body); + + let msg = ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event); + chan.send(DevtoolsControlMsg::FromChrome(msg)).unwrap(); + } +} + +fn send_response_to_devtools(devtools_chan: Option<Sender<DevtoolsControlMsg>>, + request_id: String, + headers: Option<Headers>, + status: Option<RawStatus>) { + if let Some(ref chan) = devtools_chan { + let net_event_response = NetworkEvent::HttpResponse(headers, status, None); + + let msg = ChromeToDevtoolsControlMsg::NetworkEvent(request_id, net_event_response); + chan.send(DevtoolsControlMsg::FromChrome(msg)).unwrap(); + } +} +pub fn load<A>(load_data: LoadData, + resource_mgr_chan: IpcSender<ControlMsg>, + devtools_chan: Option<Sender<DevtoolsControlMsg>>, + request_factory: &HttpRequestFactory<R=A>) + -> Result<StreamedResponse<A::R>, LoadError> where A: HttpRequest + 'static { // FIXME: At the time of writing this FIXME, servo didn't have any central // location for configuration. If you're reading this and such a // repository DOES exist, please update this constant to use it. @@ -102,6 +444,7 @@ fn load(mut load_data: LoadData, // specified in the hosts file. let mut url = replace_hosts(&load_data.url); let mut redirected_to = HashSet::new(); + let mut method = load_data.method.clone(); // If the URL is a view-source scheme then the scheme data contains the // real URL that should be used for which the source is to be viewed. @@ -109,80 +452,30 @@ fn load(mut load_data: LoadData, // the source rather than rendering the contents of the URL. let viewing_source = url.scheme == "view-source"; if viewing_source { - let inner_url = load_data.url.non_relative_scheme_data().unwrap(); - doc_url = Url::parse(inner_url).unwrap(); - url = replace_hosts(&doc_url); - match &*url.scheme { - "http" | "https" => {} - _ => { - let s = format!("The {} scheme with view-source is not supported", url.scheme); - send_error(url, s, start_chan); - return; - } - }; + url = inner_url(&load_data.url); + doc_url = url.clone(); } // Loop to handle redirects. loop { iters = iters + 1; - if &*url.scheme != "https" && request_must_be_secured(&hsts_list.lock().unwrap(), &url) { + if &*url.scheme == "http" && request_must_be_secured(&url, &resource_mgr_chan) { info!("{} is in the strict transport security list, requesting secure host", url); url = secure_url(&url); } if iters > max_redirects { - send_error(url, "too many redirects".to_string(), start_chan); - return; + return Err(LoadError::MaxRedirects(url)); } - match &*url.scheme { - "http" | "https" => {} - _ => { - let s = format!("{} request, but we don't support that scheme", url.scheme); - send_error(url, s, start_chan); - return; - } + if &*url.scheme != "http" && &*url.scheme != "https" { + return Err(LoadError::UnsupportedScheme(url)); } info!("requesting {}", url.serialize()); - let ssl_err_string = "Some(OpenSslErrors([UnknownError { library: \"SSL routines\", \ -function: \"SSL3_GET_SERVER_CERTIFICATE\", \ -reason: \"certificate verify failed\" }]))"; - - let req = if opts::get().nossl { - Request::with_connector(load_data.method.clone(), url.clone(), &HttpConnector) - } else { - let mut context = SslContext::new(SslMethod::Sslv23).unwrap(); - context.set_verify(SSL_VERIFY_PEER, None); - context.set_CA_file(&resources_dir_path().join("certs")).unwrap(); - Request::with_connector(load_data.method.clone(), url.clone(), - &HttpsConnector::new(Openssl { context: Arc::new(context) })) - }; - - let mut req = match req { - Ok(req) => req, - Err(HttpError::Io(ref io_error)) if ( - io_error.kind() == io::ErrorKind::Other && - io_error.description() == "Error in OpenSSL" && - // FIXME: This incredibly hacky. Make it more robust, and at least test it. - format!("{:?}", io_error.cause()) == ssl_err_string - ) => { - let mut image = resources_dir_path(); - image.push("badcert.html"); - let load_data = LoadData::new(Url::from_file_path(&*image).unwrap(), None); - file_loader::factory(load_data, start_chan, classifier); - return; - }, - Err(e) => { - println!("{:?}", e); - send_error(url, e.description().to_string(), start_chan); - return; - } - }; - - //Ensure that the host header is set from the original url + // Ensure that the host header is set from the original url let host = Host { hostname: doc_url.serialize_host().unwrap(), port: doc_url.port_or_default() @@ -192,156 +485,90 @@ reason: \"certificate verify failed\" }]))"; // See https://bugzilla.mozilla.org/show_bug.cgi?id=401564 and // https://bugzilla.mozilla.org/show_bug.cgi?id=216828 . // Only preserve ones which have been explicitly marked as such. - if iters == 1 { + let mut request_headers = if iters == 1 { let mut combined_headers = load_data.headers.clone(); combined_headers.extend(load_data.preserved_headers.iter()); - *req.headers_mut() = combined_headers; + combined_headers } else { - *req.headers_mut() = load_data.preserved_headers.clone(); - } + load_data.preserved_headers.clone() + }; - req.headers_mut().set(host); + request_headers.set(host); - if !req.headers().has::<Accept>() { - let accept = Accept(vec![ - qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])), - qitem(Mime(TopLevel::Application, SubLevel::Ext("xhtml+xml".to_string()), vec![])), - QualityItem::new(Mime(TopLevel::Application, SubLevel::Xml, vec![]), Quality(900u16)), - QualityItem::new(Mime(TopLevel::Star, SubLevel::Star, vec![]), Quality(800u16)), - ]); - req.headers_mut().set(accept); - } + set_default_accept(&mut request_headers); + set_default_accept_encoding(&mut request_headers); + set_request_cookies(doc_url.clone(), &mut request_headers, &resource_mgr_chan); - let (tx, rx) = ipc::channel().unwrap(); - resource_mgr_chan.send(ControlMsg::GetCookiesForUrl(doc_url.clone(), - tx, - CookieSource::HTTP)).unwrap(); - if let Some(cookie_list) = rx.recv().unwrap() { - let mut v = Vec::new(); - v.push(cookie_list.into_bytes()); - req.headers_mut().set_raw("Cookie".to_owned(), v); - } + let mut req = try!(request_factory.create(url.clone(), method.clone())); + *req.headers_mut() = request_headers; - if !req.headers().has::<AcceptEncoding>() { - req.headers_mut().set_raw("Accept-Encoding".to_owned(), vec![b"gzip, deflate".to_vec()]); - } if log_enabled!(log::LogLevel::Info) { - info!("{}", load_data.method); - for header in req.headers().iter() { + info!("{}", method); + for header in req.headers_mut().iter() { info!(" - {}", header); } info!("{:?}", load_data.data); } // Avoid automatically sending request body if a redirect has occurred. - let writer = match load_data.data { - Some(ref data) if iters == 1 => { + // + // TODO - This is the wrong behaviour according to the RFC. However, I'm not + // sure how much "correctness" vs. real-world is important in this case. + // + // https://tools.ietf.org/html/rfc7231#section-6.4 + let is_redirected_request = iters != 1; + let request_id = uuid::Uuid::new_v4().to_simple_string(); + let response = match load_data.data { + Some(ref data) if !is_redirected_request => { req.headers_mut().set(ContentLength(data.len() as u64)); - let mut writer = match req.start() { - Ok(w) => w, - Err(e) => { - send_error(url, e.description().to_string(), start_chan); - return; - } - }; - match writer.write_all(&*data) { - Err(e) => { - send_error(url, e.description().to_string(), start_chan); - return; - } - _ => {} - }; - writer - }, + + // TODO: Do this only if load_data has some pipeline_id, and send the pipeline_id + // in the message + send_request_to_devtools( + devtools_chan.clone(), request_id.clone(), url.clone(), + method.clone(), load_data.headers.clone(), + load_data.data.clone() + ); + + try!(req.send(&load_data.data)) + } _ => { - match load_data.method { - Method::Get | Method::Head => (), - _ => req.headers_mut().set(ContentLength(0)) + if load_data.method != Method::Get && load_data.method != Method::Head { + req.headers_mut().set(ContentLength(0)) } - match req.start() { - Ok(w) => w, - Err(e) => { - send_error(url, e.description().to_string(), start_chan); - return; - } - } - } - }; - // Send an HttpRequest message to devtools with a unique request_id - // TODO: Do this only if load_data has some pipeline_id, and send the pipeline_id in the message - let request_id = uuid::Uuid::new_v4().to_simple_string(); - if let Some(ref chan) = devtools_chan { - let net_event = NetworkEvent::HttpRequest(load_data.url.clone(), - load_data.method.clone(), - load_data.headers.clone(), - load_data.data.clone()); - chan.send(DevtoolsControlMsg::FromChrome( - ChromeToDevtoolsControlMsg::NetworkEvent(request_id.clone(), - net_event))).unwrap(); - } + send_request_to_devtools( + devtools_chan.clone(), request_id.clone(), url.clone(), + method.clone(), load_data.headers.clone(), + None + ); - let mut response = match writer.send() { - Ok(r) => r, - Err(e) => { - send_error(url, e.description().to_string(), start_chan); - return; + try!(req.send(&None)) } }; - // Dump headers, but only do the iteration if info!() is enabled. - info!("got HTTP response {}, headers:", response.status); + info!("got HTTP response {}, headers:", response.status()); if log_enabled!(log::LogLevel::Info) { - for header in response.headers.iter() { + for header in response.headers().iter() { info!(" - {}", header); } } - if let Some(cookies) = response.headers.get_raw("set-cookie") { - for cookie in cookies { - if let Ok(cookies) = String::from_utf8(cookie.clone()) { - resource_mgr_chan.send(ControlMsg::SetCookiesForUrl(doc_url.clone(), - cookies, - CookieSource::HTTP)).unwrap(); - } - } - } + set_cookies_from_response(doc_url.clone(), &response, &resource_mgr_chan); + update_sts_list_from_response(&url, &response, &resource_mgr_chan); - if url.scheme == "https" { - if let Some(header) = response.headers.get::<StrictTransportSecurity>() { - if let Some(host) = url.domain() { - info!("adding host {} to the strict transport security list", host); - info!("- max-age {}", header.max_age); - - let include_subdomains = if header.include_subdomains { - info!("- includeSubdomains"); - IncludeSubdomains::Included - } else { - IncludeSubdomains::NotIncluded - }; - - resource_mgr_chan.send( - ControlMsg::SetHSTSEntryForHost( - host.to_string(), include_subdomains, header.max_age - ) - ).unwrap(); - } - } - } - - - if response.status.class() == StatusClass::Redirection { - match response.headers.get::<Location>() { + // --- Loop if there's a redirect + if response.status().class() == StatusClass::Redirection { + match response.headers().get::<Location>() { Some(&Location(ref new_url)) => { // CORS (https://fetch.spec.whatwg.org/#http-fetch, status section, point 9, 10) match load_data.cors { Some(ref c) => { if c.preflight { - // The preflight lied - send_error(url, - "Preflight fetch inconsistent with main fetch".to_string(), - start_chan); - return; + return Err( + LoadError::Cors( + url, + "Preflight fetch inconsistent with main fetch".to_string())); } else { // XXXManishearth There are some CORS-related steps here, // but they don't seem necessary until credentials are implemented @@ -349,28 +576,28 @@ reason: \"certificate verify failed\" }]))"; } _ => {} } + let new_doc_url = match UrlParser::new().base_url(&doc_url).parse(&new_url) { Ok(u) => u, Err(e) => { - send_error(doc_url, e.to_string(), start_chan); - return; + return Err(LoadError::InvalidRedirect(doc_url, e.to_string())); } }; + info!("redirecting to {}", new_doc_url); url = replace_hosts(&new_doc_url); doc_url = new_doc_url; // According to https://tools.ietf.org/html/rfc7231#section-6.4.2, // historically UAs have rewritten POST->GET on 301 and 302 responses. - if load_data.method == Method::Post && - (response.status == StatusCode::MovedPermanently || - response.status == StatusCode::Found) { - load_data.method = Method::Get; + if method == Method::Post && + (response.status() == StatusCode::MovedPermanently || + response.status() == StatusCode::Found) { + method = Method::Get; } - if redirected_to.contains(&doc_url) { - send_error(doc_url, "redirect loop".to_string(), start_chan); - return; + if redirected_to.contains(&url) { + return Err(LoadError::InvalidRedirect(doc_url, "redirect loop".to_string())); } redirected_to.insert(doc_url.clone()); @@ -380,11 +607,13 @@ reason: \"certificate verify failed\" }]))"; } } - let mut adjusted_headers = response.headers.clone(); + let mut adjusted_headers = response.headers().clone(); + if viewing_source { adjusted_headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Plain, vec![]))); } - let mut metadata: Metadata = Metadata::default(doc_url); + + let mut metadata: Metadata = Metadata::default(doc_url.clone()); metadata.set_content_type(match adjusted_headers.get() { Some(&ContentType(ref mime)) => Some(mime), None => None @@ -392,56 +621,16 @@ reason: \"certificate verify failed\" }]))"; metadata.headers = Some(adjusted_headers); metadata.status = Some(response.status_raw().clone()); - let mut encoding_str: Option<String> = None; - //FIXME: Implement Content-Encoding Header https://github.com/hyperium/hyper/issues/391 - if let Some(encodings) = response.headers.get_raw("content-encoding") { - for encoding in encodings { - if let Ok(encodings) = String::from_utf8(encoding.clone()) { - if encodings == "gzip" || encodings == "deflate" { - encoding_str = Some(encodings); - break; - } - } - } - } - + // --- Tell devtools that we got a response // Send an HttpResponse message to devtools with the corresponding request_id // TODO: Send this message only if load_data has a pipeline_id that is not None - if let Some(ref chan) = devtools_chan { - let net_event_response = - NetworkEvent::HttpResponse(metadata.headers.clone(), - metadata.status.clone(), - None); - chan.send(DevtoolsControlMsg::FromChrome( - ChromeToDevtoolsControlMsg::NetworkEvent(request_id, - net_event_response))).unwrap(); - } - - match encoding_str { - Some(encoding) => { - if encoding == "gzip" { - let result = GzDecoder::new(response); - match result { - Ok(mut response_decoding) => { - send_data(&mut response_decoding, start_chan, metadata, classifier); - } - Err(err) => { - send_error(metadata.final_url, err.to_string(), start_chan); - return; - } - } - } else if encoding == "deflate" { - let mut response_decoding = DeflateDecoder::new(response); - send_data(&mut response_decoding, start_chan, metadata, classifier); - } - }, - None => { - send_data(&mut response, start_chan, metadata, classifier); - } - } + // TODO: Send this message even when the load fails? + send_response_to_devtools( + devtools_chan, request_id, + metadata.headers.clone(), metadata.status.clone() + ); - // We didn't get redirected. - break; + return StreamedResponse::from_http_response(response, metadata) } } diff --git a/components/net/mime_classifier.rs b/components/net/mime_classifier.rs index 0ed472b3982..de1b445abf4 100644 --- a/components/net/mime_classifier.rs +++ b/components/net/mime_classifier.rs @@ -125,6 +125,10 @@ impl <'a, T: Iterator<Item=&'a u8> + Clone> Matches for T { // Side effects // moves the iterator when match is found fn matches(&mut self, matches: &[u8]) -> bool { + if self.clone().nth(matches.len()).is_none() { + // there are less than matches.len() elements in self + return false + } let result = self.clone().zip(matches).all(|(s, m)| *s == *m); if result { self.nth(matches.len()); @@ -381,9 +385,10 @@ where T: Iterator<Item=&'a u8> + Clone { struct FeedsClassifier; impl FeedsClassifier { + // Implements sniffing for mislabeled feeds (https://mimesniff.spec.whatwg.org/#sniffing-a-mislabeled-feed) fn classify_impl(&self, data: &[u8]) -> Option<(&'static str, &'static str)> { - // can not be feed unless length is > 3 + // Step 4: can not be feed unless length is > 3 if data.len() < 3 { return None; } @@ -403,6 +408,7 @@ impl FeedsClassifier { return None; } + // Steps 5.2.1 to 5.2.4 match eats_until(&mut matcher, b"?", b"?>") .chain(|| eats_until(&mut matcher, b"!--", b"-->")) .chain(|| eats_until(&mut matcher, b"!", b">")) { @@ -411,20 +417,23 @@ impl FeedsClassifier { Match::Start => return None } + // Step 5.2.5 if matcher.matches(b"rss") { return Some(("application", "rss+xml")); } + // Step 5.2.6 if matcher.matches(b"feed") { return Some(("application", "atom+xml")); } - if matcher.matches(b"rdf: RDF") { + // Step 5.2.7 + if matcher.matches(b"rdf:RDF") { while matcher.next().is_some() { match eats_until(&mut matcher, - b"http: //purl.org/rss/1.0/", - b"http: //www.w3.org/1999/02/22-rdf-syntax-ns#") + b"http://purl.org/rss/1.0/", + b"http://www.w3.org/1999/02/22-rdf-syntax-ns#") .chain(|| eats_until(&mut matcher, - b"http: //www.w3.org/1999/02/22-rdf-syntax-ns#", - b"http: //purl.org/rss/1.0/")) { + b"http://www.w3.org/1999/02/22-rdf-syntax-ns#", + b"http://purl.org/rss/1.0/")) { Match::StartAndEnd => return Some(("application", "rss+xml")), Match::DidNotMatch => {}, Match::Start => return None diff --git a/components/net/resource_task.rs b/components/net/resource_task.rs index 62566f71c70..d232db51bff 100644 --- a/components/net/resource_task.rs +++ b/components/net/resource_task.rs @@ -29,7 +29,6 @@ use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use std::borrow::ToOwned; use std::boxed::FnBox; use std::sync::Arc; -use std::sync::Mutex; use std::sync::mpsc::{channel, Sender}; pub enum ProgressSender { @@ -161,23 +160,26 @@ impl ResourceChannelManager { fn start(&mut self) { loop { match self.from_client.recv().unwrap() { - ControlMsg::Load(load_data, consumer) => { - self.resource_manager.load(load_data, consumer) - } - ControlMsg::SetCookiesForUrl(request, cookie_list, source) => { - self.resource_manager.set_cookies_for_url(request, cookie_list, source) - } - ControlMsg::GetCookiesForUrl(url, consumer, source) => { - consumer.send(self.resource_manager.cookie_storage.cookies_for_url(&url, source)).unwrap(); - } - ControlMsg::SetHSTSEntryForHost(host, include_subdomains, max_age) => { - if let Some(entry) = HSTSEntry::new(host, include_subdomains, Some(max_age)) { - self.resource_manager.add_hsts_entry(entry) - } - } - ControlMsg::Exit => { - break - } + ControlMsg::Load(load_data, consumer) => { + self.resource_manager.load(load_data, consumer) + } + ControlMsg::SetCookiesForUrl(request, cookie_list, source) => { + self.resource_manager.set_cookies_for_url(request, cookie_list, source) + } + ControlMsg::GetCookiesForUrl(url, consumer, source) => { + consumer.send(self.resource_manager.cookie_storage.cookies_for_url(&url, source)).unwrap(); + } + ControlMsg::SetHSTSEntryForHost(host, include_subdomains, max_age) => { + if let Some(entry) = HSTSEntry::new(host, include_subdomains, Some(max_age)) { + self.resource_manager.add_hsts_entry(entry) + } + } + ControlMsg::GetHostMustBeSecured(host, consumer) => { + consumer.send(self.resource_manager.is_host_sts(&*host)).unwrap(); + } + ControlMsg::Exit => { + break + } } } } @@ -189,7 +191,7 @@ pub struct ResourceManager { resource_task: IpcSender<ControlMsg>, mime_classifier: Arc<MIMEClassifier>, devtools_chan: Option<Sender<DevtoolsControlMsg>>, - hsts_list: Arc<Mutex<HSTSList>> + hsts_list: HSTSList } impl ResourceManager { @@ -203,7 +205,7 @@ impl ResourceManager { resource_task: resource_task, mime_classifier: Arc::new(MIMEClassifier::new()), devtools_chan: devtools_channel, - hsts_list: Arc::new(Mutex::new(hsts_list)) + hsts_list: hsts_list } } } @@ -221,11 +223,11 @@ impl ResourceManager { } pub fn add_hsts_entry(&mut self, entry: HSTSEntry) { - self.hsts_list.lock().unwrap().push(entry); + self.hsts_list.push(entry); } pub fn is_host_sts(&self, host: &str) -> bool { - self.hsts_list.lock().unwrap().is_host_secure(host) + self.hsts_list.is_host_secure(host) } fn load(&mut self, mut load_data: LoadData, consumer: LoadConsumer) { @@ -241,7 +243,7 @@ impl ResourceManager { let loader = match &*load_data.url.scheme { "file" => from_factory(file_loader::factory), "http" | "https" | "view-source" => - http_loader::factory(self.resource_task.clone(), self.devtools_chan.clone(), self.hsts_list.clone()), + http_loader::factory(self.resource_task.clone(), self.devtools_chan.clone()), "data" => from_factory(data_loader::factory), "about" => from_factory(about_loader::factory), _ => { diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index e4a93f56673..3f9db2a7ebc 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -161,6 +161,7 @@ pub enum ControlMsg { GetCookiesForUrl(Url, IpcSender<Option<String>>, CookieSource), /// Store a domain's STS information SetHSTSEntryForHost(String, IncludeSubdomains, u64), + GetHostMustBeSecured(String, IpcSender<bool>), Exit } diff --git a/components/plugins/utils.rs b/components/plugins/utils.rs index 79025c33f70..e26cc4bc7cd 100644 --- a/components/plugins/utils.rs +++ b/components/plugins/utils.rs @@ -17,14 +17,14 @@ use syntax::ptr::P; /// Try not to use this for types defined in crates you own, use match_lang_ty instead (for lint passes) pub fn match_ty_unwrap<'a>(ty: &'a Ty, segments: &[&str]) -> Option<&'a [P<Ty>]> { match ty.node { - TyPath(_, Path {segments: ref seg, ..}) => { + TyPath(_, Path { segments: ref seg, .. }) => { // So ast::Path isn't the full path, just the tokens that were provided. // I could muck around with the maps and find the full path // however the more efficient way is to simply reverse the iterators and zip them // which will compare them in reverse until one of them runs out of segments if seg.iter().rev().zip(segments.iter().rev()).all(|(a, b)| a.identifier.name.as_str() == *b) { match seg.last() { - Some(&PathSegment {parameters: AngleBracketedParameters(ref a), ..}) => { + Some(&PathSegment { parameters: AngleBracketedParameters(ref a), .. }) => { Some(&a.types) } _ => None diff --git a/components/profile/lib.rs b/components/profile/lib.rs index 9bf2018f4be..635b9af083f 100644 --- a/components/profile/lib.rs +++ b/components/profile/lib.rs @@ -13,9 +13,9 @@ extern crate ipc_channel; extern crate libc; #[macro_use] extern crate profile_traits; -#[cfg(target_os="linux")] +#[cfg(target_os = "linux")] extern crate regex; -#[cfg(target_os="macos")] +#[cfg(target_os = "macos")] extern crate task_info; extern crate time as std_time; extern crate util; diff --git a/components/profile/mem.rs b/components/profile/mem.rs index dae86a535fd..8b1e7b1397d 100644 --- a/components/profile/mem.rs +++ b/components/profile/mem.rs @@ -368,7 +368,7 @@ mod system_reporter { use std::mem::size_of; use std::ptr::null_mut; use super::{JEMALLOC_HEAP_ALLOCATED_STR, SYSTEM_HEAP_ALLOCATED_STR}; - #[cfg(target_os="macos")] + #[cfg(target_os = "macos")] use task_info::task_basic_info::{virtual_size, resident_size}; /// Collects global measurements from the OS and heap allocators. @@ -418,12 +418,12 @@ mod system_reporter { request.reports_channel.send(reports); } - #[cfg(target_os="linux")] + #[cfg(target_os = "linux")] extern { fn mallinfo() -> struct_mallinfo; } - #[cfg(target_os="linux")] + #[cfg(target_os = "linux")] #[repr(C)] pub struct struct_mallinfo { arena: c_int, @@ -438,7 +438,7 @@ mod system_reporter { keepcost: c_int, } - #[cfg(target_os="linux")] + #[cfg(target_os = "linux")] fn get_system_heap_allocated() -> Option<usize> { let info: struct_mallinfo = unsafe { mallinfo() }; @@ -457,7 +457,7 @@ mod system_reporter { } } - #[cfg(not(target_os="linux"))] + #[cfg(not(target_os = "linux"))] fn get_system_heap_allocated() -> Option<usize> { None } @@ -507,14 +507,14 @@ mod system_reporter { ($e:expr) => (match $e { Some(e) => e, None => return None }) ); - #[cfg(target_os="linux")] + #[cfg(target_os = "linux")] fn page_size() -> usize { unsafe { ::libc::sysconf(::libc::_SC_PAGESIZE) as usize } } - #[cfg(target_os="linux")] + #[cfg(target_os = "linux")] fn get_proc_self_statm_field(field: usize) -> Option<usize> { use std::fs::File; use std::io::Read; @@ -527,37 +527,37 @@ mod system_reporter { Some(npages * page_size()) } - #[cfg(target_os="linux")] + #[cfg(target_os = "linux")] fn get_vsize() -> Option<usize> { get_proc_self_statm_field(0) } - #[cfg(target_os="linux")] + #[cfg(target_os = "linux")] fn get_resident() -> Option<usize> { get_proc_self_statm_field(1) } - #[cfg(target_os="macos")] + #[cfg(target_os = "macos")] fn get_vsize() -> Option<usize> { virtual_size() } - #[cfg(target_os="macos")] + #[cfg(target_os = "macos")] fn get_resident() -> Option<usize> { resident_size() } - #[cfg(not(any(target_os="linux", target_os = "macos")))] + #[cfg(not(any(target_os = "linux", target_os = "macos")))] fn get_vsize() -> Option<usize> { None } - #[cfg(not(any(target_os="linux", target_os = "macos")))] + #[cfg(not(any(target_os = "linux", target_os = "macos")))] fn get_resident() -> Option<usize> { None } - #[cfg(target_os="linux")] + #[cfg(target_os = "linux")] fn get_resident_segments() -> Vec<(String, usize)> { use regex::Regex; use std::collections::HashMap; @@ -658,7 +658,7 @@ mod system_reporter { segs } - #[cfg(not(target_os="linux"))] + #[cfg(not(target_os = "linux"))] fn get_resident_segments() -> Vec<(String, usize)> { vec![] } diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index e52594d8c15..aa8967a62ae 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -55,6 +55,10 @@ features = ["query_encoding", "serde_serialization"] [dependencies.offscreen_gl_context] git = "https://github.com/ecoal95/rust-offscreen-rendering-context" +[dependencies.angle] +git = "https://github.com/ecoal95/angle" +branch = "servo" + [dependencies.cssparser] version = "0.3" features = [ "serde-serialization" ] diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 63f9d010729..08370974cca 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -339,7 +339,7 @@ class CGMethodCall(CGThing): code = ( "if argc < %d {\n" " throw_type_error(cx, \"Not enough arguments to %s.\");\n" - " return 0;\n" + " return JSFalse;\n" "}" % (requiredArgs, methodName)) self.cgRoot.prepend( CGWrapper(CGGeneric(code), pre="\n", post="\n")) @@ -512,11 +512,11 @@ class CGMethodCall(CGThing): CGSwitch("argcount", argCountCases, CGGeneric("throw_type_error(cx, \"Not enough arguments to %s.\");\n" - "return 0;" % methodName))) + "return JSFalse;" % methodName))) # XXXjdm Avoid unreachable statement warnings # overloadCGThings.append( # CGGeneric('panic!("We have an always-returning default case");\n' - # 'return 0;')) + # 'return JSFalse;')) self.cgRoot = CGWrapper(CGList(overloadCGThings, "\n"), pre="\n") @@ -888,7 +888,11 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, if invalidEnumValueFatal: handleInvalidEnumValueCode = exceptionCode else: - handleInvalidEnumValueCode = "return 1;" + handleInvalidEnumValueCode = "return JSTrue;" + + transmute = "mem::transmute(index)" + if isMember == 'Dictionary': + transmute = 'unsafe { ' + transmute + ' }' template = ( "match find_enum_string_index(cx, ${val}, %(values)s) {\n" @@ -896,10 +900,11 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, " Ok(None) => { %(handleInvalidEnumValueCode)s },\n" " Ok(Some(index)) => {\n" " //XXXjdm need some range checks up in here.\n" - " unsafe { mem::transmute(index) }\n" + " %(transmute)s\n" " },\n" "}" % {"values": enum + "Values::strings", "exceptionCode": exceptionCode, + "transmute": transmute, "handleInvalidEnumValueCode": handleInvalidEnumValueCode}) if defaultValue is not None: @@ -1012,7 +1017,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, declType = CGGeneric(typeName) template = ("match %s::new(cx, ${val}) {\n" " Ok(dictionary) => dictionary,\n" - " Err(_) => return 0,\n" + " Err(_) => return JSFalse,\n" "}" % typeName) return handleOptional(template, declType, handleDefaultNull("%s::empty(cx)" % typeName)) @@ -1037,7 +1042,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, conversionBehavior = "()" if failureCode is None: - failureCode = 'return 0' + failureCode = 'return JSFalse' declType = CGGeneric(builtinNames[type.tag()]) if type.nullable(): @@ -1216,7 +1221,7 @@ class CGArgumentConverter(CGThing): return self.converter.define() -def wrapForType(jsvalRef, result='result', successCode='return 1;', pre=''): +def wrapForType(jsvalRef, result='result', successCode='return JSTrue;', pre=''): """ Reflect a Rust value into JS. @@ -1472,6 +1477,7 @@ class AttrDefiner(PropertyDefiner): def __init__(self, descriptor, name, static): PropertyDefiner.__init__(self, descriptor, name) self.name = name + self.descriptor = descriptor self.regular = [ m for m in descriptor.interface.members @@ -1488,14 +1494,14 @@ class AttrDefiner(PropertyDefiner): def getter(attr): if self.static: - accessor = 'get_' + attr.identifier.name + accessor = 'get_' + self.descriptor.internalNameFor(attr.identifier.name) jitinfo = "0 as *const JSJitInfo" else: if attr.hasLenientThis(): accessor = "generic_lenient_getter" else: accessor = "generic_getter" - jitinfo = "&%s_getterinfo" % attr.identifier.name + jitinfo = "&%s_getterinfo" % self.descriptor.internalNameFor(attr.identifier.name) return ("JSNativeWrapper { op: Some(%(native)s), info: %(info)s }" % {"info": jitinfo, @@ -1506,14 +1512,14 @@ class AttrDefiner(PropertyDefiner): return "JSNativeWrapper { op: None, info: 0 as *const JSJitInfo }" if self.static: - accessor = 'set_' + attr.identifier.name + accessor = 'set_' + self.descriptor.internalNameFor(attr.identifier.name) jitinfo = "0 as *const JSJitInfo" else: if attr.hasLenientThis(): accessor = "generic_lenient_setter" else: accessor = "generic_setter" - jitinfo = "&%s_setterinfo" % attr.identifier.name + jitinfo = "&%s_setterinfo" % self.descriptor.internalNameFor(attr.identifier.name) return ("JSNativeWrapper { op: Some(%(native)s), info: %(info)s }" % {"info": jitinfo, @@ -1620,19 +1626,11 @@ class CGImports(CGWrapper): """ if ignored_warnings is None: ignored_warnings = [ - # Allow unreachable_code because we use 'break' in a way that - # sometimes produces two 'break's in a row. See for example - # CallbackMember.getArgConversions. - 'unreachable_code', 'non_camel_case_types', 'non_upper_case_globals', - 'unused_parens', 'unused_imports', 'unused_variables', - 'unused_unsafe', - 'unused_mut', 'unused_assignments', - 'dead_code', ] def componentTypes(type): @@ -2049,7 +2047,7 @@ class CGAbstractMethod(CGThing): """ def __init__(self, descriptor, name, returnType, args, inline=False, alwaysInline=False, extern=False, pub=False, templateArgs=None, - unsafe=True): + unsafe=False): CGThing.__init__(self) self.descriptor = descriptor self.name = name @@ -2111,7 +2109,7 @@ class CGAbstractMethod(CGThing): def CreateBindingJSObject(descriptor, parent=None): - create = "let mut raw = Box::into_raw(object);\nlet _rt = RootedTraceable::new(&*raw);\n" + create = "let raw = Box::into_raw(object);\nlet _rt = RootedTraceable::new(&*raw);\n" if descriptor.proxy: assert not descriptor.isGlobal() create += """ @@ -2160,12 +2158,13 @@ class CGWrapMethod(CGAbstractMethod): assert not descriptor.interface.isCallback() if not descriptor.isGlobal(): args = [Argument('*mut JSContext', 'cx'), Argument('GlobalRef', 'scope'), - Argument("Box<%s>" % descriptor.concreteType, 'object', mutable=True)] + Argument("Box<%s>" % descriptor.concreteType, 'object')] else: args = [Argument('*mut JSContext', 'cx'), - Argument("Box<%s>" % descriptor.concreteType, 'object', mutable=True)] + Argument("Box<%s>" % descriptor.concreteType, 'object')] retval = 'Root<%s>' % descriptor.concreteType - CGAbstractMethod.__init__(self, descriptor, 'Wrap', retval, args, pub=True) + CGAbstractMethod.__init__(self, descriptor, 'Wrap', retval, args, + pub=True, unsafe=True) def definition_body(self): if not self.descriptor.isGlobal(): @@ -2309,6 +2308,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): def definition_body(self): protoChain = self.descriptor.prototypeChain if len(protoChain) == 1: + self.unsafe = True getParentProto = "parent_proto.ptr = JS_GetObjectPrototype(cx, global)" else: parentProtoName = self.descriptor.prototypeChain[-2] @@ -2382,7 +2382,7 @@ class CGGetPerInterfaceObject(CGAbstractMethod): Argument('HandleObject', 'receiver'), Argument('MutableHandleObject', 'rval')] CGAbstractMethod.__init__(self, descriptor, name, - 'void', args, pub=pub) + 'void', args, pub=pub, unsafe=True) self.id = idPrefix + "ID::" + self.descriptor.name def definition_body(self): @@ -2452,7 +2452,9 @@ class CGDefineProxyHandler(CGAbstractMethod): """ def __init__(self, descriptor): assert descriptor.proxy - CGAbstractMethod.__init__(self, descriptor, 'DefineProxyHandler', '*const libc::c_void', [], pub=True) + CGAbstractMethod.__init__(self, descriptor, 'DefineProxyHandler', + '*const libc::c_void', [], + pub=True, unsafe=True) def define(self): return CGAbstractMethod.define(self) @@ -2778,7 +2780,7 @@ class CGSetterCall(CGPerSignatureCall): def wrap_return_value(self): # We have no return value - return "\nreturn 1;" + return "\nreturn JSTrue;" def getArgc(self): return "1" @@ -2835,7 +2837,10 @@ class CGSpecializedMethod(CGAbstractExternMethod): @staticmethod def makeNativeName(descriptor, method): name = method.identifier.name - return MakeNativeName(descriptor.binaryNameFor(name)) + nativeName = descriptor.binaryNameFor(name) + if nativeName == name: + nativeName = descriptor.internalNameFor(name) + return MakeNativeName(nativeName) class CGStaticMethod(CGAbstractStaticBindingMethod): @@ -2850,7 +2855,7 @@ class CGStaticMethod(CGAbstractStaticBindingMethod): def generate_code(self): nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, self.method) - setupArgs = CGGeneric("let mut args = CallArgs::from_vp(vp, argc);\n") + setupArgs = CGGeneric("let args = CallArgs::from_vp(vp, argc);\n") call = CGMethodCall(["global.r()"], nativeName, True, self.descriptor, self.method) return CGList([setupArgs, call]) @@ -2862,7 +2867,7 @@ class CGSpecializedGetter(CGAbstractExternMethod): """ def __init__(self, descriptor, attr): self.attr = attr - name = 'get_' + attr.identifier.name + name = 'get_' + descriptor.internalNameFor(attr.identifier.name) args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', '_obj'), Argument('*const %s' % descriptor.concreteType, 'this'), @@ -2880,7 +2885,10 @@ class CGSpecializedGetter(CGAbstractExternMethod): @staticmethod def makeNativeName(descriptor, attr): name = attr.identifier.name - nativeName = MakeNativeName(descriptor.binaryNameFor(name)) + nativeName = descriptor.binaryNameFor(name) + if nativeName == name: + nativeName = descriptor.internalNameFor(name) + nativeName = MakeNativeName(nativeName) infallible = ('infallible' in descriptor.getExtendedAttributes(attr, getter=True)) if attr.type.nullable() or not infallible: @@ -2901,7 +2909,7 @@ class CGStaticGetter(CGAbstractStaticBindingMethod): def generate_code(self): nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, self.attr) - setupArgs = CGGeneric("let mut args = CallArgs::from_vp(vp, argc);\n") + setupArgs = CGGeneric("let args = CallArgs::from_vp(vp, argc);\n") call = CGGetterCall(["global.r()"], self.attr.type, nativeName, self.descriptor, self.attr) return CGList([setupArgs, call]) @@ -2914,7 +2922,7 @@ class CGSpecializedSetter(CGAbstractExternMethod): """ def __init__(self, descriptor, attr): self.attr = attr - name = 'set_' + attr.identifier.name + name = 'set_' + descriptor.internalNameFor(attr.identifier.name) args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'obj'), Argument('*const %s' % descriptor.concreteType, 'this'), @@ -2931,7 +2939,10 @@ class CGSpecializedSetter(CGAbstractExternMethod): @staticmethod def makeNativeName(descriptor, attr): name = attr.identifier.name - return "Set" + MakeNativeName(descriptor.binaryNameFor(name)) + nativeName = descriptor.binaryNameFor(name) + if nativeName == name: + nativeName = descriptor.internalNameFor(name) + return "Set" + MakeNativeName(nativeName) class CGStaticSetter(CGAbstractStaticBindingMethod): @@ -2948,9 +2959,9 @@ class CGStaticSetter(CGAbstractStaticBindingMethod): self.attr) checkForArg = CGGeneric( "let args = CallArgs::from_vp(vp, argc);\n" - "if (argc == 0) {\n" + "if argc == 0 {\n" " throw_type_error(cx, \"Not enough arguments to %s setter.\");\n" - " return 0;\n" + " return JSFalse;\n" "}" % self.attr.identifier.name) call = CGSetterCall(["global.r()"], self.attr.type, nativeName, self.descriptor, self.attr) @@ -3045,8 +3056,9 @@ class CGMemberJITInfo(CGThing): def define(self): if self.member.isAttr(): - getterinfo = ("%s_getterinfo" % self.member.identifier.name) - getter = ("get_%s" % self.member.identifier.name) + internalMemberName = self.descriptor.internalNameFor(self.member.identifier.name) + getterinfo = ("%s_getterinfo" % internalMemberName) + getter = ("get_%s" % internalMemberName) getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True) movable = self.mayBeMovable() and getterinfal @@ -3070,8 +3082,8 @@ class CGMemberJITInfo(CGThing): slotIndex, [self.member.type], None) if (not self.member.readonly or self.member.getExtendedAttribute("PutForwards")): - setterinfo = ("%s_setterinfo" % self.member.identifier.name) - setter = ("set_%s" % self.member.identifier.name) + setterinfo = ("%s_setterinfo" % internalMemberName) + setter = ("set_%s" % internalMemberName) # Setters are always fallible, since they have to do a typed unwrap. result += self.defineJitInfo(setterinfo, setter, "Setter", False, False, "AliasEverything", @@ -4021,7 +4033,8 @@ class CGProxyUnwrap(CGAbstractMethod): def __init__(self, descriptor): args = [Argument('HandleObject', 'obj')] CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", - '*const ' + descriptor.concreteType, args, alwaysInline=True) + '*const ' + descriptor.concreteType, args, + alwaysInline=True, unsafe=True) def definition_body(self): return CGGeneric("""\ @@ -4218,7 +4231,7 @@ class CGDOMJSProxyHandler_ownPropertyKeys(CGAbstractExternMethod): for name in (*unwrapped_proxy).SupportedPropertyNames() { let cstring = CString::new(name).unwrap(); let jsstring = JS_InternString(cx, cstring.as_ptr()); - let mut rooted = RootedString::new(cx, jsstring); + let rooted = RootedString::new(cx, jsstring); let jsid = INTERNED_STRING_TO_JSID(cx, rooted.handle().get()); let rooted_jsid = RootedId::new(cx, jsid); AppendToAutoIdVector(props, rooted_jsid.handle().get()); @@ -4338,7 +4351,7 @@ if !expando.ptr.is_null() { namedGetter = self.descriptor.operations['NamedGetter'] if namedGetter: - getNamed = ("if (RUST_JSID_IS_STRING(id) != 0) {\n" + + getNamed = ("if RUST_JSID_IS_STRING(id) != 0 {\n" + CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "}\n") else: @@ -5506,7 +5519,7 @@ class CallbackMember(CGNativeMember): conversion = ( CGIfWrapper(CGGeneric(conversion), "%s.is_some()" % arg.identifier.name).define() + - " else if (argc == %d) {\n" + " else if argc == %d {\n" " // This is our current trailing argument; reduce argc\n" " argc -= 1;\n" "} else {\n" @@ -5539,6 +5552,8 @@ class CallbackMember(CGNativeMember): "}\n") def getArgcDecl(self): + if self.argCount <= 1: + return CGGeneric("let argc = %s;" % self.argCountStr) return CGGeneric("let mut argc = %s;" % self.argCountStr) @staticmethod diff --git a/components/script/dom/bindings/codegen/Configuration.py b/components/script/dom/bindings/codegen/Configuration.py index 4e740f83a16..1a88968619b 100644 --- a/components/script/dom/bindings/codegen/Configuration.py +++ b/components/script/dom/bindings/codegen/Configuration.py @@ -263,6 +263,8 @@ class Descriptor(DescriptorProvider): self._binaryNames.setdefault('__legacycaller', 'LegacyCall') self._binaryNames.setdefault('__stringifier', 'Stringifier') + self._internalNames = desc.get('internalNames', {}) + for member in self.interface.members: if not member.isAttr() and not member.isMethod(): continue @@ -272,6 +274,8 @@ class Descriptor(DescriptorProvider): assert len(binaryName) == 1 self._binaryNames.setdefault(member.identifier.name, binaryName[0]) + self._internalNames.setdefault(member.identifier.name, + member.identifier.name.replace('-', '_')) # Build the prototype chain. self.prototypeChain = [] @@ -285,6 +289,9 @@ class Descriptor(DescriptorProvider): def binaryNameFor(self, name): return self._binaryNames.get(name, name) + def internalNameFor(self, name): + return self._internalNames.get(name, name) + def getExtendedAttributes(self, member, getter=False, setter=False): def maybeAppendInfallibleToAttrs(attrs, throws): if throws is None: diff --git a/components/script/dom/bindings/codegen/GlobalGen.py b/components/script/dom/bindings/codegen/GlobalGen.py index b3ed1e25b12..537883fae57 100644 --- a/components/script/dom/bindings/codegen/GlobalGen.py +++ b/components/script/dom/bindings/codegen/GlobalGen.py @@ -28,7 +28,7 @@ def generate_file(config, name, filename): def main(): # Parse arguments. from optparse import OptionParser - usageString = "usage: %prog [options] webidldir [files]" + usageString = "usage: %prog [options] configFile outputdir webidldir [files]" o = OptionParser(usage=usageString) o.add_option("--cachedir", dest='cachedir', default=None, help="Directory in which to cache lex/parse tables.") @@ -40,8 +40,9 @@ def main(): o.error(usageString) configFile = args[0] - baseDir = args[1] - fileList = args[2:] + outputdir = args[1] + baseDir = args[2] + fileList = args[3:] # Parse the WebIDL. parser = WebIDL.Parser(options.cachedir) @@ -59,22 +60,17 @@ def main(): # Load the configuration. config = Configuration(configFile, parserResults) - # Generate the prototype list. - generate_file(config, 'PrototypeList', 'PrototypeList.rs') - - # Generate the common code. - generate_file(config, 'RegisterBindings', 'RegisterBindings.rs') - - # Generate the type list. - generate_file(config, 'InterfaceTypes', 'InterfaceTypes.rs') - - # Generate the type list. - generate_file(config, 'InheritTypes', 'InheritTypes.rs') - - # Generate the module declarations. - generate_file(config, 'Bindings', 'Bindings/mod.rs') - - generate_file(config, 'UnionTypes', 'UnionTypes.rs') + to_generate = [ + ('PrototypeList', 'PrototypeList.rs'), + ('RegisterBindings', 'RegisterBindings.rs'), + ('InterfaceTypes', 'InterfaceTypes.rs'), + ('InheritTypes', 'InheritTypes.rs'), + ('Bindings', 'Bindings/mod.rs'), + ('UnionTypes', 'UnionTypes.rs'), + ] + + for name, filename in to_generate: + generate_file(config, name, os.path.join(outputdir, filename)) if __name__ == '__main__': main() diff --git a/components/script/dom/bindings/codegen/pythonpath.py b/components/script/dom/bindings/codegen/pythonpath.py deleted file mode 100644 index 793089551b5..00000000000 --- a/components/script/dom/bindings/codegen/pythonpath.py +++ /dev/null @@ -1,61 +0,0 @@ -# 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/. - -""" -Run a python script, adding extra directories to the python path. -""" - - -def main(args): - def usage(): - print >>sys.stderr, "pythonpath.py -I directory script.py [args...]" - sys.exit(150) - - paths = [] - - while True: - try: - arg = args[0] - except IndexError: - usage() - - if arg == '-I': - args.pop(0) - try: - path = args.pop(0) - except IndexError: - usage() - - paths.append(os.path.abspath(path)) - continue - - if arg.startswith('-I'): - paths.append(os.path.abspath(args.pop(0)[2:])) - continue - - if arg.startswith('-D'): - os.chdir(args.pop(0)[2:]) - continue - - break - - script = args[0] - - sys.path[0:0] = [os.path.abspath(os.path.dirname(script))] + paths - sys.argv = args - sys.argc = len(args) - - frozenglobals['__name__'] = '__main__' - frozenglobals['__file__'] = script - - execfile(script, frozenglobals) - -# Freeze scope here ... why this makes things work I have no idea ... -frozenglobals = globals() - -import sys -import os - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/components/script/dom/bindings/mod.rs b/components/script/dom/bindings/mod.rs index 283dc9b08c9..1ded213d0f4 100644 --- a/components/script/dom/bindings/mod.rs +++ b/components/script/dom/bindings/mod.rs @@ -168,15 +168,13 @@ pub mod codegen { pub mod PrototypeList { include!(concat!(env!("OUT_DIR"), "/PrototypeList.rs")); } - #[allow(unreachable_code, non_camel_case_types, non_upper_case_globals, unused_parens, - unused_imports, unused_variables, unused_unsafe, unused_mut, unused_assignments, - dead_code)] + #[allow(non_camel_case_types, non_upper_case_globals, + unused_imports, unused_variables, unused_assignments)] pub mod RegisterBindings { include!(concat!(env!("OUT_DIR"), "/RegisterBindings.rs")); } - #[allow(unreachable_code, non_camel_case_types, non_upper_case_globals, unused_parens, - unused_imports, unused_variables, unused_unsafe, unused_mut, unused_assignments, - dead_code)] + #[allow(non_camel_case_types, non_upper_case_globals, + unused_imports, unused_variables, unused_assignments)] pub mod UnionTypes { include!(concat!(env!("OUT_DIR"), "/UnionTypes.rs")); } diff --git a/components/script/dom/bindings/num.rs b/components/script/dom/bindings/num.rs index 87c5e38a3bb..1169ccdec7f 100644 --- a/components/script/dom/bindings/num.rs +++ b/components/script/dom/bindings/num.rs @@ -9,7 +9,7 @@ use num::Float; use std::ops::Deref; /// Encapsulates the IDL restricted float type. -#[derive(JSTraceable,Clone,Eq,PartialEq)] +#[derive(JSTraceable, Clone, Eq, PartialEq)] pub struct Finite<T: Float>(T); unsafe impl<T: Float> Zeroable for Finite<T> {} diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index b1dcbbce163..8e48de2c680 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -29,7 +29,7 @@ //! The `no_jsmanaged_fields!()` macro adds an empty implementation of `JSTraceable` to //! a datatype. -use dom::bindings::js::JS; +use dom::bindings::js::{JS, Root}; use dom::bindings::refcounted::Trusted; use dom::bindings::utils::{Reflectable, Reflector, WindowProxyHandler}; use script_task::ScriptChan; @@ -69,6 +69,7 @@ use std::collections::{HashMap, HashSet}; use std::ffi::CString; use std::hash::{Hash, Hasher}; use std::intrinsics::return_address; +use std::iter::{FromIterator, IntoIterator}; use std::mem; use std::ops::{Deref, DerefMut}; use std::rc::Rc; @@ -511,6 +512,17 @@ impl<T: JSTraceable + Reflectable> DerefMut for RootedVec<T> { } } +impl<A: JSTraceable + Reflectable> FromIterator<Root<A>> for RootedVec<JS<A>> { + #[allow(moved_no_move)] + fn from_iter<T>(iterable: T) -> RootedVec<JS<A>> where T: IntoIterator<Item=Root<A>> { + let mut vec = RootedVec::new_with_destination_address(unsafe { + return_address() as *const libc::c_void + }); + vec.extend(iterable.into_iter().map(|item| JS::from_rooted(&item))); + vec + } +} + /// SM Callback that traces the rooted traceables pub unsafe fn trace_traceables(tracer: *mut JSTracer) { ROOTED_TRACEABLES.with(|ref traceables| { diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs index e44e17d39de..247de62b23f 100644 --- a/components/script/dom/cssstyledeclaration.rs +++ b/components/script/dom/cssstyledeclaration.rs @@ -334,5 +334,6 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { rval } + // https://drafts.csswg.org/cssom/#cssstyledeclaration css_properties_accessors!(css_properties); } diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index bc179a82133..d4a3b216914 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -357,6 +357,7 @@ impl DedicatedWorkerGlobalScopeMethods for DedicatedWorkerGlobalScope { Ok(()) } + // https://html.spec.whatwg.org/multipage/#handler-dedicatedworkerglobalscope-onmessage event_handler!(message, GetOnmessage, SetOnmessage); } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 0ff28055e9d..196ea7af438 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -1842,7 +1842,10 @@ impl DocumentMethods for Document { // This method intentionally does nothing } + // https://html.spec.whatwg.org/multipage/#globaleventhandlers global_event_handlers!(); + + // https://html.spec.whatwg.org/multipage/#handler-onreadystatechange event_handler!(readystatechange, GetOnreadystatechange, SetOnreadystatechange); } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 83b7c24ed3a..4e03dff4086 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -1867,10 +1867,10 @@ impl Element { } } // Step 6 - None => {event.fire(target);} + None => { event.fire(target); } }, // Step 6 - None => {event.fire(target);} + None => { event.fire(target); } } // Step 7 self.set_click_in_progress(false); diff --git a/components/script/dom/filereader.rs b/components/script/dom/filereader.rs index 384ab976be3..ffd74806c04 100644 --- a/components/script/dom/filereader.rs +++ b/components/script/dom/filereader.rs @@ -253,11 +253,22 @@ impl FileReader { } impl FileReaderMethods for FileReader { + // https://w3c.github.io/FileAPI/#dfn-onloadstart event_handler!(loadstart, GetOnloadstart, SetOnloadstart); + + // https://w3c.github.io/FileAPI/#dfn-onprogress event_handler!(progress, GetOnprogress, SetOnprogress); + + // https://w3c.github.io/FileAPI/#dfn-onload event_handler!(load, GetOnload, SetOnload); + + // https://w3c.github.io/FileAPI/#dfn-onabort event_handler!(abort, GetOnabort, SetOnabort); + + // https://w3c.github.io/FileAPI/#dfn-onerror event_handler!(error, GetOnerror, SetOnerror); + + // https://w3c.github.io/FileAPI/#dfn-onloadend event_handler!(loadend, GetOnloadend, SetOnloadend); //TODO https://w3c.github.io/FileAPI/#dfn-readAsArrayBuffer diff --git a/components/script/dom/formdata.rs b/components/script/dom/formdata.rs index 8390430c39e..8631cefbd4a 100644 --- a/components/script/dom/formdata.rs +++ b/components/script/dom/formdata.rs @@ -85,21 +85,14 @@ impl FormDataMethods for FormData { self.data.borrow_mut().remove(&name); } - #[allow(unsafe_code)] // https://xhr.spec.whatwg.org/#dom-formdata-get fn Get(&self, name: DOMString) -> Option<FileOrString> { - // FIXME(https://github.com/rust-lang/rust/issues/23338) - let data = self.data.borrow(); - if data.contains_key(&name) { - match data[&name][0].clone() { - FormDatum::StringData(ref s) => Some(eString(s.clone())), - FormDatum::FileData(ref f) => { - Some(eFile(f.root())) - } - } - } else { - None - } + self.data.borrow() + .get(&name) + .map(|entry| match entry[0] { + FormDatum::StringData(ref s) => eString(s.clone()), + FormDatum::FileData(ref f) => eFile(f.root()), + }) } // https://xhr.spec.whatwg.org/#dom-formdata-has diff --git a/components/script/dom/htmlappletelement.rs b/components/script/dom/htmlappletelement.rs index a3cb281497b..988aa36e9ee 100644 --- a/components/script/dom/htmlappletelement.rs +++ b/components/script/dom/htmlappletelement.rs @@ -54,6 +54,8 @@ impl HTMLAppletElement { impl HTMLAppletElementMethods for HTMLAppletElement { // https://html.spec.whatwg.org/#the-applet-element:dom-applet-name make_getter!(Name); + + // https://html.spec.whatwg.org/#the-applet-element:dom-applet-name make_atomic_setter!(SetName, "name"); } diff --git a/components/script/dom/htmlbodyelement.rs b/components/script/dom/htmlbodyelement.rs index a38e385bc9c..3016966fc10 100644 --- a/components/script/dom/htmlbodyelement.rs +++ b/components/script/dom/htmlbodyelement.rs @@ -71,6 +71,8 @@ impl HTMLBodyElement { impl HTMLBodyElementMethods for HTMLBodyElement { // https://html.spec.whatwg.org/multipage#dom-body-bgcolor make_getter!(BgColor, "bgcolor"); + + // https://html.spec.whatwg.org/multipage#dom-body-bgcolor make_setter!(SetBgColor, "bgcolor"); // https://html.spec.whatwg.org/multipage/#the-body-element diff --git a/components/script/dom/htmlbuttonelement.rs b/components/script/dom/htmlbuttonelement.rs index a8c95eb6d3b..c7f3e71807e 100644 --- a/components/script/dom/htmlbuttonelement.rs +++ b/components/script/dom/htmlbuttonelement.rs @@ -98,22 +98,29 @@ impl HTMLButtonElementMethods for HTMLButtonElement { // https://html.spec.whatwg.org/multipage/#dom-button-type make_setter!(SetType, "type"); - // https://html.spec.whatwg.org/multipage/#htmlbuttonelement + // https://html.spec.whatwg.org/multipage/#dom-fs-formaction make_url_or_base_getter!(FormAction); + // https://html.spec.whatwg.org/multipage/#dom-fs-formaction make_setter!(SetFormAction, "formaction"); + // https://html.spec.whatwg.org/multipage/#dom-fs-formenctype make_enumerated_getter!( FormEnctype, "application/x-www-form-urlencoded", ("text/plain") | ("multipart/form-data")); + // https://html.spec.whatwg.org/multipage/#dom-fs-formenctype make_setter!(SetFormEnctype, "formenctype"); + // https://html.spec.whatwg.org/multipage/#dom-fs-formmethod make_enumerated_getter!(FormMethod, "get", ("post") | ("dialog")); + // https://html.spec.whatwg.org/multipage/#dom-fs-formmethod make_setter!(SetFormMethod, "formmethod"); + // https://html.spec.whatwg.org/multipage/#dom-fs-formtarget make_getter!(FormTarget); + // https://html.spec.whatwg.org/multipage/#dom-fs-formtarget make_setter!(SetFormTarget, "formtarget"); // https://html.spec.whatwg.org/multipage/#dom-fe-name diff --git a/components/script/dom/htmlcollection.rs b/components/script/dom/htmlcollection.rs index 6aee8645ad8..ad16e70afa2 100644 --- a/components/script/dom/htmlcollection.rs +++ b/components/script/dom/htmlcollection.rs @@ -64,7 +64,7 @@ impl HTMLCollection { } } } - let filter = AllElementFilter {namespace_filter: namespace_filter}; + let filter = AllElementFilter { namespace_filter: namespace_filter }; HTMLCollection::create(window, root, box filter) } diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs index 0b9ea1e6b8c..80e6f93d36e 100644 --- a/components/script/dom/htmlelement.rs +++ b/components/script/dom/htmlelement.rs @@ -130,16 +130,22 @@ impl HTMLElementMethods for HTMLElement { }) } + // https://html.spec.whatwg.org/multipage/#attr-title make_getter!(Title); + // https://html.spec.whatwg.org/multipage/#attr-title make_setter!(SetTitle, "title"); + // https://html.spec.whatwg.org/multipage/#attr-lang make_getter!(Lang); + // https://html.spec.whatwg.org/multipage/#attr-lang make_setter!(SetLang, "lang"); // https://html.spec.whatwg.org/multipage/#dom-hidden make_bool_getter!(Hidden); + // https://html.spec.whatwg.org/multipage/#dom-hidden make_bool_setter!(SetHidden, "hidden"); + // https://html.spec.whatwg.org/multipage/#globaleventhandlers global_event_handlers!(NoOnload); // https://html.spec.whatwg.org/multipage/#dom-dataset diff --git a/components/script/dom/htmlfontelement.rs b/components/script/dom/htmlfontelement.rs index 03750d382b8..6b745617b94 100644 --- a/components/script/dom/htmlfontelement.rs +++ b/components/script/dom/htmlfontelement.rs @@ -50,7 +50,10 @@ impl HTMLFontElement { } impl HTMLFontElementMethods for HTMLFontElement { + // https://html.spec.whatwg.org/multipage/#dom-font-color make_getter!(Color, "color"); + + // https://html.spec.whatwg.org/multipage/#dom-font-color make_setter!(SetColor, "color"); } diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index 11613d866b2..05f7442bbcc 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -122,6 +122,8 @@ impl HTMLFormElementMethods for HTMLFormElement { // https://html.spec.whatwg.org/multipage/#dom-form-name make_getter!(Name); + + // https://html.spec.whatwg.org/multipage/#dom-form-name make_atomic_setter!(SetName, "name"); // https://html.spec.whatwg.org/multipage/#dom-fs-novalidate diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 6abf4f7b877..e4665847468 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -339,12 +339,14 @@ impl HTMLIFrameElementMethods for HTMLIFrameElement { Err(NotSupported) } + // https://html.spec.whatwg.org/multipage/#dom-dim-width make_getter!(Width); - + // https://html.spec.whatwg.org/multipage/#dom-dim-width make_setter!(SetWidth, "width"); + // https://html.spec.whatwg.org/multipage/#dom-dim-height make_getter!(Height); - + // https://html.spec.whatwg.org/multipage/#dom-dim-height make_setter!(SetHeight, "height"); } diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index dcb69a44104..bfd6ca2fdd8 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -197,18 +197,22 @@ impl LayoutHTMLImageElementHelpers for LayoutJS<HTMLImageElement> { } impl HTMLImageElementMethods for HTMLImageElement { + // https://html.spec.whatwg.org/multipage/#dom-img-alt make_getter!(Alt); - + // https://html.spec.whatwg.org/multipage/#dom-img-alt make_setter!(SetAlt, "alt"); + // https://html.spec.whatwg.org/multipage/#dom-img-src make_url_getter!(Src); - + // https://html.spec.whatwg.org/multipage/#dom-img-src make_setter!(SetSrc, "src"); + // https://html.spec.whatwg.org/multipage/#dom-img-usemap make_getter!(UseMap); - + // https://html.spec.whatwg.org/multipage/#dom-img-usemap make_setter!(SetUseMap, "usemap"); + // https://html.spec.whatwg.org/multipage/#dom-img-ismap make_bool_getter!(IsMap); // https://html.spec.whatwg.org/multipage/#dom-img-ismap @@ -269,28 +273,40 @@ impl HTMLImageElementMethods for HTMLImageElement { image.is_some() } - // https://html.spec.whatwg.org/#dom-img-name + // https://html.spec.whatwg.org/multipage/#dom-img-name make_getter!(Name); + + // https://html.spec.whatwg.org/multipage/#dom-img-name make_atomic_setter!(SetName, "name"); + // https://html.spec.whatwg.org/multipage/#dom-img-align make_getter!(Align); + // https://html.spec.whatwg.org/multipage/#dom-img-align make_setter!(SetAlign, "align"); + // https://html.spec.whatwg.org/multipage/#dom-img-hspace make_uint_getter!(Hspace); + // https://html.spec.whatwg.org/multipage/#dom-img-hspace make_uint_setter!(SetHspace, "hspace"); + // https://html.spec.whatwg.org/multipage/#dom-img-vspace make_uint_getter!(Vspace); + // https://html.spec.whatwg.org/multipage/#dom-img-vspace make_uint_setter!(SetVspace, "vspace"); + // https://html.spec.whatwg.org/multipage/#dom-img-longdesc make_getter!(LongDesc); + // https://html.spec.whatwg.org/multipage/#dom-img-longdesc make_setter!(SetLongDesc, "longdesc"); + // https://html.spec.whatwg.org/multipage/#dom-img-border make_getter!(Border); + // https://html.spec.whatwg.org/multipage/#dom-img-border make_setter!(SetBorder, "border"); } diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index becbf7c4947..9204e20ab22 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -247,6 +247,8 @@ impl HTMLInputElementMethods for HTMLInputElement { // https://html.spec.whatwg.org/multipage/#dom-input-size make_uint_getter!(Size, "size", DEFAULT_INPUT_SIZE); + + // https://html.spec.whatwg.org/multipage/#dom-input-size make_limited_uint_setter!(SetSize, "size", DEFAULT_INPUT_SIZE); // https://html.spec.whatwg.org/multipage/#dom-input-type diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index 072e14bab3b..b9e3d0e081b 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -209,19 +209,34 @@ impl HTMLLinkElement { } impl HTMLLinkElementMethods for HTMLLinkElement { + // https://html.spec.whatwg.org/multipage/#dom-link-href make_url_getter!(Href); + + // https://html.spec.whatwg.org/multipage/#dom-link-href make_setter!(SetHref, "href"); + // https://html.spec.whatwg.org/multipage/#dom-link-rel make_getter!(Rel); + + // https://html.spec.whatwg.org/multipage/#dom-link-rel make_setter!(SetRel, "rel"); + // https://html.spec.whatwg.org/multipage/#dom-link-media make_getter!(Media); + + // https://html.spec.whatwg.org/multipage/#dom-link-media make_setter!(SetMedia, "media"); + // https://html.spec.whatwg.org/multipage/#dom-link-hreflang make_getter!(Hreflang); + + // https://html.spec.whatwg.org/multipage/#dom-link-hreflang make_setter!(SetHreflang, "hreflang"); + // https://html.spec.whatwg.org/multipage/#dom-link-type make_getter!(Type); + + // https://html.spec.whatwg.org/multipage/#dom-link-type make_setter!(SetType, "type"); // https://html.spec.whatwg.org/multipage/#dom-link-rellist diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 82908e2d78f..97cd6573fda 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -571,8 +571,9 @@ impl VirtualMethods for HTMLScriptElement { } impl HTMLScriptElementMethods for HTMLScriptElement { + // https://html.spec.whatwg.org/multipage/#dom-script-src make_url_getter!(Src); - + // https://html.spec.whatwg.org/multipage/#dom-script-src make_setter!(SetSrc, "src"); // https://www.whatwg.org/html/#dom-script-text diff --git a/components/script/dom/htmltablecellelement.rs b/components/script/dom/htmltablecellelement.rs index 153593f5e58..db4a08a00c5 100644 --- a/components/script/dom/htmltablecellelement.rs +++ b/components/script/dom/htmltablecellelement.rs @@ -77,6 +77,8 @@ impl HTMLTableCellElement { impl HTMLTableCellElementMethods for HTMLTableCellElement { // https://html.spec.whatwg.org/multipage/#dom-tdth-colspan make_uint_getter!(ColSpan, "colspan", DEFAULT_COLSPAN); + + // https://html.spec.whatwg.org/multipage/#dom-tdth-colspan make_uint_setter!(SetColSpan, "colspan"); } diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs index 333e5259394..a0a0c12b705 100644 --- a/components/script/dom/htmltextareaelement.rs +++ b/components/script/dom/htmltextareaelement.rs @@ -121,6 +121,8 @@ impl HTMLTextAreaElementMethods for HTMLTextAreaElement { // https://html.spec.whatwg.org/multipage/#dom-textarea-cols make_uint_getter!(Cols, "cols", DEFAULT_COLS); + + // https://html.spec.whatwg.org/multipage/#dom-textarea-cols make_limited_uint_setter!(SetCols, "cols", DEFAULT_COLS); // https://www.whatwg.org/html/#dom-fe-disabled @@ -155,6 +157,8 @@ impl HTMLTextAreaElementMethods for HTMLTextAreaElement { // https://html.spec.whatwg.org/multipage/#dom-textarea-rows make_uint_getter!(Rows, "rows", DEFAULT_ROWS); + + // https://html.spec.whatwg.org/multipage/#dom-textarea-rows make_limited_uint_setter!(SetRows, "rows", DEFAULT_ROWS); // https://html.spec.whatwg.org/multipage/#dom-textarea-wrap diff --git a/components/script/dom/location.rs b/components/script/dom/location.rs index 22c250bbb55..8b5cf37965e 100644 --- a/components/script/dom/location.rs +++ b/components/script/dom/location.rs @@ -4,6 +4,7 @@ use dom::bindings::codegen::Bindings::LocationBinding; use dom::bindings::codegen::Bindings::LocationBinding::LocationMethods; +use dom::bindings::error::ErrorResult; use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, Root}; use dom::bindings::str::USVString; @@ -33,6 +34,18 @@ impl Location { GlobalRef::Window(window), LocationBinding::Wrap) } + + fn get_url(&self) -> Url { + self.window.root().get_url() + } + + fn set_url_component(&self, value: USVString, + setter: fn(&mut Url, USVString)) { + let window = self.window.root(); + let mut url = window.get_url(); + setter(&mut url, value); + window.load_url(url); + } } impl LocationMethods for Location { @@ -52,9 +65,9 @@ impl LocationMethods for Location { UrlHelper::Hash(&self.get_url()) } - // https://url.spec.whatwg.org/#dom-urlutils-href - fn Href(&self) -> USVString { - UrlHelper::Href(&self.get_url()) + // https://url.spec.whatwg.org/#dom-urlutils-hash + fn SetHash(&self, value: USVString) { + self.set_url_component(value, UrlHelper::SetHash); } // https://url.spec.whatwg.org/#dom-urlutils-host @@ -62,31 +75,75 @@ impl LocationMethods for Location { UrlHelper::Host(&self.get_url()) } + // https://url.spec.whatwg.org/#dom-urlutils-host + fn SetHost(&self, value: USVString) { + self.set_url_component(value, UrlHelper::SetHost); + } + // https://url.spec.whatwg.org/#dom-urlutils-hostname fn Hostname(&self) -> USVString { UrlHelper::Hostname(&self.get_url()) } + // https://url.spec.whatwg.org/#dom-urlutils-hostname + fn SetHostname(&self, value: USVString) { + self.set_url_component(value, UrlHelper::SetHostname); + } + + // https://url.spec.whatwg.org/#dom-urlutils-href + fn Href(&self) -> USVString { + UrlHelper::Href(&self.get_url()) + } + + // https://url.spec.whatwg.org/#dom-urlutils-href + fn SetHref(&self, value: USVString) -> ErrorResult { + let window = self.window.root(); + if let Ok(url) = UrlParser::new().base_url(&window.get_url()).parse(&value.0) { + window.load_url(url); + }; + Ok(()) + } + // https://url.spec.whatwg.org/#dom-urlutils-password fn Password(&self) -> USVString { UrlHelper::Password(&self.get_url()) } + // https://url.spec.whatwg.org/#dom-urlutils-password + fn SetPassword(&self, value: USVString) { + self.set_url_component(value, UrlHelper::SetPassword); + } + // https://url.spec.whatwg.org/#dom-urlutils-pathname fn Pathname(&self) -> USVString { UrlHelper::Pathname(&self.get_url()) } + // https://url.spec.whatwg.org/#dom-urlutils-pathname + fn SetPathname(&self, value: USVString) { + self.set_url_component(value, UrlHelper::SetPathname); + } + // https://url.spec.whatwg.org/#dom-urlutils-port fn Port(&self) -> USVString { UrlHelper::Port(&self.get_url()) } + // https://url.spec.whatwg.org/#dom-urlutils-port + fn SetPort(&self, value: USVString) { + self.set_url_component(value, UrlHelper::SetPort); + } + // https://url.spec.whatwg.org/#dom-urlutils-protocol fn Protocol(&self) -> USVString { UrlHelper::Protocol(&self.get_url()) } + // https://url.spec.whatwg.org/#dom-urlutils-protocol + fn SetProtocol(&self, value: USVString) { + self.set_url_component(value, UrlHelper::SetProtocol); + } + // https://url.spec.whatwg.org/#URLUtils-stringification-behavior fn Stringifier(&self) -> DOMString { self.Href().0 @@ -97,16 +154,18 @@ impl LocationMethods for Location { UrlHelper::Search(&self.get_url()) } + // https://url.spec.whatwg.org/#dom-urlutils-search + fn SetSearch(&self, value: USVString) { + self.set_url_component(value, UrlHelper::SetSearch); + } + // https://url.spec.whatwg.org/#dom-urlutils-username fn Username(&self) -> USVString { UrlHelper::Username(&self.get_url()) } -} - -impl Location { - fn get_url(&self) -> Url { - let window = self.window.root(); - window.r().get_url() + // https://url.spec.whatwg.org/#dom-urlutils-username + fn SetUsername(&self, value: USVString) { + self.set_url_component(value, UrlHelper::SetUsername); } } diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index ac21c9eefc2..f46cbc0be11 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -1629,8 +1629,7 @@ impl Node { Node::adopt(node, &*parent.owner_doc()); } // Step 2. - let mut removed_nodes = RootedVec::new(); - removed_nodes.extend(parent.children().map(|child| JS::from_rooted(&child))); + let removed_nodes = parent.children().collect::<RootedVec<_>>(); // Step 3. let mut added_nodes = RootedVec::new(); let added_nodes = if let Some(node) = node.as_ref() { diff --git a/components/script/dom/nodelist.rs b/components/script/dom/nodelist.rs index 01bc27d3a08..3e2bc5145c6 100644 --- a/components/script/dom/nodelist.rs +++ b/components/script/dom/nodelist.rs @@ -215,7 +215,10 @@ impl ChildrenList { }, }; list.last_visited.set(Some(JS::from_ref(visited))); - } else { + } else if added.len() != 1 { + // The replaced child isn't the last visited one, and there are + // 0 or more than 1 nodes to replace it. Special care must be + // given to update the state of that ChildrenList. match (prev, next) { (Some(_), None) => {}, (None, Some(next)) => { diff --git a/components/script/dom/progressevent.rs b/components/script/dom/progressevent.rs index 88a369606ae..b715330edae 100644 --- a/components/script/dom/progressevent.rs +++ b/components/script/dom/progressevent.rs @@ -52,9 +52,9 @@ impl ProgressEvent { type_: DOMString, init: &ProgressEventBinding::ProgressEventInit) -> Fallible<Root<ProgressEvent>> { - let bubbles = if init.parent.bubbles {EventBubbles::Bubbles} else {EventBubbles::DoesNotBubble}; - let cancelable = if init.parent.cancelable {EventCancelable::Cancelable} - else {EventCancelable::NotCancelable}; + let bubbles = if init.parent.bubbles { EventBubbles::Bubbles } else { EventBubbles::DoesNotBubble }; + let cancelable = if init.parent.cancelable { EventCancelable::Cancelable } + else { EventCancelable::NotCancelable }; let ev = ProgressEvent::new(global, type_, bubbles, cancelable, init.lengthComputable, init.loaded, init.total); Ok(ev) diff --git a/components/script/dom/testbinding.rs b/components/script/dom/testbinding.rs index 335e261634b..e050f799c8f 100644 --- a/components/script/dom/testbinding.rs +++ b/components/script/dom/testbinding.rs @@ -120,6 +120,10 @@ impl TestBindingMethods for TestBinding { fn SetBinaryRenamedAttribute(&self, _: DOMString) {} fn ForwardedAttribute(&self) -> Root<TestBinding> { Root::from_ref(self) } fn BinaryRenamedAttribute(&self) -> DOMString { "".to_owned() } + fn SetBinaryRenamedAttribute2(&self, _: DOMString) {} + fn BinaryRenamedAttribute2(&self) -> DOMString { "".to_owned() } + fn Attr_to_automatically_rename(&self) -> DOMString { "".to_owned() } + fn SetAttr_to_automatically_rename(&self, _: DOMString) {} fn GetEnumAttributeNullable(&self) -> Option<TestEnum> { Some(_empty) } fn GetInterfaceAttributeNullable(&self) -> Option<Root<Blob>> { let global = self.global.root(); diff --git a/components/script/dom/testbindingproxy.rs b/components/script/dom/testbindingproxy.rs index 3c1f23c8ae1..5ca27eae735 100644 --- a/components/script/dom/testbindingproxy.rs +++ b/components/script/dom/testbindingproxy.rs @@ -15,20 +15,20 @@ pub struct TestBindingProxy { } impl TestBindingProxyMethods for TestBindingProxy { - fn Length(&self) -> u32 {0} - fn SupportedPropertyNames(&self) -> Vec<DOMString> {vec![]} - fn GetNamedItem(&self, _: DOMString) -> DOMString {"".to_owned()} + fn Length(&self) -> u32 { 0 } + fn SupportedPropertyNames(&self) -> Vec<DOMString> { vec![] } + fn GetNamedItem(&self, _: DOMString) -> DOMString { "".to_owned() } fn SetNamedItem(&self, _: DOMString, _: DOMString) -> () {} - fn GetItem(&self, _: u32) -> DOMString {"".to_owned()} + fn GetItem(&self, _: u32) -> DOMString { "".to_owned() } fn SetItem(&self, _: u32, _: DOMString) -> () {} fn RemoveItem(&self, _: DOMString) -> () {} - fn Stringifier(&self) -> DOMString {"".to_owned()} + fn Stringifier(&self) -> DOMString { "".to_owned() } fn NamedCreator(&self, _: DOMString, _: DOMString) -> () {} - fn IndexedGetter(&self, _: u32, _: &mut bool) -> DOMString {"".to_owned()} + fn IndexedGetter(&self, _: u32, _: &mut bool) -> DOMString { "".to_owned() } fn NamedDeleter(&self, _: DOMString) -> () {} fn IndexedSetter(&self, _: u32, _: DOMString) -> () {} fn NamedSetter(&self, _: DOMString, _: DOMString) -> () {} fn IndexedCreator(&self, _: u32, _: DOMString) -> () {} - fn NamedGetter(&self, _: DOMString, _: &mut bool) -> DOMString {"".to_owned()} + fn NamedGetter(&self, _: DOMString, _: &mut bool) -> DOMString { "".to_owned() } } diff --git a/components/script/dom/url.rs b/components/script/dom/url.rs index ec44fe2fdf9..f34428d4bbb 100644 --- a/components/script/dom/url.rs +++ b/components/script/dom/url.rs @@ -3,17 +3,18 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::codegen::Bindings::URLBinding::{self, URLMethods}; -use dom::bindings::error::{Error, Fallible}; +use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::global::GlobalRef; use dom::bindings::js::Root; use dom::bindings::str::USVString; use dom::bindings::utils::{Reflector, reflect_dom_object}; use dom::urlhelper::UrlHelper; -use url::{Host, Url, UrlParser}; +use url::{Host, ParseResult, Url, UrlParser}; use util::str::DOMString; use std::borrow::ToOwned; +use std::cell::RefCell; // https://url.spec.whatwg.org/#url #[dom_struct] @@ -21,19 +22,23 @@ pub struct URL { reflector_: Reflector, // https://url.spec.whatwg.org/#concept-urlutils-url - url: Url, + url: RefCell<Url>, + + // https://url.spec.whatwg.org/#concept-urlutils-get-the-base + base: Option<Url>, } impl URL { - fn new_inherited(url: Url) -> URL { + fn new_inherited(url: Url, base: Option<Url>) -> URL { URL { reflector_: Reflector::new(), - url: url, + url: RefCell::new(url), + base: base, } } - pub fn new(global: GlobalRef, url: Url) -> Root<URL> { - reflect_dom_object(box URL::new_inherited(url), + pub fn new(global: GlobalRef, url: Url, base: Option<Url>) -> Root<URL> { + reflect_dom_object(box URL::new_inherited(url, base), global, URLBinding::Wrap) } } @@ -59,7 +64,7 @@ impl URL { } }; // Step 3. - let parsed_url = match parser_with_base(parsed_base.as_ref()).parse(&url.0) { + let parsed_url = match parse_with_base(url, parsed_base.as_ref()) { Ok(url) => url, Err(error) => { // Step 4. @@ -67,7 +72,7 @@ impl URL { } }; // Steps 5-8. - Ok(URL::new(global, parsed_url)) + Ok(URL::new(global, parsed_url, parsed_base)) } // https://url.spec.whatwg.org/#dom-url-domaintoasciidomain @@ -87,47 +92,100 @@ impl URL { impl URLMethods for URL { // https://url.spec.whatwg.org/#dom-urlutils-hash fn Hash(&self) -> USVString { - UrlHelper::Hash(&self.url) + UrlHelper::Hash(&self.url.borrow()) + } + + // https://url.spec.whatwg.org/#dom-urlutils-hash + fn SetHash(&self, value: USVString) { + UrlHelper::SetHash(&mut self.url.borrow_mut(), value); } // https://url.spec.whatwg.org/#dom-urlutils-host fn Host(&self) -> USVString { - UrlHelper::Host(&self.url) + UrlHelper::Host(&self.url.borrow()) + } + + // https://url.spec.whatwg.org/#dom-urlutils-host + fn SetHost(&self, value: USVString) { + UrlHelper::SetHost(&mut self.url.borrow_mut(), value); } // https://url.spec.whatwg.org/#dom-urlutils-hostname fn Hostname(&self) -> USVString { - UrlHelper::Hostname(&self.url) + UrlHelper::Hostname(&self.url.borrow()) + } + + // https://url.spec.whatwg.org/#dom-urlutils-hostname + fn SetHostname(&self, value: USVString) { + UrlHelper::SetHostname(&mut self.url.borrow_mut(), value); } // https://url.spec.whatwg.org/#dom-urlutils-href fn Href(&self) -> USVString { - UrlHelper::Href(&self.url) + UrlHelper::Href(&self.url.borrow()) + } + + // https://url.spec.whatwg.org/#dom-urlutils-href + fn SetHref(&self, value: USVString) -> ErrorResult { + match parse_with_base(value, self.base.as_ref()) { + Ok(url) => { + *self.url.borrow_mut() = url; + Ok(()) + }, + Err(error) => { + Err(Error::Type(format!("could not parse URL: {}", error))) + }, + } } // https://url.spec.whatwg.org/#dom-urlutils-password fn Password(&self) -> USVString { - UrlHelper::Password(&self.url) + UrlHelper::Password(&self.url.borrow()) + } + + // https://url.spec.whatwg.org/#dom-urlutils-password + fn SetPassword(&self, value: USVString) { + UrlHelper::SetPassword(&mut self.url.borrow_mut(), value); } // https://url.spec.whatwg.org/#dom-urlutils-pathname fn Pathname(&self) -> USVString { - UrlHelper::Pathname(&self.url) + UrlHelper::Pathname(&self.url.borrow()) + } + + // https://url.spec.whatwg.org/#dom-urlutils-pathname + fn SetPathname(&self, value: USVString) { + UrlHelper::SetPathname(&mut self.url.borrow_mut(), value); } // https://url.spec.whatwg.org/#dom-urlutils-port fn Port(&self) -> USVString { - UrlHelper::Port(&self.url) + UrlHelper::Port(&self.url.borrow()) + } + + // https://url.spec.whatwg.org/#dom-urlutils-port + fn SetPort(&self, value: USVString) { + UrlHelper::SetPort(&mut self.url.borrow_mut(), value); } // https://url.spec.whatwg.org/#dom-urlutils-protocol fn Protocol(&self) -> USVString { - UrlHelper::Protocol(&self.url) + UrlHelper::Protocol(&self.url.borrow()) + } + + // https://url.spec.whatwg.org/#dom-urlutils-protocol + fn SetProtocol(&self, value: USVString) { + UrlHelper::SetProtocol(&mut self.url.borrow_mut(), value); } // https://url.spec.whatwg.org/#dom-urlutils-search fn Search(&self) -> USVString { - UrlHelper::Search(&self.url) + UrlHelper::Search(&self.url.borrow()) + } + + // https://url.spec.whatwg.org/#dom-urlutils-search + fn SetSearch(&self, value: USVString) { + UrlHelper::SetSearch(&mut self.url.borrow_mut(), value); } // https://url.spec.whatwg.org/#URLUtils-stringification-behavior @@ -137,14 +195,19 @@ impl URLMethods for URL { // https://url.spec.whatwg.org/#dom-urlutils-username fn Username(&self) -> USVString { - UrlHelper::Username(&self.url) + UrlHelper::Username(&self.url.borrow()) + } + + // https://url.spec.whatwg.org/#dom-urlutils-username + fn SetUsername(&self, value: USVString) { + UrlHelper::SetUsername(&mut self.url.borrow_mut(), value); } } -fn parser_with_base(base: Option<&Url>) -> UrlParser { +fn parse_with_base(input: USVString, base: Option<&Url>) -> ParseResult<Url> { let mut parser = UrlParser::new(); if let Some(base) = base { parser.base_url(base); } - parser + parser.parse(&input.0) } diff --git a/components/script/dom/urlhelper.rs b/components/script/dom/urlhelper.rs index 4376a85e390..684d6666e0c 100644 --- a/components/script/dom/urlhelper.rs +++ b/components/script/dom/urlhelper.rs @@ -4,7 +4,9 @@ use dom::bindings::str::USVString; -use url::{Url, SchemeData}; +use url::{Url, UrlParser, SchemeData}; + +use url::urlutils::{UrlUtils, UrlUtilsWrapper}; use std::borrow::ToOwned; use std::fmt::Write; @@ -22,6 +24,12 @@ impl UrlHelper { }) } + // https://url.spec.whatwg.org/#dom-urlutils-hash + pub fn SetHash(url: &mut Url, value: USVString) { + let mut wrapper = UrlUtilsWrapper { url: url, parser: &UrlParser::new() }; + let _ = wrapper.set_fragment(&value.0); + } + // https://url.spec.whatwg.org/#dom-urlutils-host pub fn Host(url: &Url) -> USVString { USVString(match url.scheme_data { @@ -36,11 +44,23 @@ impl UrlHelper { }) } + // https://url.spec.whatwg.org/#dom-urlutils-host + pub fn SetHost(url: &mut Url, value: USVString) { + let mut wrapper = UrlUtilsWrapper { url: url, parser: &UrlParser::new() }; + let _ = wrapper.set_host(&value.0); + } + // https://url.spec.whatwg.org/#dom-urlutils-hostname pub fn Hostname(url: &Url) -> USVString { USVString(url.serialize_host().unwrap_or_else(|| "".to_owned())) } + // https://url.spec.whatwg.org/#dom-urlutils-hostname + pub fn SetHostname(url: &mut Url, value: USVString) { + let mut wrapper = UrlUtilsWrapper { url: url, parser: &UrlParser::new() }; + let _ = wrapper.set_host_and_port(&value.0); + } + // https://url.spec.whatwg.org/#dom-urlutils-href pub fn Href(url: &Url) -> USVString { USVString(url.serialize()) @@ -51,15 +71,26 @@ impl UrlHelper { USVString(url.password().unwrap_or("").to_owned()) } + // https://url.spec.whatwg.org/#dom-urlutils-password + pub fn SetPassword(url: &mut Url, value: USVString) { + let mut wrapper = UrlUtilsWrapper { url: url, parser: &UrlParser::new() }; + let _ = wrapper.set_password(&value.0); + } + // https://url.spec.whatwg.org/#dom-urlutils-pathname pub fn Pathname(url: &Url) -> USVString { - // FIXME: Url null check is skipped for now USVString(match url.scheme_data { SchemeData::NonRelative(ref scheme_data) => scheme_data.clone(), SchemeData::Relative(..) => url.serialize_path().unwrap() }) } + // https://url.spec.whatwg.org/#dom-urlutils-pathname + pub fn SetPathname(url: &mut Url, value: USVString) { + let mut wrapper = UrlUtilsWrapper { url: url, parser: &UrlParser::new() }; + let _ = wrapper.set_path(&value.0); + } + // https://url.spec.whatwg.org/#dom-urlutils-port pub fn Port(url: &Url) -> USVString { USVString(match url.port() { @@ -68,11 +99,23 @@ impl UrlHelper { }) } + // https://url.spec.whatwg.org/#dom-urlutils-port + pub fn SetPort(url: &mut Url, value: USVString) { + let mut wrapper = UrlUtilsWrapper { url: url, parser: &UrlParser::new() }; + let _ = wrapper.set_port(&value.0); + } + // https://url.spec.whatwg.org/#dom-urlutils-protocol pub fn Protocol(url: &Url) -> USVString { USVString(format!("{}:", url.scheme.clone())) } + // https://url.spec.whatwg.org/#dom-urlutils-protocol + pub fn SetProtocol(url: &mut Url, value: USVString) { + let mut wrapper = UrlUtilsWrapper { url: url, parser: &UrlParser::new() }; + let _ = wrapper.set_scheme(&value.0); + } + // https://html.spec.whatwg.org/multipage/#same-origin pub fn SameOrigin(urlA: &Url, urlB: &Url) -> bool { if urlA.host() != urlB.host() { @@ -96,8 +139,20 @@ impl UrlHelper { }) } + // https://url.spec.whatwg.org/#dom-urlutils-search + pub fn SetSearch(url: &mut Url, value: USVString) { + let mut wrapper = UrlUtilsWrapper { url: url, parser: &UrlParser::new() }; + let _ = wrapper.set_query(&value.0); + } + // https://url.spec.whatwg.org/#dom-urlutils-username pub fn Username(url: &Url) -> USVString { USVString(url.username().unwrap_or("").to_owned()) } + + // https://url.spec.whatwg.org/#dom-urlutils-username + pub fn SetUsername(url: &mut Url, value: USVString) { + let mut wrapper = UrlUtilsWrapper { url: url, parser: &UrlParser::new() }; + let _ = wrapper.set_username(&value.0); + } } diff --git a/components/script/dom/webglshader.rs b/components/script/dom/webglshader.rs index 3920bcb7b7c..bcad061853f 100644 --- a/components/script/dom/webglshader.rs +++ b/components/script/dom/webglshader.rs @@ -11,10 +11,19 @@ use dom::webglobject::WebGLObject; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; +use angle::hl::{BuiltInResources, Output, ShaderValidator}; use canvas_traits::{CanvasMsg, CanvasWebGLMsg, WebGLResult, WebGLError, WebGLShaderParameter}; use ipc_channel::ipc::{self, IpcSender}; use std::cell::Cell; use std::cell::RefCell; +use std::sync::{Once, ONCE_INIT}; + +#[derive(Clone, Copy, PartialEq, Debug, JSTraceable, HeapSizeOf)] +pub enum ShaderCompilationStatus { + NotCompiled, + Succeeded, + Failed, +} #[dom_struct] pub struct WebGLShader { @@ -22,20 +31,32 @@ pub struct WebGLShader { id: u32, gl_type: u32, source: RefCell<Option<String>>, + info_log: RefCell<Option<String>>, is_deleted: Cell<bool>, - // TODO(ecoal95): Evaluate moving this to `WebGLObject` + compilation_status: Cell<ShaderCompilationStatus>, #[ignore_heap_size_of = "Defined in ipc-channel"] renderer: IpcSender<CanvasMsg>, } +#[cfg(not(target_os = "android"))] +const SHADER_OUTPUT_FORMAT: Output = Output::Glsl; + +#[cfg(target_os = "android")] +const SHADER_OUTPUT_FORMAT: Output = Output::Essl; + +static GLSLANG_INITIALIZATION: Once = ONCE_INIT; + impl WebGLShader { fn new_inherited(renderer: IpcSender<CanvasMsg>, id: u32, shader_type: u32) -> WebGLShader { + GLSLANG_INITIALIZATION.call_once(|| ::angle::hl::initialize().unwrap()); WebGLShader { webgl_object: WebGLObject::new_inherited(), id: id, gl_type: shader_type, source: RefCell::new(None), + info_log: RefCell::new(None), is_deleted: Cell::new(false), + compilation_status: Cell::new(ShaderCompilationStatus::NotCompiled), renderer: renderer, } } @@ -69,10 +90,33 @@ impl WebGLShader { self.gl_type } - // TODO(ecoal95): Validate shaders to be conforming to the WebGL spec /// glCompileShader pub fn compile(&self) { - self.renderer.send(CanvasMsg::WebGL(CanvasWebGLMsg::CompileShader(self.id))).unwrap() + if self.compilation_status.get() != ShaderCompilationStatus::NotCompiled { + debug!("Compiling already compiled shader {}", self.id); + } + + if let Some(ref source) = *self.source.borrow() { + let validator = ShaderValidator::for_webgl(self.gl_type, + SHADER_OUTPUT_FORMAT, + &BuiltInResources::default()).unwrap(); + match validator.compile_and_translate(&[source.as_bytes()]) { + Ok(translated_source) => { + // NOTE: At this point we should be pretty sure that the compilation in the paint task + // will succeed. + // It could be interesting to retrieve the info log from the paint task though + let msg = CanvasWebGLMsg::CompileShader(self.id, translated_source); + self.renderer.send(CanvasMsg::WebGL(msg)).unwrap(); + self.compilation_status.set(ShaderCompilationStatus::Succeeded); + }, + Err(error) => { + self.compilation_status.set(ShaderCompilationStatus::Failed); + debug!("Shader {} compilation failed: {}", self.id, error); + }, + } + + *self.info_log.borrow_mut() = Some(validator.info_log()); + } } /// Mark this shader as deleted (if it wasn't previously) @@ -86,9 +130,7 @@ impl WebGLShader { /// glGetShaderInfoLog pub fn info_log(&self) -> Option<String> { - let (sender, receiver) = ipc::channel().unwrap(); - self.renderer.send(CanvasMsg::WebGL(CanvasWebGLMsg::GetShaderInfoLog(self.id, sender))).unwrap(); - receiver.recv().unwrap() + self.info_log.borrow().clone() } /// glGetShaderParameter @@ -110,7 +152,6 @@ impl WebGLShader { /// glShaderSource pub fn set_source(&self, source: String) { - *self.source.borrow_mut() = Some(source.clone()); - self.renderer.send(CanvasMsg::WebGL(CanvasWebGLMsg::ShaderSource(self.id, source))).unwrap() + *self.source.borrow_mut() = Some(source); } } diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl index 014e12740bf..dba69a515b0 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -34,40 +34,73 @@ interface CSSStyleDeclaration { partial interface CSSStyleDeclaration { [SetterThrows, TreatNullAs=EmptyString] attribute DOMString background; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString backgroundColor; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString background-color; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString backgroundPosition; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString background-position; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString backgroundRepeat; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString background-repeat; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString backgroundImage; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString background-image; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString backgroundAttachment; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString background-attachment; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString backgroundSize; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString background-size; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString backgroundOrigin; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString background-origin; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString backgroundClip; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString background-clip; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderColor; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-color; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderRadius; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-radius; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderSpacing; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-spacing; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderStyle; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-style; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderWidth; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-width; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderBottom; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-bottom; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderBottomColor; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-bottom-color; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderBottomLeftRadius; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-bottom-left-radius; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderBottomRightRadius; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-bottom-right-radius; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderBottomStyle; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-bottom-style; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderBottomWidth; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-bottom-width; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderLeft; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-left; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderLeftColor; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-left-color; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderLeftStyle; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-left-style; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderLeftWidth; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-left-width; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderRight; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-right; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderRightColor; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-right-color; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderRightStyle; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-right-style; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderRightWidth; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-right-width; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderTop; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-top; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderTopColor; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-top-color; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderTopLeftRadius; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-top-left-radius; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderTopRightRadius; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-top-right-radius; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderTopStyle; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-top-style; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderTopWidth; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-top-width; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString content; @@ -82,8 +115,11 @@ partial interface CSSStyleDeclaration { [SetterThrows, TreatNullAs=EmptyString] attribute DOMString cursor; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString boxSizing; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString box-sizing; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString boxShadow; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString box-shadow; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString textShadow; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString text-shadow; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString _float; @@ -93,89 +129,143 @@ partial interface CSSStyleDeclaration { [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transform; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transformOrigin; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transform-origin; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString perspective; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString perspectiveOrigin; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString perspective-origin; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transformStyle; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transform-style; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString backfaceVisibility; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString backface-visibility; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString direction; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString unicodeBidi; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString unicode-bidi; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString filter; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString lineHeight; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString line-height; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString mixBlendMode; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString mix-blend-mode; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString verticalAlign; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString vertical-align; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString listStyle; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString list-style; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString listStylePosition; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString list-style-position; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString listStyleType; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString list-style-type; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString listStyleImage; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString list-style-image; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString quotes; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString counterIncrement; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString counter-increment; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString counterReset; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString counter-reset; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString overflow; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString overflowX; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString overflow-x; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString overflowY; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString overflow-y; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString overflowWrap; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString overflow-wrap; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString tableLayout; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString table-layout; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderCollapse; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-collapse; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString emptyCells; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString empty-cells; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString captionSide; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString caption-side; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString whiteSpace; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString white-space; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString writingMode; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString writing-mode; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString letterSpacing; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString letter-spacing; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString wordBreak; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString word-break; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString wordSpacing; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString word-spacing; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString wordWrap; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString word-wrap; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString textOverflow; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString text-overflow; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString textAlign; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString text-align; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString textDecoration; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString text-decoration; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString textIndent; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString text-indent; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString textJustify; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString text-justify; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString textOrientation; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString text-orientation; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString textRendering; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString text-rendering; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString textTransform; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString text-transform; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString font; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString fontFamily; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString font-family; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString fontSize; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString font-size; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString fontStretch; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString font-stretch; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString fontStyle; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString font-style; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString fontVariant; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString font-variant; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString fontWeight; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString font-weight; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString margin; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString marginBottom; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString margin-bottom; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString marginLeft; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString margin-left; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString marginRight; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString margin-right; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString marginTop; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString margin-top; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString padding; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString paddingBottom; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString padding-bottom; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString paddingLeft; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString padding-left; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString paddingRight; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString padding-right; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString paddingTop; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString padding-top; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString outline; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString outlineColor; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString outline-color; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString outlineStyle; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString outline-style; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString outlineWidth; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString outline-width; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString outlineOffset; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString outline-offset; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString position; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString pointerEvents; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString pointer-events; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString top; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString right; @@ -184,26 +274,40 @@ partial interface CSSStyleDeclaration { [SetterThrows, TreatNullAs=EmptyString] attribute DOMString height; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString minHeight; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString min-height; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString maxHeight; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString max-height; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString width; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString minWidth; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString min-width; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString maxWidth; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString max-width; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString zIndex; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString z-index; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString imageRendering; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString image-rendering; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString columnCount; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString column-count; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString columnWidth; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString column-width; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString columns; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString columnGap; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString column-gap; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transition; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transitionDuration; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transition-duration; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transitionTimingFunction; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transition-timing-function; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transitionProperty; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transition-property; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transitionDelay; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString transition-delay; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString flexDirection; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString flex-direction; }; diff --git a/components/script/dom/webidls/TestBinding.webidl b/components/script/dom/webidls/TestBinding.webidl index 08221f2ccc0..181909721aa 100644 --- a/components/script/dom/webidls/TestBinding.webidl +++ b/components/script/dom/webidls/TestBinding.webidl @@ -115,6 +115,8 @@ interface TestBinding { attribute (HTMLElement or long)? unionAttributeNullable; attribute (Event or DOMString)? union2AttributeNullable; [BinaryName="BinaryRenamedAttribute"] attribute DOMString attrToBinaryRename; + [BinaryName="BinaryRenamedAttribute2"] attribute DOMString attr-to-binary-rename; + attribute DOMString attr-to-automatically-rename; [PutForwards=booleanAttribute] readonly attribute TestBinding forwardedAttribute; diff --git a/components/script/dom/webidls/URLUtils.webidl b/components/script/dom/webidls/URLUtils.webidl index 86e5140311b..1b8965f3d37 100644 --- a/components/script/dom/webidls/URLUtils.webidl +++ b/components/script/dom/webidls/URLUtils.webidl @@ -7,27 +7,19 @@ [NoInterfaceObject] interface URLUtils { //stringifier attribute USVString href; - readonly attribute USVString href; + [SetterThrows] + attribute USVString href; //readonly attribute USVString origin; - // attribute USVString protocol; - readonly attribute USVString protocol; - // attribute USVString username; - readonly attribute USVString username; - // attribute USVString password; - readonly attribute USVString password; - // attribute USVString host; - readonly attribute USVString host; - // attribute USVString hostname; - readonly attribute USVString hostname; - // attribute USVString port; - readonly attribute USVString port; - // attribute USVString pathname; - readonly attribute USVString pathname; - // attribute USVString search; - readonly attribute USVString search; + attribute USVString protocol; + attribute USVString username; + attribute USVString password; + attribute USVString host; + attribute USVString hostname; + attribute USVString port; + attribute USVString pathname; + attribute USVString search; // attribute URLSearchParams searchParams; - // attribute USVString hash; - readonly attribute USVString hash; + attribute USVString hash; // This is only doing as well as gecko right now, bug 824857 is on file for // adding attribute stringifier support. diff --git a/components/script/dom/webidls/Worker.webidl b/components/script/dom/webidls/Worker.webidl index 9e5d2c36ad9..481c2d44849 100644 --- a/components/script/dom/webidls/Worker.webidl +++ b/components/script/dom/webidls/Worker.webidl @@ -6,7 +6,7 @@ // https://www.whatwg.org/html/#abstractworker [NoInterfaceObject/*, Exposed=Window,Worker*/] interface AbstractWorker { - // attribute EventHandler onerror; + attribute EventHandler onerror; }; // https://www.whatwg.org/html/#worker diff --git a/components/script/dom/websocket.rs b/components/script/dom/websocket.rs index 25b8d832fe4..4b614786efa 100644 --- a/components/script/dom/websocket.rs +++ b/components/script/dom/websocket.rs @@ -228,9 +228,16 @@ impl WebSocket { } impl WebSocketMethods for WebSocket { + // https://html.spec.whatwg.org/multipage/#handler-websocket-onopen event_handler!(open, GetOnopen, SetOnopen); + + // https://html.spec.whatwg.org/multipage/#handler-websocket-onclose event_handler!(close, GetOnclose, SetOnclose); + + // https://html.spec.whatwg.org/multipage/#handler-websocket-onerror event_handler!(error, GetOnerror, SetOnerror); + + // https://html.spec.whatwg.org/multipage/#handler-websocket-onmessage event_handler!(message, GetOnmessage, SetOnmessage); // https://html.spec.whatwg.org/multipage/#dom-websocket-url diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 8bce4247a4a..c463efa9f69 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -40,7 +40,6 @@ use timers::{IsInterval, TimerId, TimerManager, TimerCallback}; use webdriver_handlers::jsval_to_webdriver; use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType}; -use devtools_traits::{TracingMetadata}; use msg::compositor_msg::ScriptToCompositorMsg; use msg::constellation_msg::{LoadData, PipelineId, SubpageId, ConstellationChan, WindowSizeData, WorkerId}; use msg::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; @@ -504,8 +503,13 @@ impl WindowMethods for Window { }) } + // https://html.spec.whatwg.org/multipage/#globaleventhandlers global_event_handlers!(); + + // https://html.spec.whatwg.org/multipage/#handler-window-onunload event_handler!(unload, GetOnunload, SetOnunload); + + // https://html.spec.whatwg.org/multipage/#handler-onerror error_event_handler!(error, GetOnerror, SetOnerror); // https://developer.mozilla.org/en-US/docs/Web/API/Window/screen @@ -681,10 +685,11 @@ impl Window { debug!("script: performing reflow for goal {:?} reason {:?}", goal, reason); - if self.need_emit_timeline_marker(TimelineMarkerType::Reflow) { - let marker = TimelineMarker::new("Reflow".to_owned(), TracingMetadata::IntervalStart); - self.emit_timeline_marker(marker); - } + let marker = if self.need_emit_timeline_marker(TimelineMarkerType::Reflow) { + Some(TimelineMarker::start("Reflow".to_owned())) + } else { + None + }; // Layout will let us know when it's done. let (join_chan, join_port) = channel(); @@ -725,9 +730,8 @@ impl Window { self.pending_reflow_count.set(0); - if self.need_emit_timeline_marker(TimelineMarkerType::Reflow) { - let marker = TimelineMarker::new("Reflow".to_owned(), TracingMetadata::IntervalEnd); - self.emit_timeline_marker(marker); + if let Some(marker) = marker { + self.emit_timeline_marker(marker.end()); } } diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs index 435de83ecff..2711e8502db 100644 --- a/components/script/dom/worker.rs +++ b/components/script/dom/worker.rs @@ -162,7 +162,11 @@ impl WorkerMethods for Worker { Ok(()) } + // https://html.spec.whatwg.org/multipage/#handler-dedicatedworkerglobalscope-onmessage event_handler!(message, GetOnmessage, SetOnmessage); + + // https://html.spec.whatwg.org/multipage/#handler-workerglobalscope-onerror + event_handler!(error, GetOnerror, SetOnerror); } pub struct WorkerMessageHandler { diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index 1d7a097fd9e..5ab625c1d48 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -293,6 +293,7 @@ impl XMLHttpRequest { } impl XMLHttpRequestMethods for XMLHttpRequest { + // https://xhr.spec.whatwg.org/#handler-xhr-onreadystatechange event_handler!(readystatechange, GetOnreadystatechange, SetOnreadystatechange); // https://xhr.spec.whatwg.org/#dom-xmlhttprequest-readystate @@ -959,7 +960,7 @@ impl XMLHttpRequest { fn dispatch_response_progress_event(&self, type_: DOMString) { let len = self.response.borrow().len() as u64; - let total = self.response_headers.borrow().get::<ContentLength>().map(|x| {**x as u64}); + let total = self.response_headers.borrow().get::<ContentLength>().map(|x| { **x as u64 }); self.dispatch_progress_event(false, type_, len, total); } fn set_timeout(&self, duration_ms: u32) { diff --git a/components/script/dom/xmlhttprequesteventtarget.rs b/components/script/dom/xmlhttprequesteventtarget.rs index 0e443d65041..627e4882dde 100644 --- a/components/script/dom/xmlhttprequesteventtarget.rs +++ b/components/script/dom/xmlhttprequesteventtarget.rs @@ -38,11 +38,24 @@ impl XMLHttpRequestEventTargetDerived for EventTarget { } impl XMLHttpRequestEventTargetMethods for XMLHttpRequestEventTarget { + // https://xhr.spec.whatwg.org/#handler-xhr-onloadstart event_handler!(loadstart, GetOnloadstart, SetOnloadstart); + + // https://xhr.spec.whatwg.org/#handler-xhr-onprogress event_handler!(progress, GetOnprogress, SetOnprogress); + + // https://xhr.spec.whatwg.org/#handler-xhr-onabort event_handler!(abort, GetOnabort, SetOnabort); + + // https://xhr.spec.whatwg.org/#handler-xhr-onerror event_handler!(error, GetOnerror, SetOnerror); + + // https://xhr.spec.whatwg.org/#handler-xhr-onload event_handler!(load, GetOnload, SetOnload); + + // https://xhr.spec.whatwg.org/#handler-xhr-ontimeout event_handler!(timeout, GetOntimeout, SetOntimeout); + + // https://xhr.spec.whatwg.org/#handler-xhr-onloadend event_handler!(loadend, GetOnloadend, SetOnloadend); } diff --git a/components/script/lib.rs b/components/script/lib.rs index bfe02d56cdc..ab2f0b94669 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -33,7 +33,7 @@ #![deny(unsafe_code)] #![allow(non_snake_case)] -#![doc="The script crate contains all matters DOM."] +#![doc = "The script crate contains all matters DOM."] #![plugin(string_cache_plugin)] #![plugin(plugins)] @@ -78,6 +78,7 @@ extern crate url; extern crate uuid; extern crate string_cache; extern crate offscreen_gl_context; +extern crate angle; extern crate tendril; pub mod cors; @@ -102,7 +103,7 @@ mod webdriver_handlers; use dom::bindings::codegen::RegisterBindings; -#[cfg(target_os="linux")] +#[cfg(target_os = "linux")] #[allow(unsafe_code)] fn perform_platform_specific_initialization() { use std::mem; @@ -118,7 +119,7 @@ fn perform_platform_specific_initialization() { } } -#[cfg(not(target_os="linux"))] +#[cfg(not(target_os = "linux"))] fn perform_platform_specific_initialization() {} #[allow(unsafe_code)] diff --git a/components/script/makefile.cargo b/components/script/makefile.cargo index 9fc604382fc..fbc8533ee57 100644 --- a/components/script/makefile.cargo +++ b/components/script/makefile.cargo @@ -10,6 +10,8 @@ WEBIDLS = $(call rwildcard,$(WEBIDLS_SRC),*.webidl) BINDINGS = $(patsubst %.webidl,%Binding.rs,$(WEBIDLS)) AUTOGEN_SRC = $(foreach var,$(BINDINGS),$(subst $(WEBIDLS_SRC),$(OUT_DIR)/Bindings,$(var))) +export PYTHONPATH := $(BINDINGS_SRC)/parser:$(BINDINGS_SRC)/ply:$(PYTHONPATH) + CACHE_DIR = $(OUT_DIR)/_cache bindinggen_dependencies := $(addprefix $(BINDINGS_SRC)/,BindingGen.py Bindings.conf Configuration.py CodegenRust.py parser/WebIDL.py) $(OUT_DIR)/ParserResults.pkl $(OUT_DIR)/Bindings/.done @@ -28,18 +30,19 @@ $(CACHE_DIR)/.done: touch $@ $(OUT_DIR)/ParserResults.pkl: $(globalgen_dependencies) $(WEBIDLS) - $(PYTHON) -B $(BINDINGS_SRC)/pythonpath.py \ - -I$(BINDINGS_SRC)/parser -I$(BINDINGS_SRC)/ply \ - -D$(OUT_DIR) \ - $(BINDINGS_SRC)/GlobalGen.py $(BINDINGS_SRC)/Bindings.conf . \ - --cachedir=$(CACHE_DIR) \ - $(WEBIDLS) + $(PYTHON) \ + $(BINDINGS_SRC)/GlobalGen.py \ + --cachedir=$(CACHE_DIR) \ + $(BINDINGS_SRC)/Bindings.conf \ + $(OUT_DIR) \ + . \ + $(WEBIDLS) $(AUTOGEN_SRC): $(OUT_DIR)/Bindings/%Binding.rs: $(bindinggen_dependencies) \ $(addprefix $(WEBIDLS_SRC)/,%.webidl) - $(PYTHON) -B $(BINDINGS_SRC)/pythonpath.py \ - -I$(BINDINGS_SRC)/parser -I$(BINDINGS_SRC)/ply \ - -D$(OUT_DIR) \ + $(PYTHON) \ $(BINDINGS_SRC)/BindingGen.py \ - $(BINDINGS_SRC)/Bindings.conf Bindings/$*Binding $(addprefix $(WEBIDLS_SRC)/,$*.webidl) + $(BINDINGS_SRC)/Bindings.conf \ + $(OUT_DIR)/Bindings/$*Binding \ + $(addprefix $(WEBIDLS_SRC)/,$*.webidl) touch $@ diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 5ae982bbee5..1ab86d67570 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -52,8 +52,8 @@ use timers::TimerId; use webdriver_handlers; use devtools_traits::{DevtoolsPageInfo, DevtoolScriptControlMsg}; -use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType}; -use devtools_traits::{TracingMetadata}; +use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker}; +use devtools_traits::{StartedTimelineMarker, TimelineMarkerType}; use msg::compositor_msg::{LayerId, ScriptToCompositorMsg}; use msg::constellation_msg::Msg as ConstellationMsg; use msg::constellation_msg::{ConstellationChan, FocusType}; @@ -2016,23 +2016,22 @@ impl Drop for ScriptTask { } struct AutoDOMEventMarker<'a> { - script_task: &'a ScriptTask + script_task: &'a ScriptTask, + marker: Option<StartedTimelineMarker>, } impl<'a> AutoDOMEventMarker<'a> { fn new(script_task: &'a ScriptTask) -> AutoDOMEventMarker<'a> { - let marker = TimelineMarker::new("DOMEvent".to_owned(), TracingMetadata::IntervalStart); - script_task.emit_timeline_marker(marker); AutoDOMEventMarker { - script_task: script_task + script_task: script_task, + marker: Some(TimelineMarker::start("DOMEvent".to_owned())), } } } impl<'a> Drop for AutoDOMEventMarker<'a> { fn drop(&mut self) { - let marker = TimelineMarker::new("DOMEvent".to_owned(), TracingMetadata::IntervalEnd); - self.script_task.emit_timeline_marker(marker); + self.script_task.emit_timeline_marker(self.marker.take().unwrap().end()); } } diff --git a/components/script/textinput.rs b/components/script/textinput.rs index 0f7164d5fa0..c647f63d4d8 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -78,12 +78,12 @@ pub enum Direction { /// Was the keyboard event accompanied by the standard control modifier, /// i.e. cmd on Mac OS or ctrl on other platforms. -#[cfg(target_os="macos")] +#[cfg(target_os = "macos")] fn is_control_key(mods: KeyModifiers) -> bool { mods.contains(SUPER) && !mods.contains(CONTROL | ALT) } -#[cfg(not(target_os="macos"))] +#[cfg(not(target_os = "macos"))] fn is_control_key(mods: KeyModifiers) -> bool { mods.contains(CONTROL) && !mods.contains(SUPER | ALT) } diff --git a/components/script/timers.rs b/components/script/timers.rs index f18b2231417..8fd712e7ac6 100644 --- a/components/script/timers.rs +++ b/components/script/timers.rs @@ -183,7 +183,7 @@ impl TimerManager { if is_interval == IsInterval::NonInterval { break; } - } else if id == control_handle.id() {; + } else if id == control_handle.id() { match control_port.recv().unwrap() { TimerControlMsg::Suspend => { let msg = control_port.recv().unwrap(); @@ -194,7 +194,7 @@ impl TimerManager { break; }, } - }, + }, TimerControlMsg::Resume => panic!("Nothing to resume!"), TimerControlMsg::Cancel => { break; diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 77e83ecf130..af8c32f26b0 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -4,13 +4,18 @@ version = "0.0.1" dependencies = [ "android_glue 0.0.2", "bitflags 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "canvas 0.0.1", + "canvas_traits 0.0.1", "compositing 0.0.1", "devtools 0.0.1", "devtools_traits 0.0.1", "env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "gfx 0.0.1", "gfx_tests 0.0.1", + "gleam 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "glutin_app 0.0.1", + "layers 0.1.0 (git+https://github.com/servo/rust-layers)", "layout 0.0.1", "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", @@ -22,6 +27,8 @@ dependencies = [ "profile_traits 0.0.1", "script 0.0.1", "script_tests 0.0.1", + "script_traits 0.0.1", + "style 0.0.1", "style_tests 0.0.1", "time 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)", @@ -57,6 +64,14 @@ version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "angle" +version = "0.1.0" +source = "git+https://github.com/ecoal95/angle?branch=servo#77288884bd7a89bf3019ca94dcda6c94cb178aa4" +dependencies = [ + "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "aster" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -161,7 +176,7 @@ dependencies = [ [[package]] name = "clipboard" version = "0.0.3" -source = "git+https://github.com/aweinstock314/rust-clipboard#b0092c8630d4362e4618533822bea90a034b0792" +source = "git+https://github.com/aweinstock314/rust-clipboard#8c4c31e73a5ac5afd97825acc4c534dccfc0ab9b" dependencies = [ "clipboard-win 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -620,7 +635,7 @@ dependencies = [ [[package]] name = "glutin" version = "0.3.5" -source = "git+https://github.com/servo/glutin?branch=servo#22a561c30a0560f445f50abcebf8439d264e0c67" +source = "git+https://github.com/servo/glutin?branch=servo#0b055f889ce0baaa0fc186da4f3b5c31b7c6fd18" dependencies = [ "android_glue 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "cgl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1053,6 +1068,7 @@ name = "net_tests" version = "0.0.1" dependencies = [ "cookie 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.1.0 (git+https://github.com/pcwalton/ipc-channel)", "net 0.0.1", @@ -1349,6 +1365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "script" version = "0.0.1" dependencies = [ + "angle 0.1.0 (git+https://github.com/ecoal95/angle?branch=servo)", "bitflags 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "canvas 0.0.1", "canvas_traits 0.0.1", diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml index 00a30da7962..8d454044d4e 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -41,9 +41,10 @@ git = "https://github.com/servo/rust-png" features = [ "serde-serialization" ] [features] -default = ["glutin_app", "window"] +default = ["glutin_app", "window", "webdriver"] window = ["glutin_app/window"] headless = ["glutin_app/headless"] +webdriver = ["webdriver_server"] # Uncomment to profile on Linux: # @@ -77,17 +78,30 @@ path = "../util" [dependencies.script] path = "../script" +[dependencies.script_traits] +path = "../script_traits" + [dependencies.layout] path = "../layout" [dependencies.gfx] path = "../gfx" +[dependencies.style] +path = "../style" + +[dependencies.canvas] +path = "../canvas" + +[dependencies.canvas_traits] +path = "../canvas_traits" + [dependencies.devtools] path = "../devtools" [dependencies.webdriver_server] -path = "../webdriver_server" +path = "../webdriver_server" +optional = true [dependencies.devtools_traits] path = "../devtools_traits" @@ -104,6 +118,15 @@ optional = true version = "0.2" features = [ "serde_serialization" ] +[dependencies.euclid] +version = "0.1" + +[dependencies.layers] +git = "https://github.com/servo/rust-layers" + +[dependencies.gleam] +version = "0.1" + [dependencies] env_logger = "0.3" time = "0.1.12" diff --git a/components/servo/lib.rs b/components/servo/lib.rs index dbf127803d3..8dd05d6f6f4 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -17,22 +17,67 @@ // The `Browser` is fed events from a generic type that implements the // `WindowMethods` trait. -extern crate compositing; -extern crate devtools; -extern crate devtools_traits; -extern crate net; -extern crate net_traits; -extern crate msg; -extern crate profile; -extern crate profile_traits; #[macro_use] -extern crate util; -extern crate script; -extern crate layout; -extern crate gfx; +extern crate util as _util; + +mod export { + extern crate compositing; + extern crate devtools; + extern crate devtools_traits; + extern crate net; + extern crate net_traits; + extern crate msg; + extern crate profile; + extern crate profile_traits; + extern crate script; + extern crate script_traits; + extern crate layout; + extern crate gfx; + extern crate style; + extern crate canvas; + extern crate canvas_traits; + + extern crate euclid; + extern crate url; + extern crate layers; + extern crate gleam; +} + extern crate libc; + +#[cfg(feature = "webdriver")] extern crate webdriver_server; +#[cfg(feature = "webdriver")] +fn webdriver(port: u16, constellation: msg::constellation_msg::ConstellationChan) { + webdriver_server::start_server(port, constellation.clone()); +} + +#[cfg(not(feature = "webdriver"))] +fn webdriver(_port: u16, _constellation: msg::constellation_msg::ConstellationChan) { } + +pub use _util as util; +pub use export::compositing; +pub use export::devtools; +pub use export::devtools_traits; +pub use export::net; +pub use export::net_traits; +pub use export::msg; +pub use export::profile; +pub use export::profile_traits; +pub use export::script; +pub use export::script_traits; +pub use export::layout; +pub use export::gfx; +pub use export::style; +pub use export::canvas; +pub use export::canvas_traits; + +pub use export::euclid; +pub use export::url; +pub use export::layers; +pub use export::gleam::gl; + use compositing::CompositorEventListener; use compositing::windowing::WindowEvent; @@ -110,9 +155,11 @@ impl Browser { devtools_chan, supports_clipboard); - if let Some(port) = opts.webdriver_port { - webdriver_server::start_server(port, constellation_chan.clone()); - }; + if cfg!(feature = "webdriver") { + if let Some(port) = opts.webdriver_port { + webdriver(port, constellation_chan.clone()); + } + } // The compositor coordinates with the client window to create the final // rendered page and display it somewhere. diff --git a/components/servo/main.rs b/components/servo/main.rs index e96703026fe..afefe23f591 100644 --- a/components/servo/main.rs +++ b/components/servo/main.rs @@ -19,29 +19,23 @@ // The Servo engine extern crate servo; -// Window graphics compositing and message dispatch -extern crate compositing; -// Servo networking -extern crate net; -extern crate net_traits; -// Servo common utilitiess -extern crate util; + // The window backed by glutin extern crate glutin_app as app; extern crate time; extern crate env_logger; -#[cfg(target_os="android")] +#[cfg(target_os = "android")] #[macro_use] extern crate android_glue; -use compositing::windowing::WindowEvent; -use net_traits::hosts; use servo::Browser; +use servo::compositing::windowing::WindowEvent; +use servo::net_traits::hosts; +use servo::util::opts; use std::rc::Rc; -use util::opts; -#[cfg(target_os="android")] +#[cfg(target_os = "android")] use std::borrow::ToOwned; fn main() { @@ -134,16 +128,16 @@ impl app::NestedEventLoopListener for BrowserWrapper { } } -#[cfg(target_os="android")] +#[cfg(target_os = "android")] fn setup_logging() { android::setup_logging(); } -#[cfg(not(target_os="android"))] +#[cfg(not(target_os = "android"))] fn setup_logging() { } -#[cfg(target_os="android")] +#[cfg(target_os = "android")] fn get_args() -> Vec<String> { vec![ "servo".to_owned(), @@ -151,7 +145,7 @@ fn get_args() -> Vec<String> { ] } -#[cfg(not(target_os="android"))] +#[cfg(not(target_os = "android"))] fn get_args() -> Vec<String> { use std::env; env::args().collect() @@ -189,10 +183,10 @@ mod android { use self::libc::funcs::c95::stdio::fgets; use self::libc::funcs::posix88::stdio::fdopen; use self::libc::funcs::posix88::unistd::{pipe, dup2}; + use servo::util::task::spawn_named; use std::ffi::CStr; use std::ffi::CString; use std::str::from_utf8; - use util::task::spawn_named; unsafe { let mut pipes: [c_int; 2] = [ 0, 0 ]; diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index c6a5296c739..089d45693c2 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -6585,6 +6585,9 @@ macro_rules! css_properties_accessors { $macro_name! { % for property in SHORTHANDS + LONGHANDS: % if property.derived_from is None: + % if '-' in property.name: + [${property.ident.capitalize()}, Set${property.ident.capitalize()}, "${property.name}"], + % endif % if property != LONGHANDS[-1]: [${property.camel_case}, Set${property.camel_case}, "${property.name}"], % else: diff --git a/components/style/values.rs b/components/style/values.rs index 7e5774d9262..2a5de980ed9 100644 --- a/components/style/values.rs +++ b/components/style/values.rs @@ -151,6 +151,7 @@ pub mod specified { pub enum FontRelativeLength { Em(CSSFloat), Ex(CSSFloat), + Ch(CSSFloat), Rem(CSSFloat) } @@ -159,6 +160,7 @@ pub mod specified { match self { &FontRelativeLength::Em(length) => write!(dest, "{}em", length), &FontRelativeLength::Ex(length) => write!(dest, "{}ex", length), + &FontRelativeLength::Ch(length) => write!(dest, "{}ch", length), &FontRelativeLength::Rem(length) => write!(dest, "{}rem", length) } } @@ -172,9 +174,10 @@ pub mod specified { { match self { &FontRelativeLength::Em(length) => reference_font_size.scale_by(length), - &FontRelativeLength::Ex(length) => { - let x_height = 0.5; // TODO: find that from the font - reference_font_size.scale_by(length * x_height) + &FontRelativeLength::Ex(length) | &FontRelativeLength::Ch(length) => { + // https://github.com/servo/servo/issues/7462 + let em_factor = 0.5; + reference_font_size.scale_by(length * em_factor) }, &FontRelativeLength::Rem(length) => root_font_size.scale_by(length) } @@ -284,6 +287,7 @@ pub mod specified { match self { FontRelativeLength::Em(v) => FontRelativeLength::Em(v * scalar), FontRelativeLength::Ex(v) => FontRelativeLength::Ex(v * scalar), + FontRelativeLength::Ch(v) => FontRelativeLength::Ch(v * scalar), FontRelativeLength::Rem(v) => FontRelativeLength::Rem(v * scalar), } } @@ -338,6 +342,7 @@ pub mod specified { // font-relative "em" => Ok(Length::FontRelative(FontRelativeLength::Em(value))), "ex" => Ok(Length::FontRelative(FontRelativeLength::Ex(value))), + "ch" => Ok(Length::FontRelative(FontRelativeLength::Ch(value))), "rem" => Ok(Length::FontRelative(FontRelativeLength::Rem(value))), // viewport percentages "vw" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vw(value))), diff --git a/components/util/opts.rs b/components/util/opts.rs index 10a2bd84a78..8a8459f687c 100644 --- a/components/util/opts.rs +++ b/components/util/opts.rs @@ -56,8 +56,6 @@ pub struct Opts { pub nonincremental_layout: bool, - pub nossl: bool, - /// Where to load userscripts from, if any. An empty string will load from /// the resources/user-agent-js directory, and if the option isn't passed userscripts /// won't be loaded @@ -335,10 +333,10 @@ fn args_fail(msg: &str) -> ! { // Always use CPU painting on android. -#[cfg(target_os="android")] +#[cfg(target_os = "android")] static FORCE_CPU_PAINTING: bool = true; -#[cfg(not(target_os="android"))] +#[cfg(not(target_os = "android"))] static FORCE_CPU_PAINTING: bool = false; enum UserAgent { @@ -361,15 +359,15 @@ fn default_user_agent_string(agent: UserAgent) -> String { }.to_owned() } -#[cfg(target_os="android")] +#[cfg(target_os = "android")] const DEFAULT_USER_AGENT: UserAgent = UserAgent::Android; // FIXME: This requires https://github.com/servo/servo/issues/7138 to provide the // correct string in Gonk builds (i.e., it will never be chosen today). -#[cfg(target_os="gonk")] +#[cfg(target_os = "gonk")] const DEFAULT_USER_AGENT: UserAgent = UserAgent::Gonk; -#[cfg(not(any(target_os="android", target_os="gonk")))] +#[cfg(not(any(target_os = "android", target_os = "gonk")))] const DEFAULT_USER_AGENT: UserAgent = UserAgent::Desktop; pub fn default_opts() -> Opts { @@ -383,7 +381,6 @@ pub fn default_opts() -> Opts { mem_profiler_period: None, layout_threads: 1, nonincremental_layout: false, - nossl: false, userscripts: None, user_stylesheets: Vec::new(), output_file: None, @@ -532,7 +529,6 @@ pub fn from_cmdline_args(args: &[String]) { }; let nonincremental_layout = opt_match.opt_present("i"); - let nossl = opt_match.opt_present("no-ssl"); let mut bubble_inline_sizes_separately = debug_options.bubble_widths; if debug_options.trace_layout { @@ -588,7 +584,6 @@ pub fn from_cmdline_args(args: &[String]) { mem_profiler_period: mem_profiler_period, layout_threads: layout_threads, nonincremental_layout: nonincremental_layout, - nossl: nossl, userscripts: opt_match.opt_default("userscripts", ""), user_stylesheets: user_stylesheets, output_file: opt_match.opt_str("o"), diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 78284ce5378..a2365e9a784 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -56,6 +56,14 @@ version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "angle" +version = "0.1.0" +source = "git+https://github.com/ecoal95/angle?branch=servo#77288884bd7a89bf3019ca94dcda6c94cb178aa4" +dependencies = [ + "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "aster" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -160,7 +168,7 @@ dependencies = [ [[package]] name = "clipboard" version = "0.0.3" -source = "git+https://github.com/aweinstock314/rust-clipboard#b0092c8630d4362e4618533822bea90a034b0792" +source = "git+https://github.com/aweinstock314/rust-clipboard#8c4c31e73a5ac5afd97825acc4c534dccfc0ab9b" dependencies = [ "clipboard-win 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -612,7 +620,7 @@ dependencies = [ [[package]] name = "glutin" version = "0.3.5" -source = "git+https://github.com/servo/glutin?branch=servo#22a561c30a0560f445f50abcebf8439d264e0c67" +source = "git+https://github.com/servo/glutin?branch=servo#0b055f889ce0baaa0fc186da4f3b5c31b7c6fd18" dependencies = [ "android_glue 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "cgl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1327,6 +1335,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "script" version = "0.0.1" dependencies = [ + "angle 0.1.0 (git+https://github.com/ecoal95/angle?branch=servo)", "bitflags 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "canvas 0.0.1", "canvas_traits 0.0.1", diff --git a/ports/glutin/lib.rs b/ports/glutin/lib.rs index 0bd7fffff8e..cb611153be0 100644 --- a/ports/glutin/lib.rs +++ b/ports/glutin/lib.rs @@ -7,7 +7,7 @@ #![feature(box_syntax, result_expect)] #[macro_use] extern crate bitflags; -#[cfg(target_os="macos")] extern crate cgl; +#[cfg(target_os = "macos")] extern crate cgl; extern crate compositing; extern crate euclid; extern crate gleam; @@ -19,9 +19,9 @@ extern crate net_traits; #[cfg(feature = "window")] extern crate script_traits; extern crate time; extern crate util; -#[cfg(target_os="android")] extern crate egl; +#[cfg(target_os = "android")] extern crate egl; extern crate url; -#[cfg(target_os="linux")] extern crate x11; +#[cfg(target_os = "linux")] extern crate x11; use compositing::windowing::WindowEvent; use euclid::scale_factor::ScaleFactor; diff --git a/ports/glutin/window.rs b/ports/glutin/window.rs index a01987ee1f7..ec96727d811 100644 --- a/ports/glutin/window.rs +++ b/ports/glutin/window.rs @@ -35,7 +35,7 @@ use std::cell::{Cell, RefCell}; #[cfg(feature = "window")] use util::opts; -#[cfg(all(feature = "headless", target_os="linux"))] +#[cfg(all(feature = "headless", target_os = "linux"))] use std::ptr; #[cfg(feature = "window")] @@ -56,15 +56,15 @@ bitflags! { } // Some shortcuts use Cmd on Mac and Control on other systems. -#[cfg(all(feature = "window", target_os="macos"))] +#[cfg(all(feature = "window", target_os = "macos"))] const CMD_OR_CONTROL: constellation_msg::KeyModifiers = SUPER; -#[cfg(all(feature = "window", not(target_os="macos")))] +#[cfg(all(feature = "window", not(target_os = "macos")))] const CMD_OR_CONTROL: constellation_msg::KeyModifiers = CONTROL; // Some shortcuts use Cmd on Mac and Alt on other systems. -#[cfg(all(feature = "window", target_os="macos"))] +#[cfg(all(feature = "window", target_os = "macos"))] const CMD_OR_ALT: constellation_msg::KeyModifiers = SUPER; -#[cfg(all(feature = "window", not(target_os="macos")))] +#[cfg(all(feature = "window", not(target_os = "macos")))] const CMD_OR_ALT: constellation_msg::KeyModifiers = ALT; // This should vary by zoom level and maybe actual text size (focused or under cursor) @@ -138,22 +138,22 @@ impl Window { } } - #[cfg(not(target_os="android"))] + #[cfg(not(target_os = "android"))] fn gl_version() -> GlRequest { GlRequest::Specific(Api::OpenGl, (3, 0)) } - #[cfg(target_os="android")] + #[cfg(target_os = "android")] fn gl_version() -> GlRequest { GlRequest::Specific(Api::OpenGlEs, (2, 0)) } - #[cfg(not(target_os="android"))] + #[cfg(not(target_os = "android"))] fn load_gl_functions(window: &glutin::Window) { gl::load_with(|s| window.get_proc_address(s)); } - #[cfg(target_os="android")] + #[cfg(target_os = "android")] fn load_gl_functions(_: &glutin::Window) { } @@ -290,7 +290,7 @@ impl Window { self.event_queue.borrow_mut().push(WindowEvent::MouseWindowEventClass(event)); } - #[cfg(target_os="macos")] + #[cfg(target_os = "macos")] fn handle_next_event(&self) -> bool { let event = self.window.wait_events().next().unwrap(); let mut close = self.handle_window_event(event); @@ -305,7 +305,7 @@ impl Window { close } - #[cfg(any(target_os="linux", target_os="android"))] + #[cfg(any(target_os = "linux", target_os = "android"))] fn handle_next_event(&self) -> bool { use std::thread::sleep_ms; @@ -478,7 +478,7 @@ impl Window { result } - #[cfg(all(feature = "window", not(target_os="win")))] + #[cfg(all(feature = "window", not(target_os = "win")))] fn platform_handle_key(&self, key: Key, mods: constellation_msg::KeyModifiers) { match (mods, key) { (CMD_OR_CONTROL, Key::LeftBracket) => { @@ -491,19 +491,19 @@ impl Window { } } - #[cfg(all(feature = "window", target_os="win"))] + #[cfg(all(feature = "window", target_os = "win"))] fn platform_handle_key(&self, key: Key, mods: constellation_msg::KeyModifiers) { } } // WindowProxy is not implemented for android yet -#[cfg(all(feature = "window", target_os="android"))] +#[cfg(all(feature = "window", target_os = "android"))] fn create_window_proxy(_: &Rc<Window>) -> Option<glutin::WindowProxy> { None } -#[cfg(all(feature = "window", not(target_os="android")))] +#[cfg(all(feature = "window", not(target_os = "android")))] fn create_window_proxy(window: &Rc<Window>) -> Option<glutin::WindowProxy> { Some(window.window.create_window_proxy()) } @@ -623,7 +623,7 @@ impl WindowMethods for Window { true } - #[cfg(target_os="linux")] + #[cfg(target_os = "linux")] fn native_display(&self) -> NativeDisplay { use x11::xlib; unsafe { @@ -631,7 +631,7 @@ impl WindowMethods for Window { } } - #[cfg(not(target_os="linux"))] + #[cfg(not(target_os = "linux"))] fn native_display(&self) -> NativeDisplay { NativeDisplay::new() } @@ -791,7 +791,7 @@ impl WindowMethods for Window { true } - #[cfg(target_os="linux")] + #[cfg(target_os = "linux")] fn native_display(&self) -> NativeDisplay { NativeDisplay::new(ptr::null_mut()) } diff --git a/ports/gonk/Cargo.lock b/ports/gonk/Cargo.lock index df88f068672..0f810c0de64 100644 --- a/ports/gonk/Cargo.lock +++ b/ports/gonk/Cargo.lock @@ -42,6 +42,14 @@ dependencies = [ ] [[package]] +name = "angle" +version = "0.1.0" +source = "git+https://github.com/ecoal95/angle?branch=servo#77288884bd7a89bf3019ca94dcda6c94cb178aa4" +dependencies = [ + "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "aster" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -141,7 +149,7 @@ dependencies = [ [[package]] name = "clipboard" version = "0.0.3" -source = "git+https://github.com/aweinstock314/rust-clipboard#b0092c8630d4362e4618533822bea90a034b0792" +source = "git+https://github.com/aweinstock314/rust-clipboard#8c4c31e73a5ac5afd97825acc4c534dccfc0ab9b" dependencies = [ "clipboard-win 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1193,6 +1201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "script" version = "0.0.1" dependencies = [ + "angle 0.1.0 (git+https://github.com/ecoal95/angle?branch=servo)", "bitflags 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "canvas 0.0.1", "canvas_traits 0.0.1", diff --git a/python/servo/build_commands.py b/python/servo/build_commands.py index d49632882e5..73d9b9046fa 100644 --- a/python/servo/build_commands.py +++ b/python/servo/build_commands.py @@ -242,9 +242,6 @@ class MachCommands(CommandBase): notify_build_done(elapsed) print("Build completed in %0.2fs" % elapsed) - # XXX(#7339) Android build is broken - if android: - return 0 return status @Command('build-cef', diff --git a/python/tidy.py b/python/tidy.py index 110b0e9a01c..e576d501a37 100644 --- a/python/tidy.py +++ b/python/tidy.py @@ -228,33 +228,35 @@ def check_rust(file_name, contents): line = merged_lines + line merged_lines = '' - # get rid of strings and chars because cases like regex expression - line = re.sub('".*?"|\'.*?\'', '', line) + # get rid of strings and chars because cases like regex expression, keep attributes + if not line_is_attribute(line): + line = re.sub('".*?"|\'.*?\'', '', line) - # get rid of comments and attributes - line = re.sub('//.*?$|/\*.*?$|^\*.*?$|^#.*?$', '', line) + # get rid of comments + line = re.sub('//.*?$|/\*.*?$|^\*.*?$', '', line) + + # get rid of attributes that do not contain = + line = re.sub('^#[A-Za-z0-9\(\)\[\]_]*?$', '', line) match = re.search(r",[A-Za-z0-9]", line) if match: yield (idx + 1, "missing space after ,") - # Avoid flagging <Item=Foo> constructs - def is_associated_type(match, line, index): - open_angle = line[0:match.end()].rfind('<') - close_angle = line[open_angle:].find('>') if open_angle != -1 else -1 - is_equals = match.group(0)[index] == '=' - generic_open = open_angle != -1 and open_angle < match.start() - generic_close = close_angle != -1 and close_angle + open_angle >= match.end() - return is_equals and generic_open and generic_close - - # - not included because of scientific notation (1e-6) - match = re.search(r"[A-Za-z0-9][\+/\*%=]", line) + if line_is_attribute(line): + pre_space_re = r"[A-Za-z0-9]=" + post_space_re = r"=[A-Za-z0-9\"]" + else: + # - not included because of scientific notation (1e-6) + pre_space_re = r"[A-Za-z0-9][\+/\*%=]" + # * not included because of dereferencing and casting + # - not included because of unary negation + post_space_re = r"[\+/\%=][A-Za-z0-9\"]" + + match = re.search(pre_space_re, line) if match and not is_associated_type(match, line, 1): yield (idx + 1, "missing space before %s" % match.group(0)[1]) - # * not included because of dereferencing and casting - # - not included because of unary negation - match = re.search(r"[\+/\%=][A-Za-z0-9]", line) + match = re.search(post_space_re, line) if match and not is_associated_type(match, line, 0): yield (idx + 1, "missing space after %s" % match.group(0)[0]) @@ -283,6 +285,16 @@ def check_rust(file_name, contents): if match: yield (idx + 1, "missing space before {") + # ignored cases like {} and }} + match = re.search(r"[^\s{}]}", line) + if match and not (line.startswith("use") or line.startswith("pub use")): + yield (idx + 1, "missing space before }") + + # ignored cases like {} and {{ + match = re.search(r"{[^\s{}]", line) + if match and not (line.startswith("use") or line.startswith("pub use")): + yield (idx + 1, "missing space after {") + # imports must be in the same line and alphabetically sorted if line.startswith("use "): use = line[4:] @@ -300,6 +312,20 @@ def check_rust(file_name, contents): uses = [] +# Avoid flagging <Item=Foo> constructs +def is_associated_type(match, line, index): + open_angle = line[0:match.end()].rfind('<') + close_angle = line[open_angle:].find('>') if open_angle != -1 else -1 + is_equals = match.group(0)[index] == '=' + generic_open = open_angle != -1 and open_angle < match.start() + generic_close = close_angle != -1 and close_angle + open_angle >= match.end() + return is_equals and generic_open and generic_close + + +def line_is_attribute(line): + return re.search(r"#\[.*\]", line) + + def check_webidl_spec(file_name, contents): # Sorted by this function (in pseudo-Rust). The idea is to group the same # organization together. @@ -353,7 +379,17 @@ def check_spec(file_name, contents): raise StopIteration file_name = os.path.relpath(os.path.splitext(file_name)[0], base_path) patt = re.compile("^\s*\/\/.+") - pattern = "impl<'a> %sMethods for &'a %s {" % (file_name, file_name) + + # Pattern representing a line with a macro + macro_patt = re.compile("^\s*\S+!(.*)$") + + # Pattern representing a line with comment containing a spec link + link_patt = re.compile("^\s*///? https://.+$") + + # Pattern representing a line with comment + comment_patt = re.compile("^\s*///?.+$") + + pattern = "impl %sMethods for %s {" % (file_name, file_name) contents = contents.splitlines(True) brace_count = 0 in_impl = False @@ -363,9 +399,16 @@ def check_spec(file_name, contents): if not patt.match(line): if pattern.lower() in line.lower(): in_impl = True - if "fn " in line and brace_count == 1: - if "// https://" not in contents[idx - 1] and "// https://" not in contents[idx - 2]: - yield (idx + 1, "method declared in webidl is missing a comment with a specification link") + if ("fn " in line or macro_patt.match(line)) and brace_count == 1: + for up_idx in range(1, idx + 1): + up_line = contents[idx - up_idx] + if link_patt.match(up_line): + # Comment with spec link exists + break + if not comment_patt.match(up_line): + # No more comments exist above, yield warning + yield (idx + 1, "method declared in webidl is missing a comment with a specification link") + break if '{' in line and in_impl: brace_count += 1 if '}' in line and in_impl: diff --git a/tests/ref/absolute_table.html b/tests/ref/absolute_table.html new file mode 100644 index 00000000000..b0dc978e34f --- /dev/null +++ b/tests/ref/absolute_table.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<style> +div { display:table;position:absolute;top:0px;right:40px } +</style> +<div> +XXX +</div> diff --git a/tests/ref/absolute_table_ref.html b/tests/ref/absolute_table_ref.html new file mode 100644 index 00000000000..40018562574 --- /dev/null +++ b/tests/ref/absolute_table_ref.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<style> +div { display:block;position:absolute;top:0px;right:40px } +</style> +<div> +XXX +</div> diff --git a/tests/ref/basic.list b/tests/ref/basic.list index ed4e0d91a4b..59d0934faae 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -12,6 +12,7 @@ fragment=top != ../html/acid2.html acid2_ref.html == abs_rel_explicit_height.html abs_rel_explicit_height_ref.html == absolute_hypothetical_with_intervening_inline_block_a.html absolute_hypothetical_with_intervening_inline_block_ref.html == absolute_inline_containing_block_a.html absolute_inline_containing_block_ref.html +== absolute_table.html absolute_table_ref.html == absolute_z_index_auto_paint_order_a.html absolute_z_index_auto_paint_order_ref.html == acid1_a.html acid1_b.html == acid2_noscroll.html acid2_ref_broken.html @@ -61,6 +62,7 @@ flaky_cpu == append_style_a.html append_style_b.html == border_code_tag.html border_code_tag_ref.html == border_collapse_missing_cell_a.html border_collapse_missing_cell_ref.html == border_collapse_simple_a.html border_collapse_simple_ref.html +== border_radius_asymmetric_sizes_a.html border_radius_asymmetric_sizes_ref.html == border_radius_clip_a.html border_radius_clip_ref.html != border_radius_dashed_a.html border_radius_dashed_ref.html == border_radius_overlapping_a.html border_radius_overlapping_ref.html @@ -294,6 +296,7 @@ flaky_cpu == linebreak_simple_a.html linebreak_simple_b.html == position_relative_painting_order_a.html position_relative_painting_order_ref.html == position_relative_stacking_context_a.html position_relative_stacking_context_ref.html == position_relative_top_percentage_a.html position_relative_top_percentage_b.html +== position_relative_vertical_percentage_overflow_a.html position_relative_vertical_percentage_overflow_ref.html == pre_ignorable_whitespace_a.html pre_ignorable_whitespace_ref.html == pre_with_tab.html pre_with_tab_ref.html == pseudo_element_a.html pseudo_element_b.html diff --git a/tests/ref/border_radius_asymmetric_sizes_a.html b/tests/ref/border_radius_asymmetric_sizes_a.html new file mode 100644 index 00000000000..94676b11941 --- /dev/null +++ b/tests/ref/border_radius_asymmetric_sizes_a.html @@ -0,0 +1,71 @@ +<!DOCTYPE html> +<html> + <head> + <meta content="text/html;charset=utf-8" http-equiv="Content-Type"> + <meta content="utf-8" http-equiv="encoding"> + <style type="text/css"> + div.box { + background: white; + border-width: 10px 10px 10px 10px; + border-color: yellow red green blue; + border-radius: 10px; + border-style: solid; + height: 190px; + width: 190px; + } + div.top { + border-top-width: 30px; + } + div.right { + border-right-width: 30px; + } + div.bottom { + border-bottom-width: 30px; + } + div.left { + border-left-width: 30px; + } + div.radius10px { + border-radius: 10px; + } + div.radius20px { + border-radius: 20px; + } + div.radius30px { + border-radius: 30px; + } + div.radius40px { + border-radius: 40px; + } + + #box2, #box4, #box6, #box8, #box10, #box12, #box14 { + width: 170px; + } + </style> + </head> + <body> + <h2>Border Radius - 10px</h2> + Box#1<div id="box1" class="box top"></div><br> + Box#2<div id="box2" class="box right"></div><br> + Box#3<div id="box3" class="box bottom"></div><br> + Box#4<div id="box4" class="box left"></div><br> + + <h2>Border Radius - 20px</h2> + Box#5<div id="box5" class="box top radius20px"></div><br> + Box#6<div id="box6" class="box right radius20px"></div><br> + Box#7<div id="box7" class="box bottom radius20px"></div><br> + Box#8<div id="box8" class="box left radius20px"></div><br> + + <h2>Border Radius - 30px</h2> + Box#9<div id="box9" class="box top radius30px"></div><br> + Box#10<div id="box10" class="box right radius30px"></div><br> + Box#11<div id="box11" class="box bottom radius30px"></div><br> + Box#12<div id="box12" class="box left radius30px"></div><br> + + <h2>Border Radius - 40px</h2> + Box#13<div id="box13" class="box top radius40px"></div><br> + Box#14<div id="box14" class="box right radius40px"></div><br> + Box#15<div id="box15" class="box bottom radius40px"></div><br> + Box#16<div id="box16" class="box left radius40px"></div><br> + </body> +</html> diff --git a/tests/ref/border_radius_asymmetric_sizes_ref.html b/tests/ref/border_radius_asymmetric_sizes_ref.html new file mode 100644 index 00000000000..4993ae6fd7d --- /dev/null +++ b/tests/ref/border_radius_asymmetric_sizes_ref.html @@ -0,0 +1,217 @@ +<!DOCTYPE html> +<html> + <head> + <meta content="text/html;charset=utf-8" http-equiv="Content-Type"> + <meta content="utf-8" http-equiv="encoding"> + <style type="text/css"> + .box-top { + background: white; + width: 190px; + height: 0px; + margin: 0px; + border-style: solid; + border-color: yellow red green blue; + } + .box-middle { + background: white; + width: 190px; + height: 190px; + margin: 0px; + border-style: solid; + border-color: yellow red green blue; + } + .box-bottom { + background: white; + width: 190px; + height: 0px; + margin: 0px; + border-style: solid; + border-color: yellow red green blue; + } + .top-radius-10 { + border-top-left-radius: 10px; + border-top-right-radius: 10px; + } + .bottom-radius-10 { + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + } + .top-radius-20 { + border-top-left-radius: 20px; + border-top-right-radius: 20px; + } + .bottom-radius-20 { + border-bottom-left-radius: 20px; + border-bottom-right-radius: 20px; + } + .top-radius-30 { + border-top-left-radius: 30px; + border-top-right-radius: 30px; + } + .bottom-radius-30 { + border-bottom-left-radius: 30px; + border-bottom-right-radius: 30px; + } + .top-radius-40 { + border-top-left-radius: 40px; + border-top-right-radius: 40px; + } + .bottom-radius-40 { + border-bottom-left-radius: 40px; + border-bottom-right-radius: 40px; + } + .box1-top { + border-top-width: 30px; + border-right-width: 10px; + border-bottom-width: 0px; + border-left-width: 10px; + } + .box1-middle { + border-width: 0px 10px 0px 10px; + } + .box1-bottom { + border-width: 0px 10px 10px 10px; + } + .box2-top { + width: 170px; + border-width: 10px 30px 0px 10px; + } + .box2-middle { + width: 170px; + border-width: 0px 30px 0px 10px; + } + .box2-bottom { + width: 170px; + border-width: 0px 30px 10px 10px; + } + .box3-top { + border-width: 10px 10px 0px 10px; + } + .box3-middle { + border-width: 0px 10px 0px 10px; + } + .box3-bottom { + border-top-width: 0px; + border-left-width: 10px; + border-right-width: 10px; + border-bottom-width: 30px; + } + .box4-top { + width: 170px; + border-width: 10px 10px 0px 30px; + } + .box4-middle { + width: 170px; + border-width: 0px 10px 0px 30px; + } + .box4-bottom { + width: 170px; + border-top-width: 0px; + border-left-width: 30px; + border-right-width: 10px; + border-bottom-width: 10px; + } + </style> + </head> + <body> + <h2>Border Radius - 10px</h2> + Box#1 + <div id="box1-top" class="box-top box1-top top-radius-10"></div> + <div id="box1-middle" class="box-middle box1-middle middle-radius-10"></div> + <div id="box1-bottom" class="box-bottom box1-bottom bottom-radius-10"></div> + <br> + + Box#2 + <div id="box2-top" class="box-top box2-top top-radius-10"></div> + <div id="box2-middle" class="box-middle box2-middle middle-radius-10"></div> + <div id="box2-bottom" class="box-bottom box2-bottom bottom-radius-10"></div> + <br> + + Box#3 + <div id="box3-top" class="box-top box3-top top-radius-10"></div> + <div id="box3-middle" class="box-middle box3-middle middle-radius-10"></div> + <div id="box3-bottom" class="box-bottom box3-bottom bottom-radius-10"></div> + <br> + + Box#4 + <div id="box4-top" class="box-top box4-top top-radius-10"></div> + <div id="box4-middle" class="box-middle box4-middle middle-radius-10"></div> + <div id="box4-bottom" class="box-bottom box4-bottom bottom-radius-10"></div> + <br> + + <h2>Border Radius - 20px</h2> + Box#5 + <div id="box5-top" class="box-top box1-top top-radius-20"></div> + <div id="box5-middle" class="box-middle box1-middle middle-radius-20"></div> + <div id="box5-bottom" class="box-bottom box1-bottom bottom-radius-20"></div> + <br> + + Box#6 + <div id="box6-top" class="box-top box2-top top-radius-20"></div> + <div id="box6-middle" class="box-middle box2-middle middle-radius-20"></div> + <div id="box6-bottom" class="box-bottom box2-bottom bottom-radius-20"></div> + <br> + + Box#7 + <div id="box7-top" class="box-top box3-top top-radius-20"></div> + <div id="box7-middle" class="box-middle box3-middle middle-radius-20"></div> + <div id="box7-bottom" class="box-bottom box3-bottom bottom-radius-20"></div> + <br> + + Box#8 + <div id="box8-top" class="box-top box4-top top-radius-20"></div> + <div id="box8-middle" class="box-middle box4-middle middle-radius-20"></div> + <div id="box8-bottom" class="box-bottom box4-bottom bottom-radius-20"></div> + <br> + + <h2>Border Radius - 30px</h2> + Box#9 + <div id="box9-top" class="box-top box1-top top-radius-30"></div> + <div id="box9-middle" class="box-middle box1-middle middle-radius-30"></div> + <div id="box9-bottom" class="box-bottom box1-bottom bottom-radius-30"></div> + <br> + + Box#10 + <div id="box10-top" class="box-top box2-top top-radius-30"></div> + <div id="box10-middle" class="box-middle box2-middle middle-radius-30"></div> + <div id="box10-bottom" class="box-bottom box2-bottom bottom-radius-30"></div> + <br> + + Box#11 + <div id="box11-top" class="box-top box3-top top-radius-30"></div> + <div id="box11-middle" class="box-middle box3-middle middle-radius-30"></div> + <div id="box11-bottom" class="box-bottom box3-bottom bottom-radius-30"></div> + <br> + + Box#12 + <div id="box12-top" class="box-top box4-top top-radius-30"></div> + <div id="box12-middle" class="box-middle box4-middle middle-radius-30"></div> + <div id="box12-bottom" class="box-bottom box4-bottom bottom-radius-30"></div> + <br> + + <h2>Border Radius - 40px</h2> + Box#13 + <div id="box13-top" class="box-top box1-top top-radius-40"></div> + <div id="box13-middle" class="box-middle box1-middle middle-radius-40"></div> + <div id="box13-bottom" class="box-bottom box1-bottom bottom-radius-40"></div> + <br> + + Box#14 + <div id="box14-top" class="box-top box2-top top-radius-40"></div> + <div id="box14-middle" class="box-middle box2-middle middle-radius-40"></div> + <div id="box14-bottom" class="box-bottom box2-bottom bottom-radius-40"></div> + <br> + + Box#15 + <div id="box15-top" class="box-top box3-top top-radius-40"></div> + <div id="box15-middle" class="box-middle box3-middle middle-radius-40"></div> + <div id="box15-bottom" class="box-bottom box3-bottom bottom-radius-40"></div> + <br> + + Box#16 + <div id="box16-top" class="box-top box4-top top-radius-40"></div> + <div id="box16-middle" class="box-middle box4-middle middle-radius-40"></div> + <div id="box16-bottom" class="box-bottom box4-bottom bottom-radius-40"></div> + <br> + </body> +</html> diff --git a/tests/ref/position_relative_vertical_percentage_overflow_a.html b/tests/ref/position_relative_vertical_percentage_overflow_a.html new file mode 100644 index 00000000000..ca9aae49050 --- /dev/null +++ b/tests/ref/position_relative_vertical_percentage_overflow_a.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<style> +body, html { + margin: 0; +} +#a { + display: block; + position: absolute; + top: 100px; + left: 100px; + width: 100px; + height: 100px; + transform: translateX(0px); /* force creation of stacking context */ +} +#b { + display: block; + position: relative; + top: -50%; + width: 100px; + height: 100px; + background: purple; +} +</style> +<div id=a><div id=b> + diff --git a/tests/ref/position_relative_vertical_percentage_overflow_ref.html b/tests/ref/position_relative_vertical_percentage_overflow_ref.html new file mode 100644 index 00000000000..32bdcb8ab59 --- /dev/null +++ b/tests/ref/position_relative_vertical_percentage_overflow_ref.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<style> +body, html { + margin: 0; +} +#a { + display: block; + position: absolute; + top: 50px; + left: 100px; + width: 100px; + height: 100px; + background: purple; +} +</style> +<div id=a> + diff --git a/tests/ref/webgl-context/tex_image_2d_simple.html b/tests/ref/webgl-context/tex_image_2d_simple.html index 9ba19e2e6dc..60ea4674530 100644 --- a/tests/ref/webgl-context/tex_image_2d_simple.html +++ b/tests/ref/webgl-context/tex_image_2d_simple.html @@ -10,6 +10,7 @@ </style> <canvas id="c" width="256" height="256"></canvas> <script id="vertex_shader" type="x-shader/x-vertex"> + precision mediump float; attribute vec2 a_texCoord; attribute vec2 a_position; varying vec2 v_texCoord; @@ -21,6 +22,7 @@ </script> <script id="fragment_shader" type="x-shader/x-fragment"> + precision mediump float; uniform sampler2D u_image; varying vec2 v_texCoord; void main() { diff --git a/tests/unit/net/Cargo.toml b/tests/unit/net/Cargo.toml index 62c72b975bc..fdd82f7fb6d 100644 --- a/tests/unit/net/Cargo.toml +++ b/tests/unit/net/Cargo.toml @@ -25,3 +25,4 @@ cookie = "0.1" hyper = "0.6" url = "0.2" time = "0.1" +flate2 = "0.2.0" diff --git a/tests/unit/net/http_loader.rs b/tests/unit/net/http_loader.rs new file mode 100644 index 00000000000..f007522b433 --- /dev/null +++ b/tests/unit/net/http_loader.rs @@ -0,0 +1,666 @@ +/* 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 flate2::Compression; +use flate2::write::{GzEncoder, DeflateEncoder}; +use hyper::header::{Headers, Location, ContentLength}; +use hyper::http::RawStatus; +use hyper::method::Method; +use hyper::status::StatusCode; +use ipc_channel::ipc; +use net::http_loader::{load, LoadError, HttpRequestFactory, HttpRequest, HttpResponse}; +use net::resource_task::new_resource_task; +use net_traits::LoadData; +use net_traits::{ResourceTask, ControlMsg, CookieSource}; +use std::borrow::Cow; +use std::io::{self, Write, Read, Cursor}; +use url::Url; + +fn respond_with(body: Vec<u8>) -> MockResponse { + let mut headers = Headers::new(); + respond_with_headers(body, &mut headers) +} + +fn respond_with_headers(body: Vec<u8>, headers: &mut Headers) -> MockResponse { + headers.set(ContentLength(body.len() as u64)); + + MockResponse::new( + headers.clone(), + StatusCode::Ok, + RawStatus(200, Cow::Borrowed("Ok")), + body + ) +} + +fn read_response(reader: &mut Read) -> String { + let mut buf = vec![0; 1024]; + match reader.read(&mut buf) { + Ok(len) if len > 0 => { + unsafe { buf.set_len(len); } + String::from_utf8(buf).unwrap() + }, + Ok(_) => "".to_string(), + Err(e) => panic!("problem reading response {}", e) + } +} + +struct MockResponse { + h: Headers, + sc: StatusCode, + sr: RawStatus, + msg: Cursor<Vec<u8>> +} + +impl MockResponse { + fn new(h: Headers, sc: StatusCode, sr: RawStatus, msg: Vec<u8>) -> MockResponse { + MockResponse { h: h, sc: sc, sr: sr, msg: Cursor::new(msg) } + } +} + +impl Read for MockResponse { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.msg.read(buf) + } +} + +impl HttpResponse for MockResponse { + fn headers(&self) -> &Headers { &self.h } + fn status(&self) -> StatusCode { self.sc } + fn status_raw(&self) -> &RawStatus { &self.sr } +} + +fn redirect_to(host: String) -> MockResponse { + let mut headers = Headers::new(); + headers.set(Location(host.to_string())); + + MockResponse::new( + headers, + StatusCode::MovedPermanently, + RawStatus(301, Cow::Borrowed("Moved Permanently")), + <[_]>::to_vec("".as_bytes()) + ) +} + + +enum ResponseType { + Redirect(String), + Text(Vec<u8>), + WithHeaders(Vec<u8>, Headers) +} + +struct MockRequest { + headers: Headers, + t: ResponseType +} + +impl MockRequest { + fn new(t: ResponseType) -> MockRequest { + MockRequest { headers: Headers::new(), t: t } + } +} + +fn response_for_request_type(t: ResponseType) -> Result<MockResponse, LoadError> { + match t { + ResponseType::Redirect(location) => { + Ok(redirect_to(location)) + }, + ResponseType::Text(b) => { + Ok(respond_with(b)) + }, + ResponseType::WithHeaders(b, mut h) => { + Ok(respond_with_headers(b, &mut h)) + } + } +} + +impl HttpRequest for MockRequest { + type R = MockResponse; + + fn headers_mut(&mut self) -> &mut Headers { &mut self.headers } + + fn send(self, _: &Option<Vec<u8>>) -> Result<MockResponse, LoadError> { + response_for_request_type(self.t) + } +} + +struct AssertRequestMustHaveHeaders { + expected_headers: Headers, + request_headers: Headers, + t: ResponseType +} + +impl AssertRequestMustHaveHeaders { + fn new(t: ResponseType, expected_headers: Headers) -> Self { + AssertRequestMustHaveHeaders { expected_headers: expected_headers, request_headers: Headers::new(), t: t } + } +} + +impl HttpRequest for AssertRequestMustHaveHeaders { + type R = MockResponse; + + fn headers_mut(&mut self) -> &mut Headers { &mut self.request_headers } + + fn send(self, _: &Option<Vec<u8>>) -> Result<MockResponse, LoadError> { + for header in self.expected_headers.iter() { + assert!(self.request_headers.get_raw(header.name()).is_some()); + assert_eq!( + self.request_headers.get_raw(header.name()).unwrap(), + self.expected_headers.get_raw(header.name()).unwrap() + ) + } + + response_for_request_type(self.t) + } +} + +struct AssertMustHaveHeadersRequestFactory { + expected_headers: Headers, + body: Vec<u8> +} + +impl HttpRequestFactory for AssertMustHaveHeadersRequestFactory { + type R = AssertRequestMustHaveHeaders; + + fn create(&self, _: Url, _: Method) -> Result<AssertRequestMustHaveHeaders, LoadError> { + Ok( + AssertRequestMustHaveHeaders::new( + ResponseType::Text(self.body.clone()), + self.expected_headers.clone() + ) + ) + } +} + +fn assert_cookie_for_domain(resource_mgr: &ResourceTask, domain: &str, cookie: &str) { + let (tx, rx) = ipc::channel().unwrap(); + resource_mgr.send(ControlMsg::GetCookiesForUrl(Url::parse(&*domain).unwrap(), + tx, + CookieSource::HTTP)).unwrap(); + if let Some(cookie_list) = rx.recv().unwrap() { + assert_eq!(cookie.to_string(), cookie_list); + } else { + assert_eq!(cookie.len(), 0); + } +} + +struct AssertMustHaveBodyRequest { + expected_body: Option<Vec<u8>>, + headers: Headers, + t: ResponseType +} + +impl AssertMustHaveBodyRequest { + fn new(t: ResponseType, expected_body: Option<Vec<u8>>) -> Self { + AssertMustHaveBodyRequest { expected_body: expected_body, headers: Headers::new(), t: t } + } +} + +impl HttpRequest for AssertMustHaveBodyRequest { + type R = MockResponse; + + fn headers_mut(&mut self) -> &mut Headers { &mut self.headers } + + fn send(self, body: &Option<Vec<u8>>) -> Result<MockResponse, LoadError> { + assert_eq!(self.expected_body, *body); + + response_for_request_type(self.t) + } +} + +#[test] +fn test_load_when_request_is_not_get_or_head_and_there_is_no_body_content_length_should_be_set_to_0() { + let url = Url::parse("http://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + + let mut load_data = LoadData::new(url.clone(), None); + load_data.data = None; + load_data.method = Method::Post; + + let mut content_length = Headers::new(); + content_length.set_raw("Content-Length".to_owned(), vec![<[_]>::to_vec("0".as_bytes())]); + + let _ = load::<AssertRequestMustHaveHeaders>( + load_data.clone(), resource_mgr, None, + &AssertMustHaveHeadersRequestFactory { + expected_headers: content_length, + body: <[_]>::to_vec(&[]) + }); +} + +#[test] +fn test_load_when_redirecting_from_a_post_should_rewrite_next_request_as_get() { + struct Factory; + + impl HttpRequestFactory for Factory { + type R = MockRequest; + + fn create(&self, url: Url, method: Method) -> Result<MockRequest, LoadError> { + if url.domain().unwrap() == "mozilla.com" { + assert_eq!(Method::Post, method); + Ok(MockRequest::new(ResponseType::Redirect("http://mozilla.org".to_string()))) + } else { + assert_eq!(Method::Get, method); + Ok(MockRequest::new(ResponseType::Text(<[_]>::to_vec("Yay!".as_bytes())))) + } + } + } + + let url = Url::parse("http://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + let mut load_data = LoadData::new(url.clone(), None); + load_data.method = Method::Post; + + let _ = load::<MockRequest>(load_data, resource_mgr, None, &Factory); +} + +#[test] +fn test_load_should_decode_the_response_as_deflate_when_response_headers_have_content_encoding_deflate() { + struct Factory; + + impl HttpRequestFactory for Factory { + type R = MockRequest; + + fn create(&self, _: Url, _: Method) -> Result<MockRequest, LoadError> { + let mut e = DeflateEncoder::new(Vec::new(), Compression::Default); + e.write(b"Yay!").unwrap(); + let encoded_content = e.finish().unwrap(); + + let mut headers = Headers::new(); + headers.set_raw("Content-Encoding", vec![b"deflate".to_vec()]); + Ok(MockRequest::new(ResponseType::WithHeaders(encoded_content, headers))) + } + } + + let url = Url::parse("http://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + let load_data = LoadData::new(url.clone(), None); + let mut response = load::<MockRequest>(load_data, resource_mgr.clone(), None, &Factory).unwrap(); + + assert_eq!(read_response(&mut response), "Yay!"); +} + +#[test] +fn test_load_should_decode_the_response_as_gzip_when_response_headers_have_content_encoding_gzip() { + struct Factory; + + impl HttpRequestFactory for Factory { + type R = MockRequest; + + fn create(&self, _: Url, _: Method) -> Result<MockRequest, LoadError> { + let mut e = GzEncoder::new(Vec::new(), Compression::Default); + e.write(b"Yay!").unwrap(); + let encoded_content = e.finish().unwrap(); + + let mut headers = Headers::new(); + headers.set_raw("Content-Encoding", vec![b"gzip".to_vec()]); + Ok(MockRequest::new(ResponseType::WithHeaders(encoded_content, headers))) + } + } + + let url = Url::parse("http://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + let load_data = LoadData::new(url.clone(), None); + let mut response = load::<MockRequest>(load_data, resource_mgr.clone(), None, &Factory).unwrap(); + + assert_eq!(read_response(&mut response), "Yay!"); +} + +#[test] +fn test_load_doesnt_send_request_body_on_any_redirect() { + struct Factory; + + impl HttpRequestFactory for Factory { + type R = AssertMustHaveBodyRequest; + + fn create(&self, url: Url, _: Method) -> Result<AssertMustHaveBodyRequest, LoadError> { + if url.domain().unwrap() == "mozilla.com" { + Ok( + AssertMustHaveBodyRequest::new( + ResponseType::Redirect("http://mozilla.org".to_string()), + Some(<[_]>::to_vec("Body on POST!".as_bytes())) + ) + ) + } else { + Ok( + AssertMustHaveBodyRequest::new( + ResponseType::Text(<[_]>::to_vec("Yay!".as_bytes())), + None + ) + ) + } + } + } + + let url = Url::parse("http://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + let mut load_data = LoadData::new(url.clone(), None); + load_data.data = Some(<[_]>::to_vec("Body on POST!".as_bytes())); + + + let _ = load::<AssertMustHaveBodyRequest>(load_data, resource_mgr, None, &Factory); +} + +#[test] +fn test_load_doesnt_add_host_to_sts_list_when_url_is_http_even_if_sts_headers_are_present() { + struct Factory; + + impl HttpRequestFactory for Factory { + type R = MockRequest; + + fn create(&self, _: Url, _: Method) -> Result<MockRequest, LoadError> { + let content = <[_]>::to_vec("Yay!".as_bytes()); + let mut headers = Headers::new(); + headers.set_raw("Strict-Transport-Security", vec![b"max-age=31536000".to_vec()]); + Ok(MockRequest::new(ResponseType::WithHeaders(content, headers))) + } + } + + let url = Url::parse("http://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + + let load_data = LoadData::new(url.clone(), None); + + let _ = load::<MockRequest>(load_data, resource_mgr.clone(), None, &Factory); + + let (tx, rx) = ipc::channel().unwrap(); + resource_mgr.send(ControlMsg::GetHostMustBeSecured("mozilla.com".to_string(), tx)).unwrap(); + + assert_eq!(rx.recv().unwrap(), false); +} + +#[test] +fn test_load_adds_host_to_sts_list_when_url_is_https_and_sts_headers_are_present() { + struct Factory; + + impl HttpRequestFactory for Factory { + type R = MockRequest; + + fn create(&self, _: Url, _: Method) -> Result<MockRequest, LoadError> { + let content = <[_]>::to_vec("Yay!".as_bytes()); + let mut headers = Headers::new(); + headers.set_raw("Strict-Transport-Security", vec![b"max-age=31536000".to_vec()]); + Ok(MockRequest::new(ResponseType::WithHeaders(content, headers))) + } + } + + let url = Url::parse("https://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + + let load_data = LoadData::new(url.clone(), None); + + let _ = load::<MockRequest>(load_data, resource_mgr.clone(), None, &Factory); + + let (tx, rx) = ipc::channel().unwrap(); + resource_mgr.send(ControlMsg::GetHostMustBeSecured("mozilla.com".to_string(), tx)).unwrap(); + + assert!(rx.recv().unwrap()); +} + +#[test] +fn test_load_sets_cookies_in_the_resource_manager_when_it_get_set_cookie_header_in_response() { + struct Factory; + + impl HttpRequestFactory for Factory { + type R = MockRequest; + + fn create(&self, _: Url, _: Method) -> Result<MockRequest, LoadError> { + let content = <[_]>::to_vec("Yay!".as_bytes()); + let mut headers = Headers::new(); + headers.set_raw("set-cookie", vec![b"mozillaIs=theBest".to_vec()]); + Ok(MockRequest::new(ResponseType::WithHeaders(content, headers))) + } + } + + let url = Url::parse("http://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + assert_cookie_for_domain(&resource_mgr, "http://mozilla.com", ""); + + let load_data = LoadData::new(url.clone(), None); + + let _ = load::<MockRequest>(load_data, resource_mgr.clone(), None, &Factory); + + assert_cookie_for_domain(&resource_mgr, "http://mozilla.com", "mozillaIs=theBest"); +} + +#[test] +fn test_load_sets_requests_cookies_header_for_url_by_getting_cookies_from_the_resource_manager() { + let url = Url::parse("http://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + resource_mgr.send(ControlMsg::SetCookiesForUrl(Url::parse("http://mozilla.com").unwrap(), + "mozillaIs=theBest".to_string(), + CookieSource::HTTP)).unwrap(); + + let mut load_data = LoadData::new(url.clone(), None); + load_data.data = Some(<[_]>::to_vec("Yay!".as_bytes())); + + let mut cookie = Headers::new(); + cookie.set_raw("Cookie".to_owned(), vec![<[_]>::to_vec("mozillaIs=theBest".as_bytes())]); + + let _ = load::<AssertRequestMustHaveHeaders>( + load_data.clone(), resource_mgr, None, + &AssertMustHaveHeadersRequestFactory { + expected_headers: cookie, + body: <[_]>::to_vec(&*load_data.data.unwrap()) + }); +} + +#[test] +fn test_load_sets_content_length_to_length_of_request_body() { + let content = "This is a request body"; + + let url = Url::parse("http://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + let mut load_data = LoadData::new(url.clone(), None); + load_data.data = Some(<[_]>::to_vec(content.as_bytes())); + + let mut content_len_headers = Headers::new(); + let content_len = format!("{}", content.len()); + content_len_headers.set_raw( + "Content-Length".to_owned(), vec![<[_]>::to_vec(&*content_len.as_bytes())] + ); + + let _ = load::<AssertRequestMustHaveHeaders>( + load_data.clone(), resource_mgr, None, + &AssertMustHaveHeadersRequestFactory { + expected_headers: content_len_headers, + body: <[_]>::to_vec(&*load_data.data.unwrap()) + }); +} + +#[test] +fn test_load_uses_explicit_accept_from_headers_in_load_data() { + let mut accept_headers = Headers::new(); + accept_headers.set_raw("Accept".to_owned(), vec![b"text/html".to_vec()]); + + let url = Url::parse("http://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + let mut load_data = LoadData::new(url.clone(), None); + load_data.data = Some(<[_]>::to_vec("Yay!".as_bytes())); + load_data.headers.set_raw("Accept".to_owned(), vec![b"text/html".to_vec()]); + + let _ = load::<AssertRequestMustHaveHeaders>(load_data, resource_mgr, None, &AssertMustHaveHeadersRequestFactory { + expected_headers: accept_headers, + body: <[_]>::to_vec("Yay!".as_bytes()) + }); +} + +#[test] +fn test_load_sets_default_accept_to_html_xhtml_xml_and_then_anything_else() { + let mut accept_headers = Headers::new(); + accept_headers.set_raw( + "Accept".to_owned(), vec![b"text/html, application/xhtml+xml, application/xml; q=0.9, */*; q=0.8".to_vec()] + ); + + let url = Url::parse("http://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + let mut load_data = LoadData::new(url.clone(), None); + load_data.data = Some(<[_]>::to_vec("Yay!".as_bytes())); + + let _ = load::<AssertRequestMustHaveHeaders>(load_data, resource_mgr, None, &AssertMustHaveHeadersRequestFactory { + expected_headers: accept_headers, + body: <[_]>::to_vec("Yay!".as_bytes()) + }); +} + +#[test] +fn test_load_uses_explicit_accept_encoding_from_load_data_headers() { + let mut accept_encoding_headers = Headers::new(); + accept_encoding_headers.set_raw("Accept-Encoding".to_owned(), vec![b"chunked".to_vec()]); + + let url = Url::parse("http://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + let mut load_data = LoadData::new(url.clone(), None); + load_data.data = Some(<[_]>::to_vec("Yay!".as_bytes())); + load_data.headers.set_raw("Accept-Encoding".to_owned(), vec![b"chunked".to_vec()]); + + let _ = load::<AssertRequestMustHaveHeaders>(load_data, resource_mgr, None, &AssertMustHaveHeadersRequestFactory { + expected_headers: accept_encoding_headers, + body: <[_]>::to_vec("Yay!".as_bytes()) + }); +} + +#[test] +fn test_load_sets_default_accept_encoding_to_gzip_and_deflate() { + let mut accept_encoding_headers = Headers::new(); + accept_encoding_headers.set_raw("Accept-Encoding".to_owned(), vec![b"gzip, deflate".to_vec()]); + + let url = Url::parse("http://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + let mut load_data = LoadData::new(url.clone(), None); + load_data.data = Some(<[_]>::to_vec("Yay!".as_bytes())); + + let _ = load::<AssertRequestMustHaveHeaders>(load_data, resource_mgr, None, &AssertMustHaveHeadersRequestFactory { + expected_headers: accept_encoding_headers, + body: <[_]>::to_vec("Yay!".as_bytes()) + }); +} + +#[test] +fn test_load_errors_when_there_a_redirect_loop() { + struct Factory; + + impl HttpRequestFactory for Factory { + type R = MockRequest; + + fn create(&self, url: Url, _: Method) -> Result<MockRequest, LoadError> { + if url.domain().unwrap() == "mozilla.com" { + Ok(MockRequest::new(ResponseType::Redirect("http://mozilla.org".to_string()))) + } else if url.domain().unwrap() == "mozilla.org" { + Ok(MockRequest::new(ResponseType::Redirect("http://mozilla.com".to_string()))) + } else { + panic!("unexpected host {:?}", url) + } + } + } + + let url = Url::parse("http://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + let load_data = LoadData::new(url.clone(), None); + + match load::<MockRequest>(load_data, resource_mgr, None, &Factory) { + Err(LoadError::InvalidRedirect(_, msg)) => { + assert_eq!(msg, "redirect loop"); + }, + _ => panic!("expected max redirects to fail") + } +} + +#[test] +fn test_load_errors_when_there_is_too_many_redirects() { + struct Factory; + + impl HttpRequestFactory for Factory { + type R = MockRequest; + + fn create(&self, url: Url, _: Method) -> Result<MockRequest, LoadError> { + if url.domain().unwrap() == "mozilla.com" { + Ok(MockRequest::new(ResponseType::Redirect(format!("{}/1", url.serialize())))) + } else { + panic!("unexpected host {:?}", url) + } + } + } + + let url = Url::parse("http://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + let load_data = LoadData::new(url.clone(), None); + + match load::<MockRequest>(load_data, resource_mgr, None, &Factory) { + Err(LoadError::MaxRedirects(url)) => { + assert_eq!(url.domain().unwrap(), "mozilla.com") + }, + _ => panic!("expected max redirects to fail") + } +} + +#[test] +fn test_load_follows_a_redirect() { + struct Factory; + + impl HttpRequestFactory for Factory { + type R = MockRequest; + + fn create(&self, url: Url, _: Method) -> Result<MockRequest, LoadError> { + if url.domain().unwrap() == "mozilla.com" { + Ok(MockRequest::new(ResponseType::Redirect("http://mozilla.org".to_string()))) + } else if url.domain().unwrap() == "mozilla.org" { + Ok( + MockRequest::new( + ResponseType::Text( + <[_]>::to_vec("Yay!".as_bytes()) + ) + ) + ) + } else { + panic!("unexpected host {:?}", url) + } + } + } + + let url = Url::parse("http://mozilla.com").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + let load_data = LoadData::new(url.clone(), None); + + match load::<MockRequest>(load_data, resource_mgr, None, &Factory) { + Err(e) => panic!("expected to follow a redirect {:?}", e), + Ok(mut lr) => { + let response = read_response(&mut lr); + assert_eq!(response, "Yay!".to_string()); + } + } +} + +struct DontConnectFactory; + +impl HttpRequestFactory for DontConnectFactory { + type R = MockRequest; + + fn create(&self, url: Url, _: Method) -> Result<MockRequest, LoadError> { + Err(LoadError::Connection(url, "should not have connected".to_string())) + } +} + +#[test] +fn test_load_errors_when_scheme_is_not_http_or_https() { + let url = Url::parse("ftp://not-supported").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + let load_data = LoadData::new(url.clone(), None); + + match load::<MockRequest>(load_data, resource_mgr, None, &DontConnectFactory) { + Err(LoadError::UnsupportedScheme(_)) => {} + _ => panic!("expected ftp scheme to be unsupported") + } +} + +#[test] +fn test_load_errors_when_viewing_source_and_inner_url_scheme_is_not_http_or_https() { + let url = Url::parse("view-source:ftp://not-supported").unwrap(); + let resource_mgr = new_resource_task("Test-agent".to_string(), None); + let load_data = LoadData::new(url.clone(), None); + + match load::<MockRequest>(load_data, resource_mgr, None, &DontConnectFactory) { + Err(LoadError::UnsupportedScheme(_)) => {} + _ => panic!("expected ftp scheme to be unsupported") + } +} diff --git a/tests/unit/net/lib.rs b/tests/unit/net/lib.rs index 3f737fc1783..8f7b0d1511f 100644 --- a/tests/unit/net/lib.rs +++ b/tests/unit/net/lib.rs @@ -10,9 +10,12 @@ extern crate net_traits; extern crate url; extern crate util; extern crate time; +extern crate hyper; +extern crate flate2; #[cfg(test)] mod cookie; #[cfg(test)] mod data_loader; #[cfg(test)] mod mime_classifier; #[cfg(test)] mod resource_task; #[cfg(test)] mod hsts; +#[cfg(test)] mod http_loader; diff --git a/tests/unit/net/mime_classifier.rs b/tests/unit/net/mime_classifier.rs index b2e48926901..2c62878872e 100644 --- a/tests/unit/net/mime_classifier.rs +++ b/tests/unit/net/mime_classifier.rs @@ -434,7 +434,14 @@ fn test_sniff_utf_8_bom() { #[test] fn test_sniff_rss_feed() { + // RSS feeds test_sniff_full(&PathBuf::from("text/xml/feed.rss"), "application", "rss+xml", Some(("text", "html"))); + test_sniff_full(&PathBuf::from("text/xml/rdf_rss.xml"), "application", "rss+xml", Some(("text", "html"))); + // Not RSS feeds + test_sniff_full(&PathBuf::from("text/xml/rdf_rss_ko_1.xml"), "text", "html", Some(("text", "html"))); + test_sniff_full(&PathBuf::from("text/xml/rdf_rss_ko_2.xml"), "text", "html", Some(("text", "html"))); + test_sniff_full(&PathBuf::from("text/xml/rdf_rss_ko_3.xml"), "text", "html", Some(("text", "html"))); + test_sniff_full(&PathBuf::from("text/xml/rdf_rss_ko_4.xml"), "text", "html", Some(("text", "html"))); } #[test] diff --git a/tests/unit/net/parsable_mime/text/xml/rdf_rss.xml b/tests/unit/net/parsable_mime/text/xml/rdf_rss.xml new file mode 100644 index 00000000000..4c58f82974e --- /dev/null +++ b/tests/unit/net/parsable_mime/text/xml/rdf_rss.xml @@ -0,0 +1,7 @@ +<!-- Good format for a "RDF feed" --> +<?xml version="1.0"?> +<rdf:RDF + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns="http://purl.org/rss/1.0/" +> +</rdf:RDF>
\ No newline at end of file diff --git a/tests/unit/net/parsable_mime/text/xml/rdf_rss_ko_1.xml b/tests/unit/net/parsable_mime/text/xml/rdf_rss_ko_1.xml new file mode 100644 index 00000000000..f6e486c5960 --- /dev/null +++ b/tests/unit/net/parsable_mime/text/xml/rdf_rss_ko_1.xml @@ -0,0 +1,7 @@ +<!-- Bad format for a "RDF feed" (space between "rdf:" and "RDF") --> +<?xml version="1.0"?> +<rdf: RDF + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns="http://purl.org/rss/1.0/" +> +</rdf:RDF>
\ No newline at end of file diff --git a/tests/unit/net/parsable_mime/text/xml/rdf_rss_ko_2.xml b/tests/unit/net/parsable_mime/text/xml/rdf_rss_ko_2.xml new file mode 100644 index 00000000000..be8414382e5 --- /dev/null +++ b/tests/unit/net/parsable_mime/text/xml/rdf_rss_ko_2.xml @@ -0,0 +1,3 @@ +<!-- Bad format for a "RDF feed" (2 missing URLs) --> +<?xml version="1.0"?> +<rdf:RDF/>
\ No newline at end of file diff --git a/tests/unit/net/parsable_mime/text/xml/rdf_rss_ko_3.xml b/tests/unit/net/parsable_mime/text/xml/rdf_rss_ko_3.xml new file mode 100644 index 00000000000..5f0f03f1e2d --- /dev/null +++ b/tests/unit/net/parsable_mime/text/xml/rdf_rss_ko_3.xml @@ -0,0 +1,6 @@ +<!-- Bad format for a "RDF feed" (one missing URL) --> +<?xml version="1.0"?> +<rdf:RDF + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" +> +</rdf:RDF>
\ No newline at end of file diff --git a/tests/unit/net/parsable_mime/text/xml/rdf_rss_ko_4.xml b/tests/unit/net/parsable_mime/text/xml/rdf_rss_ko_4.xml new file mode 100644 index 00000000000..c06a80cf1f8 --- /dev/null +++ b/tests/unit/net/parsable_mime/text/xml/rdf_rss_ko_4.xml @@ -0,0 +1,7 @@ +<!-- Bad format for a "RDF feed" (unexpected space in first URL) --> +<?xml version="1.0"?> +<rdf:RDF + xmlns:rdf="http: //www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns="http://purl.org/rss/1.0/" +> +</rdf:RDF>
\ No newline at end of file diff --git a/tests/unit/script/textinput.rs b/tests/unit/script/textinput.rs index 313ab62fe0a..74891b52cae 100644 --- a/tests/unit/script/textinput.rs +++ b/tests/unit/script/textinput.rs @@ -9,9 +9,9 @@ use msg::constellation_msg::{Key, KeyModifiers}; -#[cfg(target_os="macos")] +#[cfg(target_os = "macos")] use msg::constellation_msg::SUPER; -#[cfg(not(target_os="macos"))] +#[cfg(not(target_os = "macos"))] use msg::constellation_msg::CONTROL; use script::clipboard_provider::DummyClipboardContext; @@ -194,9 +194,9 @@ fn test_textinput_set_content() { #[test] fn test_clipboard_paste() { - #[cfg(target_os="macos")] + #[cfg(target_os = "macos")] const MODIFIERS: KeyModifiers = SUPER; - #[cfg(not(target_os="macos"))] + #[cfg(not(target_os = "macos"))] const MODIFIERS: KeyModifiers = CONTROL; let mut textinput = TextInput::new(Lines::Single, "defg".to_owned(), DummyClipboardContext::new("abc")); @@ -205,3 +205,57 @@ fn test_clipboard_paste() { textinput.handle_keydown_aux(Key::V, MODIFIERS); assert_eq!(textinput.get_content(), "abcdefg"); } + +#[test] +fn test_textinput_cursor_position_correct_after_clearing_selection() { + let mut textinput = TextInput::new(Lines::Single, "abcdef".to_owned(), DummyClipboardContext::new("")); + + // Single line - Forward + textinput.adjust_horizontal(3, Selection::Selected); + textinput.adjust_horizontal(1, Selection::NotSelected); + assert_eq!(textinput.edit_point.index, 3); + + textinput.adjust_horizontal(-3, Selection::NotSelected); + textinput.adjust_horizontal(3, Selection::Selected); + textinput.adjust_horizontal_by_one(Direction::Forward, Selection::NotSelected); + assert_eq!(textinput.edit_point.index, 3); + + // Single line - Backward + textinput.adjust_horizontal(-3, Selection::NotSelected); + textinput.adjust_horizontal(3, Selection::Selected); + textinput.adjust_horizontal(-1, Selection::NotSelected); + assert_eq!(textinput.edit_point.index, 0); + + textinput.adjust_horizontal(-3, Selection::NotSelected); + textinput.adjust_horizontal(3, Selection::Selected); + textinput.adjust_horizontal_by_one(Direction::Backward, Selection::NotSelected); + assert_eq!(textinput.edit_point.index, 0); + + + let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned(), DummyClipboardContext::new("")); + + // Multiline - Forward + textinput.adjust_horizontal(4, Selection::Selected); + textinput.adjust_horizontal(1, Selection::NotSelected); + assert_eq!(textinput.edit_point.index, 0); + assert_eq!(textinput.edit_point.line, 1); + + textinput.adjust_horizontal(-4, Selection::NotSelected); + textinput.adjust_horizontal(4, Selection::Selected); + textinput.adjust_horizontal_by_one(Direction::Forward, Selection::NotSelected); + assert_eq!(textinput.edit_point.index, 0); + assert_eq!(textinput.edit_point.line, 1); + + // Multiline - Backward + textinput.adjust_horizontal(-4, Selection::NotSelected); + textinput.adjust_horizontal(4, Selection::Selected); + textinput.adjust_horizontal(-1, Selection::NotSelected); + assert_eq!(textinput.edit_point.index, 0); + assert_eq!(textinput.edit_point.line, 0); + + textinput.adjust_horizontal(-4, Selection::NotSelected); + textinput.adjust_horizontal(4, Selection::Selected); + textinput.adjust_horizontal_by_one(Direction::Backward, Selection::NotSelected); + assert_eq!(textinput.edit_point.index, 0); + assert_eq!(textinput.edit_point.line, 0); +} diff --git a/tests/wpt/include.ini b/tests/wpt/include.ini index cbd4f34dbf1..1c00f118a49 100644 --- a/tests/wpt/include.ini +++ b/tests/wpt/include.ini @@ -73,18 +73,6 @@ skip: true skip: false [workers] skip: false - [constructors] - skip: false - [SharedWorker] - skip: true - [semantics] - skip: false - [navigation] - skip: true - [reporting-errors] - skip: true - [structured-clone] - skip: true [XMLHttpRequest] skip: false [old-tests] diff --git a/tests/wpt/metadata-css/css21_dev/html4/abs-pos-non-replaced-vlr-069.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/abs-pos-non-replaced-vlr-069.htm.ini deleted file mode 100644 index 3393052ea38..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/abs-pos-non-replaced-vlr-069.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[abs-pos-non-replaced-vlr-069.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/abs-pos-non-replaced-vlr-177.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/abs-pos-non-replaced-vlr-177.htm.ini deleted file mode 100644 index e1c3b4f4d05..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/abs-pos-non-replaced-vlr-177.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[abs-pos-non-replaced-vlr-177.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/abs-pos-non-replaced-vlr-193.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/abs-pos-non-replaced-vlr-193.htm.ini deleted file mode 100644 index 5e854311604..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/abs-pos-non-replaced-vlr-193.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[abs-pos-non-replaced-vlr-193.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/abs-pos-non-replaced-vrl-176.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/abs-pos-non-replaced-vrl-176.htm.ini deleted file mode 100644 index eefff3cffd7..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/abs-pos-non-replaced-vrl-176.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[abs-pos-non-replaced-vrl-176.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/abs-pos-non-replaced-vrl-192.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/abs-pos-non-replaced-vrl-192.htm.ini deleted file mode 100644 index ae93a9937bd..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/abs-pos-non-replaced-vrl-192.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[abs-pos-non-replaced-vrl-192.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/absolute-replaced-width-015.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/absolute-replaced-width-015.htm.ini deleted file mode 100644 index d17e0d4951e..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/absolute-replaced-width-015.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[absolute-replaced-width-015.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata/html/dom/interfaces.html.ini b/tests/wpt/metadata/html/dom/interfaces.html.ini index cb01cd8f47d..080c6d4e7d5 100644 --- a/tests/wpt/metadata/html/dom/interfaces.html.ini +++ b/tests/wpt/metadata/html/dom/interfaces.html.ini @@ -8430,9 +8430,6 @@ [Worker interface: operation terminate()] expected: FAIL - [Worker interface: attribute onerror] - expected: FAIL - [SharedWorker interface: existence and properties of interface object] expected: FAIL diff --git a/tests/wpt/metadata/url/interfaces.html.ini b/tests/wpt/metadata/url/interfaces.html.ini index f1a85fdfc35..17a665ab572 100644 --- a/tests/wpt/metadata/url/interfaces.html.ini +++ b/tests/wpt/metadata/url/interfaces.html.ini @@ -3,42 +3,12 @@ [URL interface: operation domainToUnicode(ScalarValueString)] expected: FAIL - [URL interface: attribute href] - expected: FAIL - [URL interface: attribute origin] expected: FAIL - [URL interface: attribute protocol] - expected: FAIL - - [URL interface: attribute username] - expected: FAIL - - [URL interface: attribute password] - expected: FAIL - - [URL interface: attribute host] - expected: FAIL - - [URL interface: attribute hostname] - expected: FAIL - - [URL interface: attribute port] - expected: FAIL - - [URL interface: attribute pathname] - expected: FAIL - - [URL interface: attribute search] - expected: FAIL - [URL interface: attribute searchParams] expected: FAIL - [URL interface: attribute hash] - expected: FAIL - [URL interface: new URL("http://foo") must inherit property "origin" with the proper type (3)] expected: FAIL diff --git a/tests/wpt/metadata/workers/constructors/SharedWorker/__dir__.ini b/tests/wpt/metadata/workers/constructors/SharedWorker/__dir__.ini new file mode 100644 index 00000000000..168df4e0769 --- /dev/null +++ b/tests/wpt/metadata/workers/constructors/SharedWorker/__dir__.ini @@ -0,0 +1 @@ +disabled: SharedWorker diff --git a/tests/wpt/metadata/workers/semantics/navigation/001.html.ini b/tests/wpt/metadata/workers/semantics/navigation/001.html.ini new file mode 100644 index 00000000000..b3538369e2e --- /dev/null +++ b/tests/wpt/metadata/workers/semantics/navigation/001.html.ini @@ -0,0 +1,3 @@ +[001.html] + type: testharness + expected: TIMEOUT diff --git a/tests/wpt/metadata/workers/semantics/navigation/002.html.ini b/tests/wpt/metadata/workers/semantics/navigation/002.html.ini new file mode 100644 index 00000000000..23e8bb1452d --- /dev/null +++ b/tests/wpt/metadata/workers/semantics/navigation/002.html.ini @@ -0,0 +1,5 @@ +[002.html] + type: testharness + [navigating 2] + expected: FAIL + diff --git a/tests/wpt/metadata/workers/semantics/reporting-errors/__dir__.ini b/tests/wpt/metadata/workers/semantics/reporting-errors/__dir__.ini new file mode 100644 index 00000000000..168df4e0769 --- /dev/null +++ b/tests/wpt/metadata/workers/semantics/reporting-errors/__dir__.ini @@ -0,0 +1 @@ +disabled: SharedWorker diff --git a/tests/wpt/metadata/workers/semantics/structured-clone/__dir__.ini b/tests/wpt/metadata/workers/semantics/structured-clone/__dir__.ini new file mode 100644 index 00000000000..879844e9bd2 --- /dev/null +++ b/tests/wpt/metadata/workers/semantics/structured-clone/__dir__.ini @@ -0,0 +1 @@ +disabled: Broken somehow |