From 726f7d72092d5b8908cc8d8fdefe51ebea8dadf8 Mon Sep 17 00:00:00 2001 From: Utsav Oza Date: Fri, 22 May 2020 15:51:36 +0530 Subject: [PATCH 01/13] Check for valid arguments before processing canvas.fillText --- Cargo.lock | 1 + components/canvas/Cargo.toml | 3 +- components/canvas/canvas_data.rs | 70 +++++++++++++++---------------- components/script/canvas_state.rs | 8 +++- 4 files changed, 44 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0b2f7fac4c..bb0d5ddf870 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -504,6 +504,7 @@ dependencies = [ "cssparser", "euclid", "fnv", + "font-kit", "gleam 0.11.0", "half", "ipc-channel", diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index 5486dc4c9ea..918e2971f9a 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -22,6 +22,7 @@ canvas_traits = { path = "../canvas_traits" } crossbeam-channel = "0.4" cssparser = "0.27" euclid = "0.20" +font-kit = "0.7" fnv = "1.0" gleam = "0.11" half = "1" @@ -30,7 +31,7 @@ log = "0.4" lyon_geom = "0.14" num-traits = "0.2" pixels = { path = "../pixels" } -raqote = "0.8" +raqote = { version = "0.8", features = ["text"] } servo_config = { path = "../config" } sparkle = "0.1.24" # NOTE: the sm-angle feature only enables ANGLE on Windows, not other platforms! diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 1a4d1ccfc98..c3e02b67463 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -362,7 +362,7 @@ pub enum Filter { pub struct CanvasData<'a> { backend: Box, - drawtarget: Box, + draw_target: Box, path_state: Option, state: CanvasPaintState<'a>, saved_states: Vec>, @@ -372,7 +372,7 @@ pub struct CanvasData<'a> { old_image_key: Option, /// An old webrender image key that can be deleted when the current epoch ends. very_old_image_key: Option, - pub canvas_id: CanvasId, + _canvas_id: CanvasId, } fn create_backend() -> Box { @@ -390,7 +390,7 @@ impl<'a> CanvasData<'a> { let draw_target = backend.create_drawtarget(size); CanvasData { backend, - drawtarget: draw_target, + draw_target, path_state: None, state: CanvasPaintState::new(antialias), saved_states: vec![], @@ -398,7 +398,7 @@ impl<'a> CanvasData<'a> { image_key: None, old_image_key: None, very_old_image_key: None, - canvas_id: canvas_id, + _canvas_id: canvas_id, } } @@ -440,7 +440,7 @@ impl<'a> CanvasData<'a> { // TODO(pylbrecht) pass another closure for raqote self.draw_with_shadow(&rect, writer); } else { - writer(&mut *self.drawtarget); + writer(&mut *self.draw_target); } } @@ -451,8 +451,8 @@ impl<'a> CanvasData<'a> { pub fn restore_context_state(&mut self) { if let Some(state) = self.saved_states.pop() { let _ = mem::replace(&mut self.state, state); - self.drawtarget.set_transform(&self.state.transform); - self.drawtarget.pop_clip(); + self.draw_target.set_transform(&self.state.transform); + self.draw_target.pop_clip(); } } @@ -513,7 +513,7 @@ impl<'a> CanvasData<'a> { ); }); } else { - self.drawtarget.fill_rect( + self.draw_target.fill_rect( &draw_rect, self.state.fill_style.clone(), Some(&self.state.draw_options), @@ -522,7 +522,7 @@ impl<'a> CanvasData<'a> { } pub fn clear_rect(&mut self, rect: &Rect) { - self.drawtarget.clear_rect(rect); + self.draw_target.clear_rect(rect); } pub fn stroke_rect(&mut self, rect: &Rect) { @@ -542,7 +542,7 @@ impl<'a> CanvasData<'a> { } else if rect.size.width == 0. || rect.size.height == 0. { let mut stroke_opts = self.state.stroke_opts.clone(); stroke_opts.set_line_cap(LineCapStyle::Butt); - self.drawtarget.stroke_line( + self.draw_target.stroke_line( rect.origin, rect.bottom_right(), self.state.stroke_style.clone(), @@ -550,7 +550,7 @@ impl<'a> CanvasData<'a> { &self.state.draw_options, ); } else { - self.drawtarget.stroke_rect( + self.draw_target.stroke_rect( rect, self.state.stroke_style.clone(), &self.state.stroke_opts, @@ -572,7 +572,7 @@ impl<'a> CanvasData<'a> { // If there's no record of any path yet, create a new builder in user-space. if self.path_state.is_none() { self.path_state = Some(PathState::UserSpacePathBuilder( - self.drawtarget.create_path_builder(), + self.draw_target.create_path_builder(), None, )); } @@ -607,7 +607,7 @@ impl<'a> CanvasData<'a> { let new_state = match *self.path_state.as_mut().unwrap() { PathState::DeviceSpacePathBuilder(ref mut builder) => { let path = builder.finish(); - let inverse = match self.drawtarget.get_transform().inverse() { + let inverse = match self.draw_target.get_transform().inverse() { Some(m) => m, None => { warn!("Couldn't invert canvas transformation."); @@ -639,7 +639,7 @@ impl<'a> CanvasData<'a> { } self.ensure_path(); - self.drawtarget.fill( + self.draw_target.fill( &self.path().clone(), self.state.fill_style.clone(), &self.state.draw_options, @@ -652,7 +652,7 @@ impl<'a> CanvasData<'a> { } self.ensure_path(); - self.drawtarget.stroke( + self.draw_target.stroke( &self.path().clone(), self.state.stroke_style.clone(), &self.state.stroke_opts, @@ -663,7 +663,7 @@ impl<'a> CanvasData<'a> { pub fn clip(&mut self) { self.ensure_path(); let path = self.path().clone(); - self.drawtarget.push_clip(&path); + self.draw_target.push_clip(&path); } pub fn is_point_in_path( @@ -676,7 +676,7 @@ impl<'a> CanvasData<'a> { self.ensure_path(); let result = match self.path_state.as_ref() { Some(PathState::UserSpacePath(ref path, ref transform)) => { - let target_transform = self.drawtarget.get_transform(); + let target_transform = self.draw_target.get_transform(); let path_transform = transform.as_ref().unwrap_or(&target_transform); path.contains_point(x, y, path_transform) }, @@ -696,7 +696,7 @@ impl<'a> CanvasData<'a> { fn path_builder(&mut self) -> PathBuilderRef { if self.path_state.is_none() { self.path_state = Some(PathState::UserSpacePathBuilder( - self.drawtarget.create_path_builder(), + self.draw_target.create_path_builder(), None, )); } @@ -738,7 +738,7 @@ impl<'a> CanvasData<'a> { PathState::DeviceSpacePathBuilder(ref mut builder) => { return PathBuilderRef { builder, - transform: self.drawtarget.get_transform(), + transform: self.draw_target.get_transform(), }; }, _ => unreachable!(), @@ -752,7 +752,7 @@ impl<'a> CanvasData<'a> { }, PathState::DeviceSpacePathBuilder(ref mut builder) => PathBuilderRef { builder, - transform: self.drawtarget.get_transform(), + transform: self.draw_target.get_transform(), }, PathState::UserSpacePathBuilder(..) | PathState::UserSpacePath(..) => unreachable!(), } @@ -882,12 +882,12 @@ impl<'a> CanvasData<'a> { pub fn set_fill_style(&mut self, style: FillOrStrokeStyle) { self.backend - .set_fill_style(style, &mut self.state, &*self.drawtarget); + .set_fill_style(style, &mut self.state, &*self.draw_target); } pub fn set_stroke_style(&mut self, style: FillOrStrokeStyle) { self.backend - .set_stroke_style(style, &mut self.state, &*self.drawtarget); + .set_stroke_style(style, &mut self.state, &*self.draw_target); } pub fn set_line_width(&mut self, width: f32) { @@ -907,7 +907,7 @@ impl<'a> CanvasData<'a> { } pub fn get_transform(&self) -> Transform2D { - self.drawtarget.get_transform() + self.draw_target.get_transform() } pub fn set_transform(&mut self, transform: &Transform2D) { @@ -918,12 +918,12 @@ impl<'a> CanvasData<'a> { Some(PathState::UserSpacePathBuilder(_, ref mut transform)) | Some(PathState::UserSpacePath(_, ref mut transform)) => { if transform.is_none() { - *transform = Some(self.drawtarget.get_transform()); + *transform = Some(self.draw_target.get_transform()); } }, } self.state.transform = transform.clone(); - self.drawtarget.set_transform(transform) + self.draw_target.set_transform(transform) } pub fn set_global_alpha(&mut self, alpha: f32) { @@ -935,7 +935,7 @@ impl<'a> CanvasData<'a> { } pub fn recreate(&mut self, size: Size2D) { - self.drawtarget = self + self.draw_target = self .backend .create_drawtarget(Size2D::new(size.width, size.height)); self.state = self.backend.recreate_paint_state(&self.state); @@ -954,7 +954,7 @@ impl<'a> CanvasData<'a> { } pub fn send_pixels(&mut self, chan: IpcSender) { - self.drawtarget.snapshot_data(&|bytes| { + self.draw_target.snapshot_data(&|bytes| { let data = IpcSharedMemory::from_bytes(bytes); chan.send(data).unwrap(); vec![] @@ -962,7 +962,7 @@ impl<'a> CanvasData<'a> { } pub fn send_data(&mut self, chan: IpcSender) { - let size = self.drawtarget.get_size(); + let size = self.draw_target.get_size(); let descriptor = webrender_api::ImageDescriptor { size: webrender_api::units::DeviceIntSize::new(size.width, size.height), @@ -971,7 +971,7 @@ impl<'a> CanvasData<'a> { offset: 0, flags: webrender_api::ImageDescriptorFlags::empty(), }; - let data = self.drawtarget.snapshot_data_owned(); + let data = self.draw_target.snapshot_data_owned(); let data = webrender_api::ImageData::Raw(Arc::new(data)); let mut updates = vec![]; @@ -1009,14 +1009,14 @@ impl<'a> CanvasData<'a> { assert_eq!(rect.size.area() as usize, imagedata.len() / 4); pixels::rgba8_byte_swap_and_premultiply_inplace(&mut imagedata); let source_surface = self - .drawtarget + .draw_target .create_source_surface_from_data( &imagedata, rect.size.to_i32(), rect.size.width as i32 * 4, ) .unwrap(); - self.drawtarget.copy_surface( + self.draw_target.copy_surface( source_surface, Rect::from_size(rect.size.to_i32()), rect.origin.to_i32(), @@ -1048,12 +1048,12 @@ impl<'a> CanvasData<'a> { } fn create_draw_target_for_shadow(&self, source_rect: &Rect) -> Box { - let mut draw_target = self.drawtarget.create_similar_draw_target( + let mut draw_target = self.draw_target.create_similar_draw_target( &Size2D::new( source_rect.size.width as i32, source_rect.size.height as i32, ), - self.drawtarget.get_format(), + self.draw_target.get_format(), ); let matrix = Transform2D::identity() .pre_translate(-source_rect.origin.to_vector().cast::()) @@ -1069,7 +1069,7 @@ impl<'a> CanvasData<'a> { let shadow_src_rect = self.state.transform.transform_rect(rect); let mut new_draw_target = self.create_draw_target_for_shadow(&shadow_src_rect); draw_shadow_source(&mut *new_draw_target); - self.drawtarget.draw_surface_with_shadow( + self.draw_target.draw_surface_with_shadow( new_draw_target.snapshot(), &Point2D::new( shadow_src_rect.origin.x as f32, @@ -1098,7 +1098,7 @@ impl<'a> CanvasData<'a> { return vec![]; } - self.drawtarget.snapshot_data(&|bytes| { + self.draw_target.snapshot_data(&|bytes| { pixels::rgba8_get_rect(bytes, canvas_size, read_rect).into_owned() }) } diff --git a/components/script/canvas_state.rs b/components/script/canvas_state.rs index 2376d6f8d85..dac2bb04924 100644 --- a/components/script/canvas_state.rs +++ b/components/script/canvas_state.rs @@ -988,9 +988,13 @@ impl CanvasState { // https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext pub fn fill_text(&self, text: DOMString, x: f64, y: f64, max_width: Option) { - let parsed_text: String = text.into(); + let is_max_width_finite = max_width.map_or(true, |max_width| max_width.is_finite()); + if !(x.is_finite() && y.is_finite() && is_max_width_finite) { + return; + } + let style = self.state.borrow().fill_style.to_fill_or_stroke_style(); - self.send_canvas_2d_msg(Canvas2dMsg::FillText(parsed_text, x, y, max_width, style)); + self.send_canvas_2d_msg(Canvas2dMsg::FillText(text.into(), x, y, max_width, style)); } // https://html.spec.whatwg.org/multipage/#textmetrics From f161ab8e57b0149b368f892f11c83d953c55dd5a Mon Sep 17 00:00:00 2001 From: Utsav Oza Date: Sat, 23 May 2020 22:03:04 +0530 Subject: [PATCH 02/13] Basic implementation of canvas.fillText --- components/canvas/canvas_data.rs | 36 +++++++++++++++++++++++++---- components/canvas/raqote_backend.rs | 29 +++++++++++++++++++++++ 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index c3e02b67463..a3616f7e3da 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -264,6 +264,15 @@ pub trait GenericDrawTarget { operator: CompositionOp, ); fn fill(&mut self, path: &Path, pattern: Pattern, draw_options: &DrawOptions); + fn fill_text( + &mut self, + text: String, + x: f64, + y: f64, + max_width: Option, + pattern: Pattern, + draw_options: &DrawOptions, + ); fn fill_rect(&mut self, rect: &Rect, pattern: Pattern, draw_options: Option<&DrawOptions>); fn get_format(&self) -> SurfaceFormat; fn get_size(&self) -> Size2D; @@ -456,10 +465,29 @@ impl<'a> CanvasData<'a> { } } - pub fn fill_text(&self, text: String, x: f64, y: f64, max_width: Option) { - error!( - "Unimplemented canvas2d.fillText. Values received: {}, {}, {}, {:?}.", - text, x, y, max_width + pub fn fill_text(&mut self, text: String, x: f64, y: f64, max_width: Option) { + // 1. If maxWidth was provided but is less than or equal to zero or equal to NaN, + // then return an empty array. + if max_width.map_or(false, |max_width| max_width <= 0.) { + return; + } + + // 2. Replace all ASCII whitespace in text with U+0020 SPACE characters. + let text = text + .chars() + .map(|c| match c { + ' ' | '\t' | '\n' | '\r' | '\x0C' => '\x20', + _ => c, + }) + .collect(); + + self.draw_target.fill_text( + text, + x, + y, + max_width, + self.state.fill_style.clone(), + &self.state.draw_options, ); } diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs index 72eae8b40c8..c732d686472 100644 --- a/components/canvas/raqote_backend.rs +++ b/components/canvas/raqote_backend.rs @@ -13,6 +13,9 @@ use canvas_traits::canvas::*; use cssparser::RGBA; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; use euclid::Angle; +use font_kit::family_name::FamilyName; +use font_kit::properties::Properties; +use font_kit::source::SystemSource; use lyon_geom::Arc; use raqote::PathOp; use std::marker::PhantomData; @@ -513,6 +516,32 @@ impl GenericDrawTarget for raqote::DrawTarget { ), } } + + fn fill_text( + &mut self, + text: String, + x: f64, + y: f64, + max_width: Option, + pattern: canvas_data::Pattern, + draw_options: &DrawOptions, + ) { + let font = SystemSource::new() + .select_best_match(&[FamilyName::SansSerif], &Properties::new()) + .unwrap() + .load() + .unwrap(); + + self.draw_text( + &font, + 24., + &text, + Point2D::new(x as f32, y as f32), + &pattern.source(), + draw_options.as_raqote(), + ); + } + fn fill_rect( &mut self, rect: &Rect, From 7883718c125f2580490254efdf0aac952b50ce3d Mon Sep 17 00:00:00 2001 From: Utsav Oza Date: Thu, 28 May 2020 00:28:42 +0530 Subject: [PATCH 03/13] Query layout to resolve canvas font property value --- components/canvas/raqote_backend.rs | 2 +- components/layout/query.rs | 81 ++++++++++++++++++- components/layout_thread/lib.rs | 26 +++++- components/layout_thread_2020/lib.rs | 1 + components/script/canvas_state.rs | 10 +++ .../script/dom/canvasrenderingcontext2d.rs | 11 +++ .../dom/offscreencanvasrenderingcontext2d.rs | 11 +++ .../webidls/CanvasRenderingContext2D.webidl | 2 +- components/script/dom/window.rs | 19 ++++- components/script_layout_interface/message.rs | 3 + components/script_layout_interface/rpc.rs | 4 + 11 files changed, 161 insertions(+), 9 deletions(-) diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs index c732d686472..451656d7708 100644 --- a/components/canvas/raqote_backend.rs +++ b/components/canvas/raqote_backend.rs @@ -522,7 +522,7 @@ impl GenericDrawTarget for raqote::DrawTarget { text: String, x: f64, y: f64, - max_width: Option, + _max_width: Option, pattern: canvas_data::Pattern, draw_options: &DrawOptions, ) { diff --git a/components/layout/query.rs b/components/layout/query.rs index a5f66cb9dcd..b78a642ec1d 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -29,6 +29,8 @@ use script_layout_interface::wrapper_traits::{ use script_layout_interface::{LayoutElementType, LayoutNodeType}; use script_traits::LayoutMsg as ConstellationMsg; use script_traits::UntrustedNodeAddress; +use servo_arc::Arc as ServoArc; +use servo_url::ServoUrl; use std::cmp::{max, min}; use std::ops::Deref; use std::sync::{Arc, Mutex}; @@ -38,9 +40,13 @@ use style::computed_values::visibility::T as Visibility; use style::context::{StyleContext, ThreadLocalStyleContext}; use style::dom::TElement; use style::logical_geometry::{BlockFlowDirection, InlineBaseDirection, WritingMode}; -use style::properties::{style_structs, LonghandId, PropertyDeclarationId, PropertyId}; +use style::properties::{ + parse_one_declaration_into, style_structs, ComputedValues, Importance, LonghandId, + PropertyDeclarationBlock, PropertyDeclarationId, PropertyId, SourcePropertyDeclaration, +}; use style::selector_parser::PseudoElement; -use style_traits::{CSSPixel, ToCss}; +use style::shared_lock::SharedRwLock; +use style_traits::{CSSPixel, ParsingMode, ToCss}; use webrender_api::ExternalScrollId; /// Mutable data belonging to the LayoutThread. @@ -73,6 +79,9 @@ pub struct LayoutThreadData { /// A queued response for the resolved style property of an element. pub resolved_style_response: String, + /// A queued response for the resolved font style for canvas. + pub parse_font_response: Option>, + /// A queued response for the offset parent/rect of a node. pub offset_parent_response: OffsetParentResponse, @@ -170,6 +179,12 @@ impl LayoutRPC for LayoutRPCImpl { ResolvedStyleResponse(rw_data.resolved_style_response.clone()) } + fn parsed_font(&self) -> Option> { + let &LayoutRPCImpl(ref rw_data) = self; + let rw_data = rw_data.lock().unwrap(); + rw_data.parse_font_response.clone() + } + fn offset_parent(&self) -> OffsetParentResponse { let &LayoutRPCImpl(ref rw_data) = self; let rw_data = rw_data.lock().unwrap(); @@ -735,6 +750,68 @@ pub fn process_node_scroll_area_request( } } +pub fn process_parse_font_request<'dom, E>( + context: &LayoutContext, + node: E, + font_value: &str, + property: &PropertyId, + url_data: ServoUrl, + shared_lock: &SharedRwLock, +) -> Option> +where + E: LayoutNode<'dom>, +{ + use style::stylist::RuleInclusion; + use style::traversal::resolve_style; + + // 1. Parse the given font property value + let quirks_mode = context.style_context.quirks_mode(); + let mut declarations = SourcePropertyDeclaration::new(); + let result = parse_one_declaration_into( + &mut declarations, + property.clone(), + font_value, + &url_data, + None, + ParsingMode::DEFAULT, + quirks_mode, + ); + let declarations = match result { + Ok(()) => { + let mut block = PropertyDeclarationBlock::new(); + block.extend(declarations.drain(), Importance::Normal); + block + }, + Err(_) => return None, + }; + + // 2. Get resolved styles for the parent element + let element = node.as_element().unwrap(); + let parent_style = if element.has_data() { + node.to_threadsafe().as_element().unwrap().resolved_style() + } else { + let mut tlc = ThreadLocalStyleContext::new(&context.style_context); + let mut context = StyleContext { + shared: &context.style_context, + thread_local: &mut tlc, + }; + let styles = resolve_style(&mut context, element, RuleInclusion::All, None); + styles.primary().clone() + }; + + // 3. Resolve the parsed value with resolved styles of the parent element + Some( + context + .style_context + .stylist + .compute_for_declarations::( + &context.style_context.guards, + &*parent_style, + ServoArc::new(shared_lock.wrap(declarations)), + ), + ) +} + /// Return the resolved value of property for a given (pseudo)element. /// pub fn process_resolved_style_request<'dom>( diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 6819027564c..9ae955789a9 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -49,12 +49,12 @@ use layout::flow_ref::FlowRef; use layout::incremental::{RelayoutMode, SpecialRestyleDamage}; use layout::layout_debug; use layout::parallel; -use layout::query::{process_client_rect_query, process_element_inner_text_query}; use layout::query::{ - process_content_box_request, process_content_boxes_request, LayoutRPCImpl, LayoutThreadData, + process_client_rect_query, process_content_box_request, process_content_boxes_request, + process_element_inner_text_query, process_node_scroll_area_request, + process_node_scroll_id_request, process_offset_parent_query, process_parse_font_request, + process_resolved_style_request, LayoutRPCImpl, LayoutThreadData, }; -use layout::query::{process_node_scroll_area_request, process_node_scroll_id_request}; -use layout::query::{process_offset_parent_query, process_resolved_style_request}; use layout::sequential; use layout::traversal::{ construct_flows_at_ancestors, ComputeStackingRelativePositions, PreorderFlowTraversal, @@ -558,6 +558,7 @@ impl LayoutThread { scroll_id_response: None, scroll_area_response: Rect::zero(), resolved_style_response: String::new(), + parse_font_response: None, offset_parent_response: OffsetParentResponse::empty(), scroll_offsets: HashMap::new(), text_index_response: TextIndexResponse(None), @@ -1231,6 +1232,9 @@ impl LayoutThread { &QueryMsg::ElementInnerTextQuery(_) => { rw_data.element_inner_text_response = String::new(); }, + &QueryMsg::ParseFontQuery(..) => { + rw_data.parse_font_response = None; + }, &QueryMsg::InnerWindowDimensionsQuery(_) => { rw_data.inner_window_dimensions_response = None; }, @@ -1498,6 +1502,7 @@ impl LayoutThread { &mut *rw_data, &mut layout_context, data.result.borrow_mut().as_mut().unwrap(), + document_shared_lock, ); } @@ -1507,6 +1512,7 @@ impl LayoutThread { rw_data: &mut LayoutThreadData, context: &mut LayoutContext, reflow_result: &mut ReflowComplete, + shared_lock: &SharedRwLock, ) { reflow_result.pending_images = std::mem::replace(&mut *context.pending_images.lock().unwrap(), vec![]); @@ -1549,6 +1555,18 @@ impl LayoutThread { rw_data.resolved_style_response = process_resolved_style_request(context, node, pseudo, property, root_flow); }, + &QueryMsg::ParseFontQuery(node, ref property, ref value) => { + let node = unsafe { ServoLayoutNode::new(&node) }; + let url = self.url.clone(); + rw_data.parse_font_response = process_parse_font_request( + context, + node, + value, + property, + url, + shared_lock, + ); + }, &QueryMsg::OffsetParentQuery(node) => { rw_data.offset_parent_response = process_offset_parent_query(node, root_flow); }, diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index 3bb0a7a3353..a11160ee11f 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -1242,6 +1242,7 @@ impl LayoutThread { // builder in order to support query iframe sizing. rw_data.inner_window_dimensions_response = None; }, + &QueryMsg::ParseFontQuery(_, _) => unimplemented!(), }, ReflowGoal::Full | ReflowGoal::TickAnimations => {}, } diff --git a/components/script/canvas_state.rs b/components/script/canvas_state.rs index dac2bb04924..ab2f93b5baa 100644 --- a/components/script/canvas_state.rs +++ b/components/script/canvas_state.rs @@ -1006,6 +1006,16 @@ impl CanvasState { ) } + // https://html.spec.whatwg.org/multipage/#dom-context-2d-font + pub fn set_font(&self, _canvas: Option<&HTMLCanvasElement>, _value: DOMString) { + unimplemented!() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-font + pub fn font(&self) -> DOMString { + unimplemented!() + } + // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth pub fn line_width(&self) -> f64 { self.state.borrow().line_width diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 4820a3bbfd7..3e0df6bb4b9 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -297,6 +297,17 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { self.canvas_state.measure_text(&self.global(), text) } + // https://html.spec.whatwg.org/multipage/#dom-context-2d-font + fn Font(&self) -> DOMString { + self.canvas_state.font() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-font + fn SetFont(&self, value: DOMString) { + self.canvas_state + .set_font(self.canvas.as_ref().map(|c| &**c), value) + } + // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage fn DrawImage(&self, image: CanvasImageSource, dx: f64, dy: f64) -> ErrorResult { self.canvas_state diff --git a/components/script/dom/offscreencanvasrenderingcontext2d.rs b/components/script/dom/offscreencanvasrenderingcontext2d.rs index a3a873250f1..d36785d71f6 100644 --- a/components/script/dom/offscreencanvasrenderingcontext2d.rs +++ b/components/script/dom/offscreencanvasrenderingcontext2d.rs @@ -257,6 +257,17 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex self.canvas_state.measure_text(&self.global(), text) } + // https://html.spec.whatwg.org/multipage/#dom-context-2d-font + fn Font(&self) -> DOMString { + self.canvas_state.font() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-font + fn SetFont(&self, value: DOMString) { + self.canvas_state + .set_font(self.htmlcanvas.as_ref().map(|c| &**c), value) + } + // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth fn LineWidth(&self) -> f64 { self.canvas_state.line_width() diff --git a/components/script/dom/webidls/CanvasRenderingContext2D.webidl b/components/script/dom/webidls/CanvasRenderingContext2D.webidl index af88cfe9281..2267984ce2f 100644 --- a/components/script/dom/webidls/CanvasRenderingContext2D.webidl +++ b/components/script/dom/webidls/CanvasRenderingContext2D.webidl @@ -211,7 +211,7 @@ interface mixin CanvasPathDrawingStyles { [Exposed=(PaintWorklet, Window, Worker)] interface mixin CanvasTextDrawingStyles { // text - //attribute DOMString font; // (default 10px sans-serif) + attribute DOMString font; // (default 10px sans-serif) //attribute CanvasTextAlign textAlign; // "start", "end", "left", "right", "center" (default: "start") //attribute CanvasTextBaseline textBaseline; // "top", "hanging", "middle", "alphabetic", // "ideographic", "bottom" (default: "alphabetic") diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index c050579918e..6209fb72d2f 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -118,6 +118,7 @@ use script_traits::{ }; use script_traits::{TimerSchedulerMsg, WebrenderIpcSender, WindowSizeData, WindowSizeType}; use selectors::attr::CaseSensitivity; +use servo_arc::Arc as ServoArc; use servo_geometry::{f32_rect_to_au_rect, MaxRect}; use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; use std::borrow::Cow; @@ -136,7 +137,7 @@ use style::dom::OpaqueNode; use style::error_reporting::{ContextualParseError, ParseErrorReporter}; use style::media_queries; use style::parser::ParserContext as CssParserContext; -use style::properties::PropertyId; +use style::properties::{ComputedValues, PropertyId, ShorthandId}; use style::selector_parser::PseudoElement; use style::str::HTML_SPACE_CHARACTERS; use style::stylesheets::CssRuleType; @@ -1847,6 +1848,21 @@ impl Window { ) } + pub fn parse_font_query(&self, node: &Node, value: String) -> Option> { + if !node.is_connected() { + return None; + } + let id = PropertyId::Shorthand(ShorthandId::Font); + if !self.layout_reflow(QueryMsg::ParseFontQuery( + node.to_trusted_node_address(), + id, + value, + )) { + return None; + } + self.layout_rpc.parsed_font() + } + pub fn layout(&self) -> &dyn LayoutRPC { &*self.layout_rpc } @@ -2504,6 +2520,7 @@ fn debug_reflow_events(id: PipelineId, reflow_goal: &ReflowGoal, reason: &Reflow &QueryMsg::StyleQuery => "\tStyleQuery", &QueryMsg::TextIndexQuery(..) => "\tTextIndexQuery", &QueryMsg::ElementInnerTextQuery(_) => "\tElementInnerTextQuery", + &QueryMsg::ParseFontQuery(..) => "\nParseFontQuery", &QueryMsg::InnerWindowDimensionsQuery(_) => "\tInnerWindowDimensionsQuery", }, }; diff --git a/components/script_layout_interface/message.rs b/components/script_layout_interface/message.rs index 8495d61fc8a..5cd6c66345f 100644 --- a/components/script_layout_interface/message.rs +++ b/components/script_layout_interface/message.rs @@ -117,6 +117,7 @@ pub enum QueryMsg { ResolvedStyleQuery(TrustedNodeAddress, Option, PropertyId), StyleQuery, ElementInnerTextQuery(TrustedNodeAddress), + ParseFontQuery(TrustedNodeAddress, PropertyId, String), InnerWindowDimensionsQuery(BrowsingContextId), } @@ -145,6 +146,7 @@ impl ReflowGoal { QueryMsg::NodeScrollGeometryQuery(_) | QueryMsg::NodeScrollIdQuery(_) | QueryMsg::ResolvedStyleQuery(..) | + QueryMsg::ParseFontQuery(..) | QueryMsg::OffsetParentQuery(_) | QueryMsg::StyleQuery => false, }, @@ -166,6 +168,7 @@ impl ReflowGoal { QueryMsg::NodeScrollGeometryQuery(_) | QueryMsg::NodeScrollIdQuery(_) | QueryMsg::ResolvedStyleQuery(..) | + QueryMsg::ParseFontQuery(..) | QueryMsg::OffsetParentQuery(_) | QueryMsg::InnerWindowDimensionsQuery(_) | QueryMsg::StyleQuery => false, diff --git a/components/script_layout_interface/rpc.rs b/components/script_layout_interface/rpc.rs index 991437553cd..f21982475cb 100644 --- a/components/script_layout_interface/rpc.rs +++ b/components/script_layout_interface/rpc.rs @@ -6,6 +6,8 @@ use app_units::Au; use euclid::default::Rect; use euclid::Size2D; use script_traits::UntrustedNodeAddress; +use servo_arc::Arc; +use style::properties::ComputedValues; use style_traits::CSSPixel; use webrender_api::ExternalScrollId; @@ -30,6 +32,8 @@ pub trait LayoutRPC { fn node_scroll_id(&self) -> NodeScrollIdResponse; /// Query layout for the resolved value of a given CSS property fn resolved_style(&self) -> ResolvedStyleResponse; + /// Query layout to get the parsed font property for canvas. + fn parsed_font(&self) -> Option>; fn offset_parent(&self) -> OffsetParentResponse; fn text_index(&self) -> TextIndexResponse; /// Requests the list of nodes from the given point. From f3cb7a1910a2306d4625a534b6ffaab4644c0152 Mon Sep 17 00:00:00 2001 From: Utsav Oza Date: Fri, 29 May 2020 00:25:42 +0530 Subject: [PATCH 04/13] Add todos for missing steps while processing parse font query --- components/layout/query.rs | 116 ++++++++++++++++++++---------- components/script/canvas_state.rs | 11 ++- components/script/dom/window.rs | 3 - 3 files changed, 88 insertions(+), 42 deletions(-) diff --git a/components/layout/query.rs b/components/layout/query.rs index b78a642ec1d..fb73e8d1e41 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -37,7 +37,7 @@ use std::sync::{Arc, Mutex}; use style::computed_values::display::T as Display; use style::computed_values::position::T as Position; use style::computed_values::visibility::T as Visibility; -use style::context::{StyleContext, ThreadLocalStyleContext}; +use style::context::{QuirksMode, SharedStyleContext, StyleContext, ThreadLocalStyleContext}; use style::dom::TElement; use style::logical_geometry::{BlockFlowDirection, InlineBaseDirection, WritingMode}; use style::properties::{ @@ -750,28 +750,18 @@ pub fn process_node_scroll_area_request( } } -pub fn process_parse_font_request<'dom, E>( - context: &LayoutContext, - node: E, - font_value: &str, +fn create_font_declaration( + value: &str, property: &PropertyId, - url_data: ServoUrl, - shared_lock: &SharedRwLock, -) -> Option> -where - E: LayoutNode<'dom>, -{ - use style::stylist::RuleInclusion; - use style::traversal::resolve_style; - - // 1. Parse the given font property value - let quirks_mode = context.style_context.quirks_mode(); + url_data: &ServoUrl, + quirks_mode: QuirksMode, +) -> Option { let mut declarations = SourcePropertyDeclaration::new(); let result = parse_one_declaration_into( &mut declarations, property.clone(), - font_value, - &url_data, + value, + url_data, None, ParsingMode::DEFAULT, quirks_mode, @@ -784,32 +774,84 @@ where }, Err(_) => return None, }; + // TODO: Force to set line-height property to 'normal' font property. + Some(declarations) +} + +fn resolve_for_declarations<'dom, E>( + context: &SharedStyleContext, + parent_style: Option<&ComputedValues>, + declarations: PropertyDeclarationBlock, + shared_lock: &SharedRwLock, +) -> ServoArc +where + E: LayoutNode<'dom>, +{ + let parent_style = match parent_style { + Some(parent) => &*parent, + None => context.stylist.device().default_computed_values(), + }; + context + .stylist + .compute_for_declarations::( + &context.guards, + &*parent_style, + ServoArc::new(shared_lock.wrap(declarations)), + ) +} + +pub fn process_parse_font_request<'dom, E>( + context: &LayoutContext, + node: E, + value: &str, + property: &PropertyId, + url_data: ServoUrl, + shared_lock: &SharedRwLock, +) -> Option> +where + E: LayoutNode<'dom>, +{ + use style::stylist::RuleInclusion; + use style::traversal::resolve_style; + + // 1. Parse the given font property value + let quirks_mode = context.style_context.quirks_mode(); + let declarations = create_font_declaration(value, property, &url_data, quirks_mode)?; + + // TODO: Reject 'inherit' and 'initial' values for the font property. // 2. Get resolved styles for the parent element let element = node.as_element().unwrap(); - let parent_style = if element.has_data() { - node.to_threadsafe().as_element().unwrap().resolved_style() + let parent_style = if node.is_connected() { + if element.has_data() { + node.to_threadsafe().as_element().unwrap().resolved_style() + } else { + let mut tlc = ThreadLocalStyleContext::new(&context.style_context); + let mut context = StyleContext { + shared: &context.style_context, + thread_local: &mut tlc, + }; + let styles = resolve_style(&mut context, element, RuleInclusion::All, None); + styles.primary().clone() + } } else { - let mut tlc = ThreadLocalStyleContext::new(&context.style_context); - let mut context = StyleContext { - shared: &context.style_context, - thread_local: &mut tlc, - }; - let styles = resolve_style(&mut context, element, RuleInclusion::All, None); - styles.primary().clone() + let default_declarations = + create_font_declaration("10px sans-serif", property, &url_data, quirks_mode).unwrap(); + resolve_for_declarations::( + &context.style_context, + None, + default_declarations, + shared_lock, + ) }; // 3. Resolve the parsed value with resolved styles of the parent element - Some( - context - .style_context - .stylist - .compute_for_declarations::( - &context.style_context.guards, - &*parent_style, - ServoArc::new(shared_lock.wrap(declarations)), - ), - ) + Some(resolve_for_declarations::( + &context.style_context, + Some(&*parent_style), + declarations, + shared_lock, + )) } /// Return the resolved value of property for a given (pseudo)element. diff --git a/components/script/canvas_state.rs b/components/script/canvas_state.rs index ab2f93b5baa..5550ff10fc3 100644 --- a/components/script/canvas_state.rs +++ b/components/script/canvas_state.rs @@ -22,7 +22,7 @@ use crate::dom::element::Element; use crate::dom::globalscope::GlobalScope; use crate::dom::htmlcanvaselement::{CanvasContext, HTMLCanvasElement}; use crate::dom::imagedata::ImageData; -use crate::dom::node::{Node, NodeDamage}; +use crate::dom::node::{window_from_node, Node, NodeDamage}; use crate::dom::offscreencanvas::{OffscreenCanvas, OffscreenCanvasContext}; use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope; use crate::dom::textmetrics::TextMetrics; @@ -1007,7 +1007,14 @@ impl CanvasState { } // https://html.spec.whatwg.org/multipage/#dom-context-2d-font - pub fn set_font(&self, _canvas: Option<&HTMLCanvasElement>, _value: DOMString) { + pub fn set_font(&self, canvas: Option<&HTMLCanvasElement>, value: DOMString) { + let _resolved_font = if let Some(element) = canvas { + let node = element.upcast::(); + let window = window_from_node(&*node); + window.parse_font_query(&node, value.to_string()) + } else { + None + }; unimplemented!() } diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 6209fb72d2f..25198c7e16e 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -1849,9 +1849,6 @@ impl Window { } pub fn parse_font_query(&self, node: &Node, value: String) -> Option> { - if !node.is_connected() { - return None; - } let id = PropertyId::Shorthand(ShorthandId::Font); if !self.layout_reflow(QueryMsg::ParseFontQuery( node.to_trusted_node_address(), From 43051c7bf3649689ea0d520a6b760908af368c33 Mon Sep 17 00:00:00 2001 From: Utsav Oza Date: Fri, 29 May 2020 01:04:52 +0530 Subject: [PATCH 05/13] Fix ./mach build --release --with-layout-2020 --- components/layout_2020/query.rs | 7 ++++++- components/layout_thread_2020/lib.rs | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/components/layout_2020/query.rs b/components/layout_2020/query.rs index 4b895025e11..704dad8db56 100644 --- a/components/layout_2020/query.rs +++ b/components/layout_2020/query.rs @@ -21,13 +21,14 @@ use script_layout_interface::wrapper_traits::{ }; use script_traits::LayoutMsg as ConstellationMsg; use script_traits::UntrustedNodeAddress; +use servo_arc::Arc as ServoArc; use std::collections::HashMap; use std::sync::{Arc, Mutex}; use style::computed_values::position::T as Position; use style::context::{StyleContext, ThreadLocalStyleContext}; use style::dom::OpaqueNode; use style::dom::TElement; -use style::properties::{LonghandId, PropertyDeclarationId, PropertyId}; +use style::properties::{ComputedValues, LonghandId, PropertyDeclarationId, PropertyId}; use style::selector_parser::PseudoElement; use style::stylist::RuleInclusion; use style::traversal::resolve_style; @@ -139,6 +140,10 @@ impl LayoutRPC for LayoutRPCImpl { ResolvedStyleResponse(rw_data.resolved_style_response.clone()) } + fn parsed_font(&self) -> Option> { + unimplemented!() + } + fn offset_parent(&self) -> OffsetParentResponse { let &LayoutRPCImpl(ref rw_data) = self; let rw_data = rw_data.lock().unwrap(); diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index a11160ee11f..ae154e0dd4d 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -914,6 +914,7 @@ impl LayoutThread { &QueryMsg::ResolvedStyleQuery(_, _, _) => { rw_data.resolved_style_response = String::new(); }, + &QueryMsg::ParseFontQuery(_, _, _) => unimplemented!(), &QueryMsg::OffsetParentQuery(_) => { rw_data.offset_parent_response = OffsetParentResponse::empty(); }, @@ -1206,6 +1207,7 @@ impl LayoutThread { fragment_tree, ); }, + &QueryMsg::ParseFontQuery(_, _, _) => unimplemented!(), &QueryMsg::OffsetParentQuery(node) => { rw_data.offset_parent_response = process_offset_parent_query(node); }, @@ -1242,7 +1244,6 @@ impl LayoutThread { // builder in order to support query iframe sizing. rw_data.inner_window_dimensions_response = None; }, - &QueryMsg::ParseFontQuery(_, _) => unimplemented!(), }, ReflowGoal::Full | ReflowGoal::TickAnimations => {}, } From 5493424d9aacbdf1066e2e2c48ccbc2a98752509 Mon Sep 17 00:00:00 2001 From: Utsav Oza Date: Sat, 30 May 2020 18:46:18 +0530 Subject: [PATCH 06/13] Revert unnecessary changes --- components/canvas/canvas_data.rs | 68 ++++++++++++++++---------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index a3616f7e3da..6ff87eca291 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -371,7 +371,7 @@ pub enum Filter { pub struct CanvasData<'a> { backend: Box, - draw_target: Box, + drawtarget: Box, path_state: Option, state: CanvasPaintState<'a>, saved_states: Vec>, @@ -399,7 +399,7 @@ impl<'a> CanvasData<'a> { let draw_target = backend.create_drawtarget(size); CanvasData { backend, - draw_target, + drawtarget: draw_target, path_state: None, state: CanvasPaintState::new(antialias), saved_states: vec![], @@ -449,7 +449,7 @@ impl<'a> CanvasData<'a> { // TODO(pylbrecht) pass another closure for raqote self.draw_with_shadow(&rect, writer); } else { - writer(&mut *self.draw_target); + writer(&mut *self.drawtarget); } } @@ -460,8 +460,8 @@ impl<'a> CanvasData<'a> { pub fn restore_context_state(&mut self) { if let Some(state) = self.saved_states.pop() { let _ = mem::replace(&mut self.state, state); - self.draw_target.set_transform(&self.state.transform); - self.draw_target.pop_clip(); + self.drawtarget.set_transform(&self.state.transform); + self.drawtarget.pop_clip(); } } @@ -481,7 +481,7 @@ impl<'a> CanvasData<'a> { }) .collect(); - self.draw_target.fill_text( + self.drawtarget.fill_text( text, x, y, @@ -541,7 +541,7 @@ impl<'a> CanvasData<'a> { ); }); } else { - self.draw_target.fill_rect( + self.drawtarget.fill_rect( &draw_rect, self.state.fill_style.clone(), Some(&self.state.draw_options), @@ -550,7 +550,7 @@ impl<'a> CanvasData<'a> { } pub fn clear_rect(&mut self, rect: &Rect) { - self.draw_target.clear_rect(rect); + self.drawtarget.clear_rect(rect); } pub fn stroke_rect(&mut self, rect: &Rect) { @@ -570,7 +570,7 @@ impl<'a> CanvasData<'a> { } else if rect.size.width == 0. || rect.size.height == 0. { let mut stroke_opts = self.state.stroke_opts.clone(); stroke_opts.set_line_cap(LineCapStyle::Butt); - self.draw_target.stroke_line( + self.drawtarget.stroke_line( rect.origin, rect.bottom_right(), self.state.stroke_style.clone(), @@ -578,7 +578,7 @@ impl<'a> CanvasData<'a> { &self.state.draw_options, ); } else { - self.draw_target.stroke_rect( + self.drawtarget.stroke_rect( rect, self.state.stroke_style.clone(), &self.state.stroke_opts, @@ -600,7 +600,7 @@ impl<'a> CanvasData<'a> { // If there's no record of any path yet, create a new builder in user-space. if self.path_state.is_none() { self.path_state = Some(PathState::UserSpacePathBuilder( - self.draw_target.create_path_builder(), + self.drawtarget.create_path_builder(), None, )); } @@ -635,7 +635,7 @@ impl<'a> CanvasData<'a> { let new_state = match *self.path_state.as_mut().unwrap() { PathState::DeviceSpacePathBuilder(ref mut builder) => { let path = builder.finish(); - let inverse = match self.draw_target.get_transform().inverse() { + let inverse = match self.drawtarget.get_transform().inverse() { Some(m) => m, None => { warn!("Couldn't invert canvas transformation."); @@ -667,7 +667,7 @@ impl<'a> CanvasData<'a> { } self.ensure_path(); - self.draw_target.fill( + self.drawtarget.fill( &self.path().clone(), self.state.fill_style.clone(), &self.state.draw_options, @@ -680,7 +680,7 @@ impl<'a> CanvasData<'a> { } self.ensure_path(); - self.draw_target.stroke( + self.drawtarget.stroke( &self.path().clone(), self.state.stroke_style.clone(), &self.state.stroke_opts, @@ -691,7 +691,7 @@ impl<'a> CanvasData<'a> { pub fn clip(&mut self) { self.ensure_path(); let path = self.path().clone(); - self.draw_target.push_clip(&path); + self.drawtarget.push_clip(&path); } pub fn is_point_in_path( @@ -704,7 +704,7 @@ impl<'a> CanvasData<'a> { self.ensure_path(); let result = match self.path_state.as_ref() { Some(PathState::UserSpacePath(ref path, ref transform)) => { - let target_transform = self.draw_target.get_transform(); + let target_transform = self.drawtarget.get_transform(); let path_transform = transform.as_ref().unwrap_or(&target_transform); path.contains_point(x, y, path_transform) }, @@ -724,7 +724,7 @@ impl<'a> CanvasData<'a> { fn path_builder(&mut self) -> PathBuilderRef { if self.path_state.is_none() { self.path_state = Some(PathState::UserSpacePathBuilder( - self.draw_target.create_path_builder(), + self.drawtarget.create_path_builder(), None, )); } @@ -766,7 +766,7 @@ impl<'a> CanvasData<'a> { PathState::DeviceSpacePathBuilder(ref mut builder) => { return PathBuilderRef { builder, - transform: self.draw_target.get_transform(), + transform: self.drawtarget.get_transform(), }; }, _ => unreachable!(), @@ -780,7 +780,7 @@ impl<'a> CanvasData<'a> { }, PathState::DeviceSpacePathBuilder(ref mut builder) => PathBuilderRef { builder, - transform: self.draw_target.get_transform(), + transform: self.drawtarget.get_transform(), }, PathState::UserSpacePathBuilder(..) | PathState::UserSpacePath(..) => unreachable!(), } @@ -910,12 +910,12 @@ impl<'a> CanvasData<'a> { pub fn set_fill_style(&mut self, style: FillOrStrokeStyle) { self.backend - .set_fill_style(style, &mut self.state, &*self.draw_target); + .set_fill_style(style, &mut self.state, &*self.drawtarget); } pub fn set_stroke_style(&mut self, style: FillOrStrokeStyle) { self.backend - .set_stroke_style(style, &mut self.state, &*self.draw_target); + .set_stroke_style(style, &mut self.state, &*self.drawtarget); } pub fn set_line_width(&mut self, width: f32) { @@ -935,7 +935,7 @@ impl<'a> CanvasData<'a> { } pub fn get_transform(&self) -> Transform2D { - self.draw_target.get_transform() + self.drawtarget.get_transform() } pub fn set_transform(&mut self, transform: &Transform2D) { @@ -946,12 +946,12 @@ impl<'a> CanvasData<'a> { Some(PathState::UserSpacePathBuilder(_, ref mut transform)) | Some(PathState::UserSpacePath(_, ref mut transform)) => { if transform.is_none() { - *transform = Some(self.draw_target.get_transform()); + *transform = Some(self.drawtarget.get_transform()); } }, } self.state.transform = transform.clone(); - self.draw_target.set_transform(transform) + self.drawtarget.set_transform(transform) } pub fn set_global_alpha(&mut self, alpha: f32) { @@ -963,7 +963,7 @@ impl<'a> CanvasData<'a> { } pub fn recreate(&mut self, size: Size2D) { - self.draw_target = self + self.drawtarget = self .backend .create_drawtarget(Size2D::new(size.width, size.height)); self.state = self.backend.recreate_paint_state(&self.state); @@ -982,7 +982,7 @@ impl<'a> CanvasData<'a> { } pub fn send_pixels(&mut self, chan: IpcSender) { - self.draw_target.snapshot_data(&|bytes| { + self.drawtarget.snapshot_data(&|bytes| { let data = IpcSharedMemory::from_bytes(bytes); chan.send(data).unwrap(); vec![] @@ -990,7 +990,7 @@ impl<'a> CanvasData<'a> { } pub fn send_data(&mut self, chan: IpcSender) { - let size = self.draw_target.get_size(); + let size = self.drawtarget.get_size(); let descriptor = webrender_api::ImageDescriptor { size: webrender_api::units::DeviceIntSize::new(size.width, size.height), @@ -999,7 +999,7 @@ impl<'a> CanvasData<'a> { offset: 0, flags: webrender_api::ImageDescriptorFlags::empty(), }; - let data = self.draw_target.snapshot_data_owned(); + let data = self.drawtarget.snapshot_data_owned(); let data = webrender_api::ImageData::Raw(Arc::new(data)); let mut updates = vec![]; @@ -1037,14 +1037,14 @@ impl<'a> CanvasData<'a> { assert_eq!(rect.size.area() as usize, imagedata.len() / 4); pixels::rgba8_byte_swap_and_premultiply_inplace(&mut imagedata); let source_surface = self - .draw_target + .drawtarget .create_source_surface_from_data( &imagedata, rect.size.to_i32(), rect.size.width as i32 * 4, ) .unwrap(); - self.draw_target.copy_surface( + self.drawtarget.copy_surface( source_surface, Rect::from_size(rect.size.to_i32()), rect.origin.to_i32(), @@ -1076,12 +1076,12 @@ impl<'a> CanvasData<'a> { } fn create_draw_target_for_shadow(&self, source_rect: &Rect) -> Box { - let mut draw_target = self.draw_target.create_similar_draw_target( + let mut draw_target = self.drawtarget.create_similar_draw_target( &Size2D::new( source_rect.size.width as i32, source_rect.size.height as i32, ), - self.draw_target.get_format(), + self.drawtarget.get_format(), ); let matrix = Transform2D::identity() .pre_translate(-source_rect.origin.to_vector().cast::()) @@ -1097,7 +1097,7 @@ impl<'a> CanvasData<'a> { let shadow_src_rect = self.state.transform.transform_rect(rect); let mut new_draw_target = self.create_draw_target_for_shadow(&shadow_src_rect); draw_shadow_source(&mut *new_draw_target); - self.draw_target.draw_surface_with_shadow( + self.drawtarget.draw_surface_with_shadow( new_draw_target.snapshot(), &Point2D::new( shadow_src_rect.origin.x as f32, @@ -1126,7 +1126,7 @@ impl<'a> CanvasData<'a> { return vec![]; } - self.draw_target.snapshot_data(&|bytes| { + self.drawtarget.snapshot_data(&|bytes| { pixels::rgba8_get_rect(bytes, canvas_size, read_rect).into_owned() }) } From 15fd256302cc4401e0c4e2d154d473bfaa16223d Mon Sep 17 00:00:00 2001 From: Utsav Oza Date: Wed, 3 Jun 2020 15:47:44 +0530 Subject: [PATCH 07/13] Store resolved font style in canvas context state --- components/layout/query.rs | 21 ++++++----- components/layout_2020/query.rs | 12 +++++-- components/layout_thread/lib.rs | 15 ++++---- components/layout_thread_2020/lib.rs | 6 ++-- components/script/canvas_state.rs | 35 +++++++++++++------ components/script/dom/bindings/trace.rs | 2 ++ .../script/dom/canvasrenderingcontext2d.rs | 3 +- .../dom/offscreencanvasrenderingcontext2d.rs | 13 +++---- components/script/dom/window.rs | 11 +++--- components/script_layout_interface/message.rs | 6 ++-- components/script_layout_interface/rpc.rs | 6 ++-- 11 files changed, 81 insertions(+), 49 deletions(-) diff --git a/components/layout/query.rs b/components/layout/query.rs index fb73e8d1e41..56b1fc7d0f8 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -40,9 +40,10 @@ use style::computed_values::visibility::T as Visibility; use style::context::{QuirksMode, SharedStyleContext, StyleContext, ThreadLocalStyleContext}; use style::dom::TElement; use style::logical_geometry::{BlockFlowDirection, InlineBaseDirection, WritingMode}; +use style::properties::style_structs::{self, Font}; use style::properties::{ - parse_one_declaration_into, style_structs, ComputedValues, Importance, LonghandId, - PropertyDeclarationBlock, PropertyDeclarationId, PropertyId, SourcePropertyDeclaration, + parse_one_declaration_into, ComputedValues, Importance, LonghandId, PropertyDeclarationBlock, + PropertyDeclarationId, PropertyId, SourcePropertyDeclaration, }; use style::selector_parser::PseudoElement; use style::shared_lock::SharedRwLock; @@ -80,7 +81,7 @@ pub struct LayoutThreadData { pub resolved_style_response: String, /// A queued response for the resolved font style for canvas. - pub parse_font_response: Option>, + pub resolved_font_style_response: Option>, /// A queued response for the offset parent/rect of a node. pub offset_parent_response: OffsetParentResponse, @@ -179,10 +180,10 @@ impl LayoutRPC for LayoutRPCImpl { ResolvedStyleResponse(rw_data.resolved_style_response.clone()) } - fn parsed_font(&self) -> Option> { + fn resolved_font_style(&self) -> Option> { let &LayoutRPCImpl(ref rw_data) = self; let rw_data = rw_data.lock().unwrap(); - rw_data.parse_font_response.clone() + rw_data.resolved_font_style_response.clone() } fn offset_parent(&self) -> OffsetParentResponse { @@ -800,14 +801,14 @@ where ) } -pub fn process_parse_font_request<'dom, E>( +pub fn process_resolved_font_style_request<'dom, E>( context: &LayoutContext, node: E, value: &str, property: &PropertyId, url_data: ServoUrl, shared_lock: &SharedRwLock, -) -> Option> +) -> Option> where E: LayoutNode<'dom>, { @@ -846,12 +847,14 @@ where }; // 3. Resolve the parsed value with resolved styles of the parent element - Some(resolve_for_declarations::( + let computed_values = resolve_for_declarations::( &context.style_context, Some(&*parent_style), declarations, shared_lock, - )) + ); + + Some(computed_values.clone_font()) } /// Return the resolved value of property for a given (pseudo)element. diff --git a/components/layout_2020/query.rs b/components/layout_2020/query.rs index 704dad8db56..a8a27a30faa 100644 --- a/components/layout_2020/query.rs +++ b/components/layout_2020/query.rs @@ -28,7 +28,8 @@ use style::computed_values::position::T as Position; use style::context::{StyleContext, ThreadLocalStyleContext}; use style::dom::OpaqueNode; use style::dom::TElement; -use style::properties::{ComputedValues, LonghandId, PropertyDeclarationId, PropertyId}; +use style::properties::style_structs::Font; +use style::properties::{LonghandId, PropertyDeclarationId, PropertyId}; use style::selector_parser::PseudoElement; use style::stylist::RuleInclusion; use style::traversal::resolve_style; @@ -66,6 +67,9 @@ pub struct LayoutThreadData { /// A queued response for the resolved style property of an element. pub resolved_style_response: String, + /// A queued response for the resolved font style for canvas. + pub resolved_font_style_response: Option>, + /// A queued response for the offset parent/rect of a node. pub offset_parent_response: OffsetParentResponse, @@ -140,8 +144,10 @@ impl LayoutRPC for LayoutRPCImpl { ResolvedStyleResponse(rw_data.resolved_style_response.clone()) } - fn parsed_font(&self) -> Option> { - unimplemented!() + fn resolved_font_style(&self) -> Option> { + let &LayoutRPCImpl(ref rw_data) = self; + let rw_data = rw_data.lock().unwrap(); + rw_data.resolved_font_style_response.clone() } fn offset_parent(&self) -> OffsetParentResponse { diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 9ae955789a9..14dbfc0c80e 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -52,8 +52,9 @@ use layout::parallel; use layout::query::{ process_client_rect_query, process_content_box_request, process_content_boxes_request, process_element_inner_text_query, process_node_scroll_area_request, - process_node_scroll_id_request, process_offset_parent_query, process_parse_font_request, - process_resolved_style_request, LayoutRPCImpl, LayoutThreadData, + process_node_scroll_id_request, process_offset_parent_query, + process_resolved_font_style_request, process_resolved_style_request, LayoutRPCImpl, + LayoutThreadData, }; use layout::sequential; use layout::traversal::{ @@ -558,7 +559,7 @@ impl LayoutThread { scroll_id_response: None, scroll_area_response: Rect::zero(), resolved_style_response: String::new(), - parse_font_response: None, + resolved_font_style_response: None, offset_parent_response: OffsetParentResponse::empty(), scroll_offsets: HashMap::new(), text_index_response: TextIndexResponse(None), @@ -1232,8 +1233,8 @@ impl LayoutThread { &QueryMsg::ElementInnerTextQuery(_) => { rw_data.element_inner_text_response = String::new(); }, - &QueryMsg::ParseFontQuery(..) => { - rw_data.parse_font_response = None; + &QueryMsg::ResolvedFontStyleQuery(..) => { + rw_data.resolved_font_style_response = None; }, &QueryMsg::InnerWindowDimensionsQuery(_) => { rw_data.inner_window_dimensions_response = None; @@ -1555,10 +1556,10 @@ impl LayoutThread { rw_data.resolved_style_response = process_resolved_style_request(context, node, pseudo, property, root_flow); }, - &QueryMsg::ParseFontQuery(node, ref property, ref value) => { + &QueryMsg::ResolvedFontStyleQuery(node, ref property, ref value) => { let node = unsafe { ServoLayoutNode::new(&node) }; let url = self.url.clone(); - rw_data.parse_font_response = process_parse_font_request( + rw_data.resolved_font_style_response = process_resolved_font_style_request( context, node, value, diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index ae154e0dd4d..bdfb75a2323 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -914,7 +914,9 @@ impl LayoutThread { &QueryMsg::ResolvedStyleQuery(_, _, _) => { rw_data.resolved_style_response = String::new(); }, - &QueryMsg::ParseFontQuery(_, _, _) => unimplemented!(), + &QueryMsg::ResolvedFontStyleQuery(_, _, _) => { + rw_data.resolved_font_style_response = None; + }, &QueryMsg::OffsetParentQuery(_) => { rw_data.offset_parent_response = OffsetParentResponse::empty(); }, @@ -1207,7 +1209,7 @@ impl LayoutThread { fragment_tree, ); }, - &QueryMsg::ParseFontQuery(_, _, _) => unimplemented!(), + &QueryMsg::ResolvedFontStyleQuery(_, _, _) => unimplemented!(), &QueryMsg::OffsetParentQuery(node) => { rw_data.offset_parent_response = process_offset_parent_query(node); }, diff --git a/components/script/canvas_state.rs b/components/script/canvas_state.rs index 5550ff10fc3..dc40ad3203d 100644 --- a/components/script/canvas_state.rs +++ b/components/script/canvas_state.rs @@ -49,6 +49,7 @@ use std::cell::Cell; use std::fmt; use std::str::FromStr; use std::sync::Arc; +use style::properties::style_structs::Font; #[unrooted_must_root_lint::must_root] #[derive(Clone, JSTraceable, MallocSizeOf)] @@ -86,6 +87,7 @@ pub(crate) struct CanvasContextState { shadow_offset_y: f64, shadow_blur: f64, shadow_color: RGBA, + font_style: Option, } impl CanvasContextState { @@ -106,6 +108,7 @@ impl CanvasContextState { shadow_offset_y: 0.0, shadow_blur: 0.0, shadow_color: RGBA::transparent(), + font_style: None, } } } @@ -987,9 +990,18 @@ impl CanvasState { } // https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext - pub fn fill_text(&self, text: DOMString, x: f64, y: f64, max_width: Option) { - let is_max_width_finite = max_width.map_or(true, |max_width| max_width.is_finite()); - if !(x.is_finite() && y.is_finite() && is_max_width_finite) { + pub fn fill_text( + &self, + _canvas: Option<&HTMLCanvasElement>, + text: DOMString, + x: f64, + y: f64, + max_width: Option, + ) { + if !x.is_finite() || !y.is_finite() { + return; + } + if max_width.map_or(false, |max_width| !max_width.is_finite() || max_width <= 0.) { return; } @@ -1008,14 +1020,17 @@ impl CanvasState { // https://html.spec.whatwg.org/multipage/#dom-context-2d-font pub fn set_font(&self, canvas: Option<&HTMLCanvasElement>, value: DOMString) { - let _resolved_font = if let Some(element) = canvas { - let node = element.upcast::(); - let window = window_from_node(&*node); - window.parse_font_query(&node, value.to_string()) - } else { - None + let canvas = match canvas { + Some(element) => element, + None => return, }; - unimplemented!() + let node = canvas.upcast::(); + let window = window_from_node(&*canvas); + let font_style = match window.resolved_font_style_query(&node, value.to_string()) { + Some(value) => value, + None => return, // syntax error + }; + self.state.borrow_mut().font_style = Some((*font_style).clone()); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-font diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 1f6d15a191a..f80a7e57c93 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -144,6 +144,7 @@ use style::context::QuirksMode; use style::dom::OpaqueNode; use style::element_state::*; use style::media_queries::MediaList; +use style::properties::style_structs::Font; use style::properties::PropertyDeclarationBlock; use style::selector_parser::{PseudoElement, Snapshot}; use style::shared_lock::{Locked as StyleLocked, SharedRwLock as StyleSharedRwLock}; @@ -479,6 +480,7 @@ unsafe_no_jsmanaged_fields!(NetworkError); unsafe_no_jsmanaged_fields!(Atom, Prefix, LocalName, Namespace, QualName); unsafe_no_jsmanaged_fields!(TrustedPromise); unsafe_no_jsmanaged_fields!(PropertyDeclarationBlock); +unsafe_no_jsmanaged_fields!(Font); // These three are interdependent, if you plan to put jsmanaged data // in one of these make sure it is propagated properly to containing structs unsafe_no_jsmanaged_fields!(DocumentActivity, WindowSizeData, WindowSizeType); diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 3e0df6bb4b9..c8838fe283e 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -288,7 +288,8 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { // https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext fn FillText(&self, text: DOMString, x: f64, y: f64, max_width: Option) { - self.canvas_state.fill_text(text, x, y, max_width); + self.canvas_state + .fill_text(self.canvas.as_ref().map(|c| &**c), text, x, y, max_width); self.mark_as_dirty(); } diff --git a/components/script/dom/offscreencanvasrenderingcontext2d.rs b/components/script/dom/offscreencanvasrenderingcontext2d.rs index d36785d71f6..ee9c010c391 100644 --- a/components/script/dom/offscreencanvasrenderingcontext2d.rs +++ b/components/script/dom/offscreencanvasrenderingcontext2d.rs @@ -60,11 +60,6 @@ impl OffscreenCanvasRenderingContext2D { )); reflect_dom_object(boxed, global) } - /* - pub fn get_canvas_state(&self) -> Ref { - self.canvas_state.borrow() - } - */ pub fn set_canvas_bitmap_dimensions(&self, size: Size2D) { self.canvas_state.set_bitmap_dimensions(size); @@ -249,7 +244,13 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex // https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext fn FillText(&self, text: DOMString, x: f64, y: f64, max_width: Option) { - self.canvas_state.fill_text(text, x, y, max_width) + self.canvas_state.fill_text( + self.htmlcanvas.as_ref().map(|c| &**c), + text, + x, + y, + max_width, + ) } // https://html.spec.whatwg.org/multipage/#textmetrics diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 25198c7e16e..d7fde4f27d5 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -137,7 +137,8 @@ use style::dom::OpaqueNode; use style::error_reporting::{ContextualParseError, ParseErrorReporter}; use style::media_queries; use style::parser::ParserContext as CssParserContext; -use style::properties::{ComputedValues, PropertyId, ShorthandId}; +use style::properties::style_structs::Font; +use style::properties::{PropertyId, ShorthandId}; use style::selector_parser::PseudoElement; use style::str::HTML_SPACE_CHARACTERS; use style::stylesheets::CssRuleType; @@ -1848,16 +1849,16 @@ impl Window { ) } - pub fn parse_font_query(&self, node: &Node, value: String) -> Option> { + pub fn resolved_font_style_query(&self, node: &Node, value: String) -> Option> { let id = PropertyId::Shorthand(ShorthandId::Font); - if !self.layout_reflow(QueryMsg::ParseFontQuery( + if !self.layout_reflow(QueryMsg::ResolvedFontStyleQuery( node.to_trusted_node_address(), id, value, )) { return None; } - self.layout_rpc.parsed_font() + self.layout_rpc.resolved_font_style() } pub fn layout(&self) -> &dyn LayoutRPC { @@ -2513,11 +2514,11 @@ fn debug_reflow_events(id: PipelineId, reflow_goal: &ReflowGoal, reason: &Reflow &QueryMsg::NodeScrollGeometryQuery(_n) => "\tNodeScrollGeometryQuery", &QueryMsg::NodeScrollIdQuery(_n) => "\tNodeScrollIdQuery", &QueryMsg::ResolvedStyleQuery(_, _, _) => "\tResolvedStyleQuery", + &QueryMsg::ResolvedFontStyleQuery(..) => "\nResolvedFontStyleQuery", &QueryMsg::OffsetParentQuery(_n) => "\tOffsetParentQuery", &QueryMsg::StyleQuery => "\tStyleQuery", &QueryMsg::TextIndexQuery(..) => "\tTextIndexQuery", &QueryMsg::ElementInnerTextQuery(_) => "\tElementInnerTextQuery", - &QueryMsg::ParseFontQuery(..) => "\nParseFontQuery", &QueryMsg::InnerWindowDimensionsQuery(_) => "\tInnerWindowDimensionsQuery", }, }; diff --git a/components/script_layout_interface/message.rs b/components/script_layout_interface/message.rs index 5cd6c66345f..724cd2b79e7 100644 --- a/components/script_layout_interface/message.rs +++ b/components/script_layout_interface/message.rs @@ -117,7 +117,7 @@ pub enum QueryMsg { ResolvedStyleQuery(TrustedNodeAddress, Option, PropertyId), StyleQuery, ElementInnerTextQuery(TrustedNodeAddress), - ParseFontQuery(TrustedNodeAddress, PropertyId, String), + ResolvedFontStyleQuery(TrustedNodeAddress, PropertyId, String), InnerWindowDimensionsQuery(BrowsingContextId), } @@ -146,7 +146,7 @@ impl ReflowGoal { QueryMsg::NodeScrollGeometryQuery(_) | QueryMsg::NodeScrollIdQuery(_) | QueryMsg::ResolvedStyleQuery(..) | - QueryMsg::ParseFontQuery(..) | + QueryMsg::ResolvedFontStyleQuery(..) | QueryMsg::OffsetParentQuery(_) | QueryMsg::StyleQuery => false, }, @@ -168,7 +168,7 @@ impl ReflowGoal { QueryMsg::NodeScrollGeometryQuery(_) | QueryMsg::NodeScrollIdQuery(_) | QueryMsg::ResolvedStyleQuery(..) | - QueryMsg::ParseFontQuery(..) | + QueryMsg::ResolvedFontStyleQuery(..) | QueryMsg::OffsetParentQuery(_) | QueryMsg::InnerWindowDimensionsQuery(_) | QueryMsg::StyleQuery => false, diff --git a/components/script_layout_interface/rpc.rs b/components/script_layout_interface/rpc.rs index f21982475cb..232fecabe6b 100644 --- a/components/script_layout_interface/rpc.rs +++ b/components/script_layout_interface/rpc.rs @@ -7,7 +7,7 @@ use euclid::default::Rect; use euclid::Size2D; use script_traits::UntrustedNodeAddress; use servo_arc::Arc; -use style::properties::ComputedValues; +use style::properties::style_structs::Font; use style_traits::CSSPixel; use webrender_api::ExternalScrollId; @@ -32,8 +32,8 @@ pub trait LayoutRPC { fn node_scroll_id(&self) -> NodeScrollIdResponse; /// Query layout for the resolved value of a given CSS property fn resolved_style(&self) -> ResolvedStyleResponse; - /// Query layout to get the parsed font property for canvas. - fn parsed_font(&self) -> Option>; + /// Query layout to get the resolved font style for canvas. + fn resolved_font_style(&self) -> Option>; fn offset_parent(&self) -> OffsetParentResponse; fn text_index(&self) -> TextIndexResponse; /// Requests the list of nodes from the given point. From c21fde375184c367f923b9e3776ba3adbe7f53dd Mon Sep 17 00:00:00 2001 From: Utsav Oza Date: Thu, 4 Jun 2020 01:32:25 +0530 Subject: [PATCH 08/13] Implement CanvasRenderingContext2D.font property --- Cargo.lock | 2 + components/canvas/Cargo.toml | 1 + components/canvas/canvas_data.rs | 6 +++ components/canvas/canvas_paint_thread.rs | 1 + components/canvas/raqote_backend.rs | 1 + components/canvas_traits/Cargo.toml | 1 + components/canvas_traits/canvas.rs | 2 + components/script/canvas_state.rs | 38 +++++++++++++++++-- .../style/properties/properties.mako.rs | 2 +- components/style/values/computed/font.rs | 16 +++++++- components/style/values/specified/font.rs | 4 ++ 11 files changed, 68 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb0d5ddf870..d080e4a7c39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -515,6 +515,7 @@ dependencies = [ "raqote", "servo_config", "sparkle", + "style", "surfman", "surfman-chains", "surfman-chains-api", @@ -542,6 +543,7 @@ dependencies = [ "serde_bytes", "servo_config", "sparkle", + "style", "time", "webrender_api", "webxr-api", diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index 918e2971f9a..3d7fa7f38e0 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -34,6 +34,7 @@ pixels = { path = "../pixels" } raqote = { version = "0.8", features = ["text"] } servo_config = { path = "../config" } sparkle = "0.1.24" +style = { path = "../style" } # NOTE: the sm-angle feature only enables ANGLE on Windows, not other platforms! surfman = { version = "0.2", features = ["sm-angle", "sm-angle-default"] } surfman-chains = "0.3" diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 6ff87eca291..c90fca964f7 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -13,6 +13,7 @@ use num_traits::ToPrimitive; use std::marker::PhantomData; use std::mem; use std::sync::Arc; +use style::properties::style_structs::Font as FontStyleStruct; use webrender_api::units::RectExt as RectExt_; /// The canvas data stores a state machine for the current status of @@ -1067,6 +1068,10 @@ impl<'a> CanvasData<'a> { self.backend.set_shadow_color(value, &mut self.state); } + pub fn set_font(&mut self, font_style: FontStyleStruct) { + self.state.font_style = Some(font_style) + } + // https://html.spec.whatwg.org/multipage/#when-shadows-are-drawn fn need_to_draw_shadow(&self) -> bool { self.backend.need_to_draw_shadow(&self.state.shadow_color) && @@ -1158,6 +1163,7 @@ pub struct CanvasPaintState<'a> { pub shadow_offset_y: f64, pub shadow_blur: f64, pub shadow_color: Color, + pub font_style: Option, } /// It writes an image to the destination target diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index 6832a5cddc8..9d9f8350e1d 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -247,6 +247,7 @@ impl<'a> CanvasPaintThread<'a> { }, Canvas2dMsg::SetShadowBlur(value) => self.canvas(canvas_id).set_shadow_blur(value), Canvas2dMsg::SetShadowColor(color) => self.canvas(canvas_id).set_shadow_color(color), + Canvas2dMsg::SetFont(font_style) => self.canvas(canvas_id).set_font(font_style), } } diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs index 451656d7708..3c30c0f1a19 100644 --- a/components/canvas/raqote_backend.rs +++ b/components/canvas/raqote_backend.rs @@ -90,6 +90,7 @@ impl<'a> CanvasPaintState<'a> { shadow_offset_y: 0.0, shadow_blur: 0.0, shadow_color: Color::Raqote(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)), + font_style: None, } } } diff --git a/components/canvas_traits/Cargo.toml b/components/canvas_traits/Cargo.toml index 0625221a484..8414349939c 100644 --- a/components/canvas_traits/Cargo.toml +++ b/components/canvas_traits/Cargo.toml @@ -27,6 +27,7 @@ serde = "1.0" serde_bytes = "0.11" servo_config = { path = "../config" } sparkle = "0.1" +style = { path = "../style" } time = { version = "0.1.0", optional = true } webrender_api = { git = "https://github.com/servo/webrender" } webxr-api = { git = "https://github.com/servo/webxr", features = ["ipc"] } diff --git a/components/canvas_traits/canvas.rs b/components/canvas_traits/canvas.rs index 13fc6eaa800..1750e7d8e99 100644 --- a/components/canvas_traits/canvas.rs +++ b/components/canvas_traits/canvas.rs @@ -8,6 +8,7 @@ use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender, IpcSender, IpcSharedMem use serde_bytes::ByteBuf; use std::default::Default; use std::str::FromStr; +use style::properties::style_structs::Font; #[derive(Clone, Debug, Deserialize, Serialize)] pub enum FillRule { @@ -70,6 +71,7 @@ pub enum Canvas2dMsg { SetShadowOffsetY(f64), SetShadowBlur(f64), SetShadowColor(RGBA), + SetFont(Font), } #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/components/script/canvas_state.rs b/components/script/canvas_state.rs index dc40ad3203d..9685d719157 100644 --- a/components/script/canvas_state.rs +++ b/components/script/canvas_state.rs @@ -49,7 +49,10 @@ use std::cell::Cell; use std::fmt; use std::str::FromStr; use std::sync::Arc; +use style::properties::longhands::font_variant_caps::computed_value::T as FontVariantCaps; use style::properties::style_structs::Font; +use style::values::computed::font::FontStyle; +use style_traits::values::ToCss; #[unrooted_must_root_lint::must_root] #[derive(Clone, JSTraceable, MallocSizeOf)] @@ -1026,16 +1029,24 @@ impl CanvasState { }; let node = canvas.upcast::(); let window = window_from_node(&*canvas); - let font_style = match window.resolved_font_style_query(&node, value.to_string()) { + let resolved_font_style = match window.resolved_font_style_query(&node, value.to_string()) { Some(value) => value, None => return, // syntax error }; - self.state.borrow_mut().font_style = Some((*font_style).clone()); + self.state.borrow_mut().font_style = Some((*resolved_font_style).clone()); + self.send_canvas_2d_msg(Canvas2dMsg::SetFont((*resolved_font_style).clone())); } // https://html.spec.whatwg.org/multipage/#dom-context-2d-font pub fn font(&self) -> DOMString { - unimplemented!() + self.state.borrow().font_style.as_ref().map_or_else( + || DOMString::from("10px sans-serif"), + |style| { + let mut result = String::new(); + serialize_font(style, &mut result).unwrap(); + DOMString::from(result) + }, + ) } // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth @@ -1663,3 +1674,24 @@ pub fn adjust_size_sign( } (origin, size.to_u32()) } + +fn serialize_font(style: &Font, dest: &mut W) -> fmt::Result +where + W: fmt::Write, +{ + if style.font_style == FontStyle::Italic { + write!(dest, "{} ", style.font_style.to_css_string())?; + } + if style.font_weight.is_bold() { + write!(dest, "{} ", style.font_weight.to_css_string())?; + } + if style.font_variant_caps == FontVariantCaps::SmallCaps { + write!(dest, "{} ", style.font_variant_caps.to_css_string())?; + } + write!( + dest, + "{} {}", + style.font_size.to_css_string(), + style.font_family.to_css_string() + ) +} diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index a098534793f..7bf6813994b 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -2601,7 +2601,7 @@ pub mod style_structs { % for style_struct in data.active_style_structs(): % if style_struct.name == "Font": - #[derive(Clone, Debug, MallocSizeOf)] + #[derive(Clone, Debug, MallocSizeOf, Serialize, Deserialize)] % else: #[derive(Clone, Debug, MallocSizeOf, PartialEq)] % endif diff --git a/components/style/values/computed/font.rs b/components/style/values/computed/font.rs index ed00f42d678..cc1b7f67d79 100644 --- a/components/style/values/computed/font.rs +++ b/components/style/values/computed/font.rs @@ -80,6 +80,8 @@ impl ToAnimatedValue for FontWeight { ToAnimatedZero, ToCss, ToResolvedValue, + Serialize, + Deserialize, )] /// The computed value of font-size pub struct FontSize { @@ -179,7 +181,7 @@ impl ToAnimatedValue for FontSize { } #[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue)] -#[cfg_attr(feature = "servo", derive(Hash, MallocSizeOf))] +#[cfg_attr(feature = "servo", derive(Hash, MallocSizeOf, Serialize, Deserialize))] /// Specifies a prioritized list of font family names or generic family names. pub struct FontFamily { /// The actual list of family names. @@ -445,7 +447,17 @@ impl SingleFontFamily { #[cfg(feature = "servo")] #[derive( - Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, + Clone, + Debug, + Eq, + Hash, + MallocSizeOf, + PartialEq, + ToComputedValue, + ToResolvedValue, + ToShmem, + Serialize, + Deserialize, )] /// A list of SingleFontFamily pub struct FontFamilyList(Box<[SingleFontFamily]>); diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index dd9f9d3b86d..9d70648ce71 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -496,6 +496,8 @@ impl ToComputedValue for FontStretch { ToCss, ToResolvedValue, ToShmem, + Serialize, + Deserialize, )] #[allow(missing_docs)] pub enum KeywordSize { @@ -540,6 +542,8 @@ impl Default for KeywordSize { ToCss, ToResolvedValue, ToShmem, + Serialize, + Deserialize, )] /// Additional information for keyword-derived font sizes. pub struct KeywordInfo { From 34d0c313dccc7e12b4409e10ec1f7ffae63e4528 Mon Sep 17 00:00:00 2001 From: Utsav Oza Date: Sun, 7 Jun 2020 01:38:04 +0530 Subject: [PATCH 09/13] Enable textAlign, textBaseline and direction attributes for canvas --- Cargo.lock | 3 + components/canvas/Cargo.toml | 2 + components/canvas/canvas_data.rs | 153 ++++++++++++++---- components/canvas/canvas_paint_thread.rs | 25 ++- components/canvas/raqote_backend.rs | 35 ++-- components/canvas_traits/canvas.rs | 92 ++++++++++- components/script/canvas_state.rs | 98 ++++++++++- components/script/dom/bindings/trace.rs | 6 +- .../script/dom/canvasrenderingcontext2d.rs | 33 ++++ .../dom/offscreencanvasrenderingcontext2d.rs | 33 ++++ .../webidls/CanvasRenderingContext2D.webidl | 6 +- components/servo/lib.rs | 10 +- components/style/Cargo.toml | 1 + components/style/lib.rs | 2 + components/style/values/computed/font.rs | 51 ++++++ 15 files changed, 486 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d080e4a7c39..e291d5cc7ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -505,6 +505,7 @@ dependencies = [ "euclid", "fnv", "font-kit", + "gfx", "gleam 0.11.0", "half", "ipc-channel", @@ -513,6 +514,7 @@ dependencies = [ "num-traits", "pixels", "raqote", + "servo_arc", "servo_config", "sparkle", "style", @@ -5434,6 +5436,7 @@ dependencies = [ "encoding_rs", "euclid", "fallible", + "font-kit", "fxhash", "hashglobe", "html5ever", diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index 3d7fa7f38e0..7285776ddec 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -24,6 +24,7 @@ cssparser = "0.27" euclid = "0.20" font-kit = "0.7" fnv = "1.0" +gfx = { path = "../gfx" } gleam = "0.11" half = "1" ipc-channel = "0.14" @@ -32,6 +33,7 @@ lyon_geom = "0.14" num-traits = "0.2" pixels = { path = "../pixels" } raqote = { version = "0.8", features = ["text"] } +servo_arc = { path = "../servo_arc" } servo_config = { path = "../config" } sparkle = "0.1.24" style = { path = "../style" } diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index c90fca964f7..a1617be42dc 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -7,12 +7,22 @@ use crate::raqote_backend::Repetition; use canvas_traits::canvas::*; use cssparser::RGBA; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; +use euclid::point2; +use font_kit::family_name::FamilyName; +use font_kit::font::Font; +use font_kit::properties::Properties; +use font_kit::source::SystemSource; +use gfx::font::FontHandleMethods; +use gfx::font_cache_thread::FontCacheThread; +use gfx::font_context::FontContext; use ipc_channel::ipc::{IpcSender, IpcSharedMemory}; use num_traits::ToPrimitive; +use servo_arc::Arc as ServoArc; +use std::cell::RefCell; #[allow(unused_imports)] use std::marker::PhantomData; use std::mem; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use style::properties::style_structs::Font as FontStyleStruct; use webrender_api::units::RectExt as RectExt_; @@ -267,10 +277,10 @@ pub trait GenericDrawTarget { fn fill(&mut self, path: &Path, pattern: Pattern, draw_options: &DrawOptions); fn fill_text( &mut self, - text: String, - x: f64, - y: f64, - max_width: Option, + font: &Font, + point_size: f32, + text: &str, + start: Point2D, pattern: Pattern, draw_options: &DrawOptions, ); @@ -370,6 +380,21 @@ pub enum Filter { Point, } +pub(crate) type CanvasFontContext = FontContext; + +thread_local!(static FONT_CONTEXT: RefCell> = RefCell::new(None)); + +pub(crate) fn with_thread_local_font_context(canvas_data: &CanvasData, f: F) -> R +where + F: FnOnce(&mut CanvasFontContext) -> R, +{ + FONT_CONTEXT.with(|font_context| { + f(font_context.borrow_mut().get_or_insert_with(|| { + FontContext::new(canvas_data.font_cache_thread.lock().unwrap().clone()) + })) + }) +} + pub struct CanvasData<'a> { backend: Box, drawtarget: Box, @@ -382,6 +407,7 @@ pub struct CanvasData<'a> { old_image_key: Option, /// An old webrender image key that can be deleted when the current epoch ends. very_old_image_key: Option, + font_cache_thread: Mutex, _canvas_id: CanvasId, } @@ -395,6 +421,7 @@ impl<'a> CanvasData<'a> { webrender_api: Box, antialias: AntialiasMode, canvas_id: CanvasId, + font_cache_thread: FontCacheThread, ) -> CanvasData<'a> { let backend = create_backend(); let draw_target = backend.create_drawtarget(size); @@ -408,6 +435,7 @@ impl<'a> CanvasData<'a> { image_key: None, old_image_key: None, very_old_image_key: None, + font_cache_thread: Mutex::new(font_cache_thread), _canvas_id: canvas_id, } } @@ -466,30 +494,49 @@ impl<'a> CanvasData<'a> { } } - pub fn fill_text(&mut self, text: String, x: f64, y: f64, max_width: Option) { - // 1. If maxWidth was provided but is less than or equal to zero or equal to NaN, - // then return an empty array. - if max_width.map_or(false, |max_width| max_width <= 0.) { - return; - } + pub fn fill_text( + &mut self, + text: String, + x: f64, + y: f64, + _max_width: Option, + _is_rtl: bool, + ) { + // Step 2. Replace all ASCII whitespace in text with U+0020 SPACE characters. + let text = replace_ascii_whitespace(text); - // 2. Replace all ASCII whitespace in text with U+0020 SPACE characters. - let text = text - .chars() - .map(|c| match c { - ' ' | '\t' | '\n' | '\r' | '\x0C' => '\x20', - _ => c, - }) - .collect(); - - self.drawtarget.fill_text( - text, - x, - y, - max_width, - self.state.fill_style.clone(), - &self.state.draw_options, + // Step 3. Let font be the current font of target, as given by that object's font attribute. + let point_size = self + .state + .font_style + .as_ref() + .map_or(10., |style| style.font_size.size().px()); + let font_style = self.state.font_style.as_ref(); + let font = font_style.map_or_else( + || load_system_font_from_style(font_style), + |style| { + with_thread_local_font_context(&self, |font_context| { + let font_group = font_context.font_group(ServoArc::new(style.clone())); + let font = font_group.borrow_mut().first(font_context).expect(""); + let font = font.borrow_mut(); + if let Some(bytes) = font.handle.template().bytes_if_in_memory() { + Font::from_bytes(Arc::new(bytes), 0) + .unwrap_or_else(|_| load_system_font_from_style(Some(style))) + } else { + load_system_font_from_style(Some(style)) + } + }) + }, ); + let start = point2(x as f32, y as f32); + + // TODO: Process bidi text + + // Step 8. + let fill_style = self.state.fill_style.clone(); + let draw_options = &self.state.draw_options; + self.drawtarget + .fill_text(&font, point_size, &text, start, fill_style, draw_options); } pub fn fill_rect(&mut self, rect: &Rect) { @@ -1072,6 +1119,14 @@ impl<'a> CanvasData<'a> { self.state.font_style = Some(font_style) } + pub fn set_text_align(&mut self, text_align: TextAlign) { + self.state.text_align = text_align; + } + + pub fn set_text_baseline(&mut self, text_baseline: TextBaseline) { + self.state.text_baseline = text_baseline; + } + // https://html.spec.whatwg.org/multipage/#when-shadows-are-drawn fn need_to_draw_shadow(&self) -> bool { self.backend.need_to_draw_shadow(&self.state.shadow_color) && @@ -1164,6 +1219,8 @@ pub struct CanvasPaintState<'a> { pub shadow_blur: f64, pub shadow_color: Color, pub font_style: Option, + pub text_align: TextAlign, + pub text_baseline: TextBaseline, } /// It writes an image to the destination target @@ -1245,3 +1302,45 @@ impl RectExt for Rect { self.cast() } } + +fn load_system_font_from_style(font_style: Option<&FontStyleStruct>) -> Font { + let mut properties = Properties::new(); + let style = match font_style { + Some(style) => style, + None => return load_default_system_fallback_font(&properties), + }; + let family_names = style + .font_family + .families + .iter() + .map(|family_name| family_name.into()) + .collect::>(); + let properties = properties + .style(style.font_style.into()) + .weight(style.font_weight.into()) + .stretch(style.font_stretch.into()); + let font_handle = match SystemSource::new().select_best_match(&family_names, &properties) { + Ok(handle) => handle, + Err(_) => return load_default_system_fallback_font(&properties), + }; + font_handle + .load() + .unwrap_or_else(|_| load_default_system_fallback_font(&properties)) +} + +fn load_default_system_fallback_font(properties: &Properties) -> Font { + SystemSource::new() + .select_best_match(&[FamilyName::SansSerif], properties) + .unwrap() + .load() + .unwrap() +} + +fn replace_ascii_whitespace(text: String) -> String { + text.chars() + .map(|c| match c { + ' ' | '\t' | '\n' | '\r' | '\x0C' => '\x20', + _ => c, + }) + .collect() +} diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index 9d9f8350e1d..763e7d04a18 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -7,6 +7,7 @@ use canvas_traits::canvas::*; use canvas_traits::ConstellationCanvasMsg; use crossbeam_channel::{select, unbounded, Sender}; use euclid::default::Size2D; +use gfx::font_cache_thread::FontCacheThread; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use std::borrow::ToOwned; @@ -35,14 +36,19 @@ pub struct CanvasPaintThread<'a> { canvases: HashMap>, next_canvas_id: CanvasId, webrender_api: Box, + font_cache_thread: FontCacheThread, } impl<'a> CanvasPaintThread<'a> { - fn new(webrender_api: Box) -> CanvasPaintThread<'a> { + fn new( + webrender_api: Box, + font_cache_thread: FontCacheThread, + ) -> CanvasPaintThread<'a> { CanvasPaintThread { canvases: HashMap::new(), next_canvas_id: CanvasId(0), webrender_api, + font_cache_thread, } } @@ -50,6 +56,7 @@ impl<'a> CanvasPaintThread<'a> { /// communicate with it. pub fn start( webrender_api: Box, + font_cache_thread: FontCacheThread, ) -> (Sender, IpcSender) { let (ipc_sender, ipc_receiver) = ipc::channel::().unwrap(); let msg_receiver = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(ipc_receiver); @@ -57,7 +64,7 @@ impl<'a> CanvasPaintThread<'a> { thread::Builder::new() .name("CanvasThread".to_owned()) .spawn(move || { - let mut canvas_paint_thread = CanvasPaintThread::new(webrender_api); + let mut canvas_paint_thread = CanvasPaintThread::new(webrender_api, font_cache_thread); loop { select! { recv(msg_receiver) -> msg => { @@ -118,6 +125,8 @@ impl<'a> CanvasPaintThread<'a> { AntialiasMode::None }; + let font_cache_thread = self.font_cache_thread.clone(); + let canvas_id = self.next_canvas_id.clone(); self.next_canvas_id.0 += 1; @@ -126,6 +135,7 @@ impl<'a> CanvasPaintThread<'a> { self.webrender_api.clone(), antialias, canvas_id.clone(), + font_cache_thread, ); self.canvases.insert(canvas_id.clone(), canvas_data); @@ -134,9 +144,10 @@ impl<'a> CanvasPaintThread<'a> { fn process_canvas_2d_message(&mut self, message: Canvas2dMsg, canvas_id: CanvasId) { match message { - Canvas2dMsg::FillText(text, x, y, max_width, style) => { + Canvas2dMsg::FillText(text, x, y, max_width, style, is_rtl) => { self.canvas(canvas_id).set_fill_style(style); - self.canvas(canvas_id).fill_text(text, x, y, max_width); + self.canvas(canvas_id) + .fill_text(text, x, y, max_width, is_rtl); }, Canvas2dMsg::FillRect(rect, style) => { self.canvas(canvas_id).set_fill_style(style); @@ -248,6 +259,12 @@ impl<'a> CanvasPaintThread<'a> { Canvas2dMsg::SetShadowBlur(value) => self.canvas(canvas_id).set_shadow_blur(value), Canvas2dMsg::SetShadowColor(color) => self.canvas(canvas_id).set_shadow_color(color), Canvas2dMsg::SetFont(font_style) => self.canvas(canvas_id).set_font(font_style), + Canvas2dMsg::SetTextAlign(text_align) => { + self.canvas(canvas_id).set_text_align(text_align) + }, + Canvas2dMsg::SetTextBaseline(text_baseline) => { + self.canvas(canvas_id).set_text_baseline(text_baseline) + }, } } diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs index 3c30c0f1a19..5bab459b326 100644 --- a/components/canvas/raqote_backend.rs +++ b/components/canvas/raqote_backend.rs @@ -13,9 +13,7 @@ use canvas_traits::canvas::*; use cssparser::RGBA; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; use euclid::Angle; -use font_kit::family_name::FamilyName; -use font_kit::properties::Properties; -use font_kit::source::SystemSource; +use font_kit::font::Font; use lyon_geom::Arc; use raqote::PathOp; use std::marker::PhantomData; @@ -78,6 +76,9 @@ impl Backend for RaqoteBackend { } impl<'a> CanvasPaintState<'a> { + pub const HANGING_BASELINE_DEFAULT: f32 = 0.8; // fraction of ascent + pub const IDEOGRAPHIC_BASELINE_DEFAULT: f32 = 0.5; // fraction descent + pub fn new(_antialias: AntialiasMode) -> CanvasPaintState<'a> { let pattern = Pattern::Color(255, 0, 0, 0); CanvasPaintState { @@ -91,6 +92,8 @@ impl<'a> CanvasPaintState<'a> { shadow_blur: 0.0, shadow_color: Color::Raqote(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)), font_style: None, + text_align: Default::default(), + text_baseline: Default::default(), } } } @@ -520,26 +523,20 @@ impl GenericDrawTarget for raqote::DrawTarget { fn fill_text( &mut self, - text: String, - x: f64, - y: f64, - _max_width: Option, + font: &Font, + point_size: f32, + text: &str, + start: Point2D, pattern: canvas_data::Pattern, - draw_options: &DrawOptions, + options: &DrawOptions, ) { - let font = SystemSource::new() - .select_best_match(&[FamilyName::SansSerif], &Properties::new()) - .unwrap() - .load() - .unwrap(); - self.draw_text( - &font, - 24., - &text, - Point2D::new(x as f32, y as f32), + font, + point_size, + text, + start, &pattern.source(), - draw_options.as_raqote(), + options.as_raqote(), ); } diff --git a/components/canvas_traits/canvas.rs b/components/canvas_traits/canvas.rs index 1750e7d8e99..836285863e8 100644 --- a/components/canvas_traits/canvas.rs +++ b/components/canvas_traits/canvas.rs @@ -46,7 +46,7 @@ pub enum Canvas2dMsg { ClosePath, Ellipse(Point2D, f32, f32, f32, f32, f32, bool), Fill(FillOrStrokeStyle), - FillText(String, f64, f64, Option, FillOrStrokeStyle), + FillText(String, f64, f64, Option, FillOrStrokeStyle, bool), FillRect(Rect, FillOrStrokeStyle), GetImageData(Rect, Size2D, IpcBytesSender), GetTransform(IpcSender>), @@ -72,6 +72,8 @@ pub enum Canvas2dMsg { SetShadowBlur(f64), SetShadowColor(RGBA), SetFont(Font), + SetTextAlign(TextAlign), + SetTextBaseline(TextBaseline), } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -394,3 +396,91 @@ impl FromStr for CompositionOrBlending { Err(()) } } + +#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] +pub enum TextAlign { + Start, + End, + Left, + Right, + Center, +} + +impl FromStr for TextAlign { + type Err = (); + + fn from_str(string: &str) -> Result { + match string { + "start" => Ok(TextAlign::Start), + "end" => Ok(TextAlign::End), + "left" => Ok(TextAlign::Left), + "right" => Ok(TextAlign::Right), + "center" => Ok(TextAlign::Center), + _ => Err(()), + } + } +} + +impl Default for TextAlign { + fn default() -> TextAlign { + TextAlign::Start + } +} + +#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] +pub enum TextBaseline { + Top, + Hanging, + Middle, + Alphabetic, + Ideographic, + Bottom, +} + +impl FromStr for TextBaseline { + type Err = (); + + fn from_str(string: &str) -> Result { + match string { + "top" => Ok(TextBaseline::Top), + "hanging" => Ok(TextBaseline::Hanging), + "middle" => Ok(TextBaseline::Middle), + "alphabetic" => Ok(TextBaseline::Alphabetic), + "ideographic" => Ok(TextBaseline::Ideographic), + "bottom" => Ok(TextBaseline::Bottom), + _ => Err(()), + } + } +} + +impl Default for TextBaseline { + fn default() -> TextBaseline { + TextBaseline::Alphabetic + } +} + +#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] +pub enum Direction { + Ltr, + Rtl, + Inherit, +} + +impl FromStr for Direction { + type Err = (); + + fn from_str(string: &str) -> Result { + match string { + "ltr" => Ok(Direction::Ltr), + "rtl" => Ok(Direction::Rtl), + "inherit" => Ok(Direction::Inherit), + _ => Err(()), + } + } +} + +impl Default for Direction { + fn default() -> Direction { + Direction::Inherit + } +} diff --git a/components/script/canvas_state.rs b/components/script/canvas_state.rs index 9685d719157..fa6728c34e1 100644 --- a/components/script/canvas_state.rs +++ b/components/script/canvas_state.rs @@ -3,10 +3,13 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasDirection; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasFillRule; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasImageSource; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineCap; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineJoin; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasTextAlign; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasTextBaseline; use crate::dom::bindings::codegen::Bindings::ImageDataBinding::ImageDataMethods; use crate::dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern; use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; @@ -27,7 +30,7 @@ use crate::dom::offscreencanvas::{OffscreenCanvas, OffscreenCanvasContext}; use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope; use crate::dom::textmetrics::TextMetrics; use crate::unpremultiplytable::UNPREMULTIPLY_TABLE; -use canvas_traits::canvas::{Canvas2dMsg, CanvasId, CanvasMsg}; +use canvas_traits::canvas::{Canvas2dMsg, CanvasId, CanvasMsg, Direction, TextAlign, TextBaseline}; use canvas_traits::canvas::{CompositionOrBlending, FillOrStrokeStyle, FillRule}; use canvas_traits::canvas::{LineCapStyle, LineJoinStyle, LinearGradientStyle}; use canvas_traits::canvas::{RadialGradientStyle, RepetitionStyle}; @@ -91,9 +94,14 @@ pub(crate) struct CanvasContextState { shadow_blur: f64, shadow_color: RGBA, font_style: Option, + text_align: TextAlign, + text_baseline: TextBaseline, + direction: Direction, } impl CanvasContextState { + const DEFAULT_FONT_STYLE: &'static str = "10px sans-serif"; + pub(crate) fn new() -> CanvasContextState { let black = RGBA::new(0, 0, 0, 255); CanvasContextState { @@ -112,6 +120,9 @@ impl CanvasContextState { shadow_blur: 0.0, shadow_color: RGBA::transparent(), font_style: None, + text_align: Default::default(), + text_baseline: Default::default(), + direction: Default::default(), } } } @@ -995,7 +1006,7 @@ impl CanvasState { // https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext pub fn fill_text( &self, - _canvas: Option<&HTMLCanvasElement>, + canvas: Option<&HTMLCanvasElement>, text: DOMString, x: f64, y: f64, @@ -1007,9 +1018,19 @@ impl CanvasState { if max_width.map_or(false, |max_width| !max_width.is_finite() || max_width <= 0.) { return; } - + if self.state.borrow().font_style.is_none() { + self.set_font(canvas, CanvasContextState::DEFAULT_FONT_STYLE.into()) + } + let is_rtl = false; // TODO: resolve is_rtl wrt to canvas element let style = self.state.borrow().fill_style.to_fill_or_stroke_style(); - self.send_canvas_2d_msg(Canvas2dMsg::FillText(text.into(), x, y, max_width, style)); + self.send_canvas_2d_msg(Canvas2dMsg::FillText( + text.into(), + x, + y, + max_width, + style, + is_rtl, + )); } // https://html.spec.whatwg.org/multipage/#textmetrics @@ -1040,7 +1061,7 @@ impl CanvasState { // https://html.spec.whatwg.org/multipage/#dom-context-2d-font pub fn font(&self) -> DOMString { self.state.borrow().font_style.as_ref().map_or_else( - || DOMString::from("10px sans-serif"), + || CanvasContextState::DEFAULT_FONT_STYLE.into(), |style| { let mut result = String::new(); serialize_font(style, &mut result).unwrap(); @@ -1049,6 +1070,73 @@ impl CanvasState { ) } + // https://html.spec.whatwg.org/multipage/#dom-context-2d-textalign + pub fn text_align(&self) -> CanvasTextAlign { + match self.state.borrow().text_align { + TextAlign::Start => CanvasTextAlign::Start, + TextAlign::End => CanvasTextAlign::End, + TextAlign::Left => CanvasTextAlign::Left, + TextAlign::Right => CanvasTextAlign::Right, + TextAlign::Center => CanvasTextAlign::Center, + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-textalign + pub fn set_text_align(&self, value: CanvasTextAlign) { + let text_align = match value { + CanvasTextAlign::Start => TextAlign::Start, + CanvasTextAlign::End => TextAlign::End, + CanvasTextAlign::Left => TextAlign::Left, + CanvasTextAlign::Right => TextAlign::Right, + CanvasTextAlign::Center => TextAlign::Center, + }; + self.state.borrow_mut().text_align = text_align; + self.send_canvas_2d_msg(Canvas2dMsg::SetTextAlign(text_align)); + } + + pub fn text_baseline(&self) -> CanvasTextBaseline { + match self.state.borrow().text_baseline { + TextBaseline::Top => CanvasTextBaseline::Top, + TextBaseline::Hanging => CanvasTextBaseline::Hanging, + TextBaseline::Middle => CanvasTextBaseline::Middle, + TextBaseline::Alphabetic => CanvasTextBaseline::Alphabetic, + TextBaseline::Ideographic => CanvasTextBaseline::Ideographic, + TextBaseline::Bottom => CanvasTextBaseline::Bottom, + } + } + + pub fn set_text_baseline(&self, value: CanvasTextBaseline) { + let text_baseline = match value { + CanvasTextBaseline::Top => TextBaseline::Top, + CanvasTextBaseline::Hanging => TextBaseline::Hanging, + CanvasTextBaseline::Middle => TextBaseline::Middle, + CanvasTextBaseline::Alphabetic => TextBaseline::Alphabetic, + CanvasTextBaseline::Ideographic => TextBaseline::Ideographic, + CanvasTextBaseline::Bottom => TextBaseline::Bottom, + }; + self.state.borrow_mut().text_baseline = text_baseline; + self.send_canvas_2d_msg(Canvas2dMsg::SetTextBaseline(text_baseline)); + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-direction + pub fn direction(&self) -> CanvasDirection { + match self.state.borrow().direction { + Direction::Ltr => CanvasDirection::Ltr, + Direction::Rtl => CanvasDirection::Rtl, + Direction::Inherit => CanvasDirection::Inherit, + } + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-direction + pub fn set_direction(&self, value: CanvasDirection) { + let direction = match value { + CanvasDirection::Ltr => Direction::Ltr, + CanvasDirection::Rtl => Direction::Rtl, + CanvasDirection::Inherit => Direction::Inherit, + }; + self.state.borrow_mut().direction = direction; + } + // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth pub fn line_width(&self) -> f64 { self.state.borrow().line_width diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index f80a7e57c93..9518a2c0989 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -47,7 +47,10 @@ use app_units::Au; use canvas_traits::canvas::{ CanvasGradientStop, CanvasId, LinearGradientStyle, RadialGradientStyle, }; -use canvas_traits::canvas::{CompositionOrBlending, LineCapStyle, LineJoinStyle, RepetitionStyle}; +use canvas_traits::canvas::{ + CompositionOrBlending, Direction, LineCapStyle, LineJoinStyle, RepetitionStyle, TextAlign, + TextBaseline, +}; use canvas_traits::webgl::WebGLVertexArrayId; use canvas_traits::webgl::{ ActiveAttribInfo, ActiveUniformBlockInfo, ActiveUniformInfo, GlType, TexDataType, TexFormat, @@ -503,6 +506,7 @@ unsafe_no_jsmanaged_fields!(RGBA); unsafe_no_jsmanaged_fields!(StorageType); unsafe_no_jsmanaged_fields!(CanvasGradientStop, LinearGradientStyle, RadialGradientStyle); unsafe_no_jsmanaged_fields!(LineCapStyle, LineJoinStyle, CompositionOrBlending); +unsafe_no_jsmanaged_fields!(TextAlign, TextBaseline, Direction); unsafe_no_jsmanaged_fields!(RepetitionStyle); unsafe_no_jsmanaged_fields!(WebGLError, GLLimits, GlType); unsafe_no_jsmanaged_fields!(TimeProfilerChan); diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index c8838fe283e..3ebf8f130d9 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -3,11 +3,14 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::canvas_state::CanvasState; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasDirection; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasFillRule; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasImageSource; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineCap; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineJoin; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasTextAlign; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasTextBaseline; use crate::dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern; use crate::dom::bindings::error::{ErrorResult, Fallible}; use crate::dom::bindings::num::Finite; @@ -309,6 +312,36 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { .set_font(self.canvas.as_ref().map(|c| &**c), value) } + // https://html.spec.whatwg.org/multipage/#dom-context-2d-textalign + fn TextAlign(&self) -> CanvasTextAlign { + self.canvas_state.text_align() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-textalign + fn SetTextAlign(&self, value: CanvasTextAlign) { + self.canvas_state.set_text_align(value) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-textbaseline + fn TextBaseline(&self) -> CanvasTextBaseline { + self.canvas_state.text_baseline() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-textbaseline + fn SetTextBaseline(&self, value: CanvasTextBaseline) { + self.canvas_state.set_text_baseline(value) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-direction + fn Direction(&self) -> CanvasDirection { + self.canvas_state.direction() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-direction + fn SetDirection(&self, value: CanvasDirection) { + self.canvas_state.set_direction(value) + } + // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage fn DrawImage(&self, image: CanvasImageSource, dx: f64, dy: f64) -> ErrorResult { self.canvas_state diff --git a/components/script/dom/offscreencanvasrenderingcontext2d.rs b/components/script/dom/offscreencanvasrenderingcontext2d.rs index ee9c010c391..8c3deb001f4 100644 --- a/components/script/dom/offscreencanvasrenderingcontext2d.rs +++ b/components/script/dom/offscreencanvasrenderingcontext2d.rs @@ -3,10 +3,13 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::canvas_state::CanvasState; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasDirection; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasFillRule; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasImageSource; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineCap; use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasLineJoin; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasTextAlign; +use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasTextBaseline; use crate::dom::bindings::codegen::Bindings::OffscreenCanvasRenderingContext2DBinding::OffscreenCanvasRenderingContext2DMethods; use crate::dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern; use crate::dom::bindings::error::ErrorResult; @@ -269,6 +272,36 @@ impl OffscreenCanvasRenderingContext2DMethods for OffscreenCanvasRenderingContex .set_font(self.htmlcanvas.as_ref().map(|c| &**c), value) } + // https://html.spec.whatwg.org/multipage/#dom-context-2d-textalign + fn TextAlign(&self) -> CanvasTextAlign { + self.canvas_state.text_align() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-textalign + fn SetTextAlign(&self, value: CanvasTextAlign) { + self.canvas_state.set_text_align(value) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-textbaseline + fn TextBaseline(&self) -> CanvasTextBaseline { + self.canvas_state.text_baseline() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-textbaseline + fn SetTextBaseline(&self, value: CanvasTextBaseline) { + self.canvas_state.set_text_baseline(value) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-direction + fn Direction(&self) -> CanvasDirection { + self.canvas_state.direction() + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-direction + fn SetDirection(&self, value: CanvasDirection) { + self.canvas_state.set_direction(value) + } + // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth fn LineWidth(&self) -> f64 { self.canvas_state.line_width() diff --git a/components/script/dom/webidls/CanvasRenderingContext2D.webidl b/components/script/dom/webidls/CanvasRenderingContext2D.webidl index 2267984ce2f..62669f4c2ba 100644 --- a/components/script/dom/webidls/CanvasRenderingContext2D.webidl +++ b/components/script/dom/webidls/CanvasRenderingContext2D.webidl @@ -212,10 +212,10 @@ interface mixin CanvasPathDrawingStyles { interface mixin CanvasTextDrawingStyles { // text attribute DOMString font; // (default 10px sans-serif) - //attribute CanvasTextAlign textAlign; // "start", "end", "left", "right", "center" (default: "start") - //attribute CanvasTextBaseline textBaseline; // "top", "hanging", "middle", "alphabetic", + attribute CanvasTextAlign textAlign; // "start", "end", "left", "right", "center" (default: "start") + attribute CanvasTextBaseline textBaseline; // "top", "hanging", "middle", "alphabetic", // "ideographic", "bottom" (default: "alphabetic") - //attribute CanvasDirection direction; // "ltr", "rtl", "inherit" (default: "inherit") + attribute CanvasDirection direction; // "ltr", "rtl", "inherit" (default: "inherit") }; [Exposed=(PaintWorklet, Window, Worker)] diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 25fdd2261b4..58304e33600 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -879,8 +879,13 @@ fn create_constellation( Box::new(FontCacheWR(compositor_proxy.clone())), ); + let (canvas_chan, ipc_canvas_chan) = CanvasPaintThread::start( + Box::new(CanvasWebrenderApi(compositor_proxy.clone())), + font_cache_thread.clone(), + ); + let initial_state = InitialConstellationState { - compositor_proxy: compositor_proxy.clone(), + compositor_proxy, embedder_proxy, debugger_chan, devtools_chan, @@ -899,9 +904,6 @@ fn create_constellation( user_agent, }; - let (canvas_chan, ipc_canvas_chan) = - CanvasPaintThread::start(Box::new(CanvasWebrenderApi(compositor_proxy))); - let constellation_chan = Constellation::< script_layout_interface::message::Msg, layout_thread::LayoutThread, diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index bf66228c8fb..956a2c3f3d7 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -39,6 +39,7 @@ derive_more = "0.99" encoding_rs = { version = "0.8", optional = true } euclid = "0.20" fallible = { path = "../fallible" } +font-kit = "0.7" fxhash = "0.2" hashglobe = { path = "../hashglobe" } html5ever = { version = "0.25", optional = true } diff --git a/components/style/lib.rs b/components/style/lib.rs index 52b77a7507e..7ff69ed3c7b 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -40,6 +40,8 @@ extern crate debug_unreachable; extern crate derive_more; extern crate euclid; extern crate fallible; +#[cfg(feature = "servo")] +extern crate font_kit; extern crate fxhash; #[cfg(feature = "gecko")] #[macro_use] diff --git a/components/style/values/computed/font.rs b/components/style/values/computed/font.rs index cc1b7f67d79..68f60c02e14 100644 --- a/components/style/values/computed/font.rs +++ b/components/style/values/computed/font.rs @@ -22,6 +22,12 @@ use crate::values::specified::length::{FontBaseSize, NoCalcLength}; use crate::values::CSSFloat; use crate::Atom; use cssparser::{serialize_identifier, CssStringWriter, Parser}; +#[cfg(feature = "servo")] +use font_kit::family_name::FamilyName as FontKitFamilyName; +#[cfg(feature = "servo")] +use font_kit::properties::{ + Stretch as FontKitFontStretch, Style as FontKitFontStyle, Weight as FontKitFontWeight, +}; #[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use std::fmt::{self, Write}; @@ -69,6 +75,13 @@ impl ToAnimatedValue for FontWeight { } } +#[cfg(feature = "servo")] +impl From for FontKitFontWeight { + fn from(font_weight: FontWeight) -> Self { + FontKitFontWeight(font_weight.0) + } +} + #[derive( Animate, Clone, @@ -445,6 +458,26 @@ impl SingleFontFamily { } } +#[cfg(feature = "servo")] +impl From<&SingleFontFamily> for FontKitFamilyName { + fn from(font_family: &SingleFontFamily) -> Self { + match font_family { + SingleFontFamily::FamilyName(family_name) => { + FontKitFamilyName::Title(family_name.to_css_string()) + }, + SingleFontFamily::Generic(GenericFontFamily::Serif) => FontKitFamilyName::Serif, + SingleFontFamily::Generic(GenericFontFamily::SansSerif) => FontKitFamilyName::SansSerif, + SingleFontFamily::Generic(GenericFontFamily::Monospace) => FontKitFamilyName::Monospace, + SingleFontFamily::Generic(GenericFontFamily::Fantasy) => FontKitFamilyName::Fantasy, + SingleFontFamily::Generic(GenericFontFamily::Cursive) => FontKitFamilyName::Cursive, + SingleFontFamily::Generic(family_name) => { + warn!("unsupported font family name: {:?}", family_name); + FontKitFamilyName::SansSerif + }, + } + } +} + #[cfg(feature = "servo")] #[derive( Clone, @@ -943,6 +976,17 @@ impl ToCss for FontStyle { } } +#[cfg(feature = "servo")] +impl From for FontKitFontStyle { + fn from(font_style: FontStyle) -> Self { + match font_style { + FontStyle::Normal => FontKitFontStyle::Normal, + FontStyle::Italic => FontKitFontStyle::Italic, + FontStyle::Oblique(_) => FontKitFontStyle::Oblique, + } + } +} + /// A value for the font-stretch property per: /// /// https://drafts.csswg.org/css-fonts-4/#propdef-font-stretch @@ -965,6 +1009,13 @@ impl FontStretch { } } +#[cfg(feature = "servo")] +impl From for FontKitFontStretch { + fn from(stretch: FontStretch) -> Self { + FontKitFontStretch(stretch.value()) + } +} + impl ToAnimatedValue for FontStretch { type AnimatedValue = Percentage; From d1241a8d06c8e47b56866c5e7999a6c03b5e4c83 Mon Sep 17 00:00:00 2001 From: Utsav Oza Date: Tue, 9 Jun 2020 12:42:38 +0530 Subject: [PATCH 10/13] Fix ./mach build --release --with-layout-2020 --- components/canvas/canvas_data.rs | 127 ++++++++++++++++++++++----- components/canvas/raqote_backend.rs | 9 +- components/canvas_traits/canvas.rs | 4 +- components/layout_2020/query.rs | 8 ++ components/layout_thread_2020/lib.rs | 10 ++- components/script/canvas_state.rs | 10 ++- 6 files changed, 136 insertions(+), 32 deletions(-) diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index a1617be42dc..ec59074582a 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -7,9 +7,10 @@ use crate::raqote_backend::Repetition; use canvas_traits::canvas::*; use cssparser::RGBA; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; -use euclid::point2; +use euclid::{point2, vec2}; use font_kit::family_name::FamilyName; use font_kit::font::Font; +use font_kit::metrics::Metrics; use font_kit::properties::Properties; use font_kit::source::SystemSource; use gfx::font::FontHandleMethods; @@ -281,7 +282,7 @@ pub trait GenericDrawTarget { point_size: f32, text: &str, start: Point2D, - pattern: Pattern, + pattern: &Pattern, draw_options: &DrawOptions, ); fn fill_rect(&mut self, rect: &Rect, pattern: Pattern, draw_options: Option<&DrawOptions>); @@ -499,13 +500,13 @@ impl<'a> CanvasData<'a> { text: String, x: f64, y: f64, - _max_width: Option, - _is_rtl: bool, + max_width: Option, + is_rtl: bool, ) { - // Step 2. Replace all ASCII whitespace in text with U+0020 SPACE characters. + // Step 2. let text = replace_ascii_whitespace(text); - // Step 3. Let font be the current font of target, as given by that object's font attribute. + // Step 3. let point_size = self .state .font_style @@ -513,12 +514,18 @@ impl<'a> CanvasData<'a> { .map_or(10., |style| style.font_size.size().px()); let font_style = self.state.font_style.as_ref(); let font = font_style.map_or_else( - || load_system_font_from_style(font_style), + || load_system_font_from_style(None), |style| { with_thread_local_font_context(&self, |font_context| { let font_group = font_context.font_group(ServoArc::new(style.clone())); - let font = font_group.borrow_mut().first(font_context).expect(""); + let font = font_group + .borrow_mut() + .first(font_context) + .expect("couldn't find font"); let font = font.borrow_mut(); + // Retrieving bytes from font template seems to panic for some core text fonts. + // This check avoids having to obtain bytes from the font template data if they + // are not already in the memory. if let Some(bytes) = font.handle.template().bytes_if_in_memory() { Font::from_bytes(Arc::new(bytes), 0) .unwrap_or_else(|_| load_system_font_from_style(Some(style))) @@ -528,15 +535,73 @@ impl<'a> CanvasData<'a> { }) }, ); - let start = point2(x as f32, y as f32); + let font_width = font_width(&text, point_size, &font); - // TODO: Process bidi text + // Step 6. + let max_width = max_width.map(|width| width as f32); + let (width, scale_factor) = match max_width { + Some(max_width) if max_width > font_width => (max_width, 1.), + Some(max_width) => (font_width, max_width / font_width), + None => (font_width, 1.), + }; + + // Step 7. + let start = self.text_origin(x as f32, y as f32, &font.metrics(), width, is_rtl); + + // TODO: Bidi text layout + + let old_transform = self.get_transform(); + self.set_transform( + &old_transform + .pre_translate(vec2(start.x, 0.)) + .pre_scale(scale_factor, 1.) + .pre_translate(vec2(-start.x, 0.)), + ); // Step 8. - let fill_style = self.state.fill_style.clone(); - let draw_options = &self.state.draw_options; - self.drawtarget - .fill_text(&font, point_size, &text, start, fill_style, draw_options); + self.drawtarget.fill_text( + &font, + point_size, + &text, + start, + &self.state.fill_style, + &self.state.draw_options, + ); + + self.set_transform(&old_transform); + } + + fn text_origin( + &self, + x: f32, + y: f32, + metrics: &Metrics, + width: f32, + is_rtl: bool, + ) -> Point2D { + let text_align = match self.state.text_align { + TextAlign::Start if is_rtl => TextAlign::Right, + TextAlign::Start => TextAlign::Left, + TextAlign::End if is_rtl => TextAlign::Left, + TextAlign::End => TextAlign::Right, + text_align => text_align, + }; + let anchor_x = match text_align { + TextAlign::Center => -width / 2., + TextAlign::Right => -width, + _ => 0., + }; + + let anchor_y = match self.state.text_baseline { + TextBaseline::Top => metrics.ascent, + TextBaseline::Hanging => metrics.ascent * HANGING_BASELINE_DEFAULT, + TextBaseline::Ideographic => -metrics.descent * IDEOGRAPHIC_BASELINE_DEFAULT, + TextBaseline::Middle => (metrics.ascent - metrics.descent) / 2., + TextBaseline::Alphabetic => 0., + TextBaseline::Bottom => -metrics.descent, + }; + + point2(x + anchor_x, y + anchor_y) } pub fn fill_rect(&mut self, rect: &Rect) { @@ -1206,6 +1271,9 @@ impl<'a> Drop for CanvasData<'a> { } } +const HANGING_BASELINE_DEFAULT: f32 = 0.8; +const IDEOGRAPHIC_BASELINE_DEFAULT: f32 = 0.5; + #[derive(Clone)] pub struct CanvasPaintState<'a> { pub draw_options: DrawOptions, @@ -1321,19 +1389,23 @@ fn load_system_font_from_style(font_style: Option<&FontStyleStruct>) -> Font { .stretch(style.font_stretch.into()); let font_handle = match SystemSource::new().select_best_match(&family_names, &properties) { Ok(handle) => handle, - Err(_) => return load_default_system_fallback_font(&properties), + Err(e) => { + error!("error getting font handle for style {:?}: {}", style, e); + return load_default_system_fallback_font(&properties); + }, }; - font_handle - .load() - .unwrap_or_else(|_| load_default_system_fallback_font(&properties)) + font_handle.load().unwrap_or_else(|e| { + error!("error loading font for style {:?}: {}", style, e); + load_default_system_fallback_font(&properties) + }) } fn load_default_system_fallback_font(properties: &Properties) -> Font { SystemSource::new() .select_best_match(&[FamilyName::SansSerif], properties) - .unwrap() + .expect("error getting font handle for default system font") .load() - .unwrap() + .expect("error loading default system font") } fn replace_ascii_whitespace(text: String) -> String { @@ -1344,3 +1416,18 @@ fn replace_ascii_whitespace(text: String) -> String { }) .collect() } + +// TODO: This currently calculates the width using just advances and doesn't +// determine the fallback font in case a character glyph isn't found. +fn font_width(text: &str, point_size: f32, font: &Font) -> f32 { + let metrics = font.metrics(); + let mut width = 0.; + for c in text.chars() { + if let Some(glyph_id) = font.glyph_for_char(c) { + if let Ok(advance) = font.advance(glyph_id) { + width += advance.x() * point_size / metrics.units_per_em as f32; + } + } + } + width +} diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs index 5bab459b326..5f39a1260b3 100644 --- a/components/canvas/raqote_backend.rs +++ b/components/canvas/raqote_backend.rs @@ -76,9 +76,6 @@ impl Backend for RaqoteBackend { } impl<'a> CanvasPaintState<'a> { - pub const HANGING_BASELINE_DEFAULT: f32 = 0.8; // fraction of ascent - pub const IDEOGRAPHIC_BASELINE_DEFAULT: f32 = 0.5; // fraction descent - pub fn new(_antialias: AntialiasMode) -> CanvasPaintState<'a> { let pattern = Pattern::Color(255, 0, 0, 0); CanvasPaintState { @@ -92,8 +89,8 @@ impl<'a> CanvasPaintState<'a> { shadow_blur: 0.0, shadow_color: Color::Raqote(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)), font_style: None, - text_align: Default::default(), - text_baseline: Default::default(), + text_align: TextAlign::default(), + text_baseline: TextBaseline::default(), } } } @@ -527,7 +524,7 @@ impl GenericDrawTarget for raqote::DrawTarget { point_size: f32, text: &str, start: Point2D, - pattern: canvas_data::Pattern, + pattern: &canvas_data::Pattern, options: &DrawOptions, ) { self.draw_text( diff --git a/components/canvas_traits/canvas.rs b/components/canvas_traits/canvas.rs index 836285863e8..f302aaa8e3e 100644 --- a/components/canvas_traits/canvas.rs +++ b/components/canvas_traits/canvas.rs @@ -8,7 +8,7 @@ use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender, IpcSender, IpcSharedMem use serde_bytes::ByteBuf; use std::default::Default; use std::str::FromStr; -use style::properties::style_structs::Font; +use style::properties::style_structs::Font as FontStyleStruct; #[derive(Clone, Debug, Deserialize, Serialize)] pub enum FillRule { @@ -71,7 +71,7 @@ pub enum Canvas2dMsg { SetShadowOffsetY(f64), SetShadowBlur(f64), SetShadowColor(RGBA), - SetFont(Font), + SetFont(FontStyleStruct), SetTextAlign(TextAlign), SetTextBaseline(TextBaseline), } diff --git a/components/layout_2020/query.rs b/components/layout_2020/query.rs index a8a27a30faa..6df2e789e64 100644 --- a/components/layout_2020/query.rs +++ b/components/layout_2020/query.rs @@ -387,3 +387,11 @@ pub fn process_element_inner_text_query<'dom>(_node: impl LayoutNode<'dom>) -> S pub fn process_text_index_request(_node: OpaqueNode, _point: Point2D) -> TextIndexResponse { TextIndexResponse(None) } + +pub fn process_resolved_font_style_query<'dom>( + _node: impl LayoutNode<'dom>, + _property: &PropertyId, + _value: &str, +) -> Option> { + None +} diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index bdfb75a2323..4ae69e6255a 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -37,7 +37,8 @@ use layout::context::LayoutContext; use layout::display_list::{DisplayListBuilder, WebRenderImageInfo}; use layout::layout_debug; use layout::query::{ - process_content_box_request, process_content_boxes_request, LayoutRPCImpl, LayoutThreadData, + process_content_box_request, process_content_boxes_request, process_resolved_font_style_query, + LayoutRPCImpl, LayoutThreadData, }; use layout::query::{process_element_inner_text_query, process_node_geometry_request}; use layout::query::{process_node_scroll_area_request, process_node_scroll_id_request}; @@ -525,6 +526,7 @@ impl LayoutThread { scroll_id_response: None, scroll_area_response: Rect::zero(), resolved_style_response: String::new(), + resolved_font_style_response: None, offset_parent_response: OffsetParentResponse::empty(), scroll_offsets: HashMap::new(), text_index_response: TextIndexResponse(None), @@ -1209,7 +1211,11 @@ impl LayoutThread { fragment_tree, ); }, - &QueryMsg::ResolvedFontStyleQuery(_, _, _) => unimplemented!(), + &QueryMsg::ResolvedFontStyleQuery(node, ref property, ref value) => { + let node = unsafe { ServoLayoutNode::new(&node) }; + rw_data.resolved_font_style_response = + process_resolved_font_style_query(node, property, value); + }, &QueryMsg::OffsetParentQuery(node) => { rw_data.offset_parent_response = process_offset_parent_query(node); }, diff --git a/components/script/canvas_state.rs b/components/script/canvas_state.rs index fa6728c34e1..0a6fba842e4 100644 --- a/components/script/canvas_state.rs +++ b/components/script/canvas_state.rs @@ -1021,7 +1021,13 @@ impl CanvasState { if self.state.borrow().font_style.is_none() { self.set_font(canvas, CanvasContextState::DEFAULT_FONT_STYLE.into()) } - let is_rtl = false; // TODO: resolve is_rtl wrt to canvas element + + let is_rtl = match self.state.borrow().direction { + Direction::Ltr => false, + Direction::Rtl => true, + Direction::Inherit => false, // TODO: resolve direction wrt to canvas element + }; + let style = self.state.borrow().fill_style.to_fill_or_stroke_style(); self.send_canvas_2d_msg(Canvas2dMsg::FillText( text.into(), @@ -1046,7 +1052,7 @@ impl CanvasState { pub fn set_font(&self, canvas: Option<&HTMLCanvasElement>, value: DOMString) { let canvas = match canvas { Some(element) => element, - None => return, + None => return, // offscreen canvas doesn't have a placeholder canvas }; let node = canvas.upcast::(); let window = window_from_node(&*canvas); From 6c864290826f76763c2ab0d5097cf4024b0575fd Mon Sep 17 00:00:00 2001 From: Utsav Oza Date: Wed, 10 Jun 2020 23:15:00 +0530 Subject: [PATCH 11/13] Update web-platform-tests metadata --- .../2d.text.align.default.html.ini | 4 ---- .../2d.text.align.invalid.html.ini | 4 ---- .../2d.text.baseline.default.html.ini | 4 ---- .../2d.text.baseline.invalid.html.ini | 4 ---- .../text-styles/2d.text.font.default.html.ini | 4 ---- .../2d.text.font.parse.basic.html.ini | 4 ---- ...ont.parse.size.percentage.default.html.ini | 4 ---- ...d.text.font.parse.size.percentage.html.ini | 4 ---- .../2d.text.font.parse.system.html.ini | 4 ---- .../2d.text.font.relative_size.html.ini | 4 ---- .../2d.state.saverestore.font.html.ini | 4 ---- .../2d.state.saverestore.textAlign.html.ini | 4 ---- ...2d.state.saverestore.textBaseline.html.ini | 4 ---- .../text/2d.text.align.default.html.ini | 4 ---- .../text/2d.text.align.default.worker.js.ini | 4 ---- .../text/2d.text.align.invalid.html.ini | 4 ---- .../text/2d.text.align.invalid.worker.js.ini | 4 ---- .../text/2d.text.baseline.default.html.ini | 4 ---- .../2d.text.baseline.default.worker.js.ini | 4 ---- .../text/2d.text.baseline.invalid.html.ini | 4 ---- .../2d.text.baseline.invalid.worker.js.ini | 4 ---- .../text/2d.text.font.default.html.ini | 4 ---- .../text/2d.text.font.default.worker.js.ini | 4 ---- .../text/2d.text.font.parse.system.html.ini | 4 ---- .../2d.text.font.parse.system.worker.js.ini | 4 ---- .../text/2d.text.font.parse.tiny.html.ini | 4 ++++ .../2d.text.font.parse.tiny.worker.js.ini | 4 ++++ .../text/2d.text.font.relative_size.html.ini | 4 ---- .../2d.text.font.relative_size.worker.js.ini | 4 ---- .../html/dom/idlharness.https.html.ini | 24 ------------------- .../initial.reset.2dstate.html.ini | 5 ---- 31 files changed, 8 insertions(+), 137 deletions(-) delete mode 100644 tests/wpt/metadata/html/canvas/element/text-styles/2d.text.align.default.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/element/text-styles/2d.text.align.invalid.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/element/text-styles/2d.text.baseline.default.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/element/text-styles/2d.text.baseline.invalid.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.default.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.parse.basic.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.parse.size.percentage.default.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.parse.size.percentage.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.parse.system.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.relative_size.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/element/the-canvas-state/2d.state.saverestore.font.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/element/the-canvas-state/2d.state.saverestore.textAlign.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/element/the-canvas-state/2d.state.saverestore.textBaseline.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/offscreen/text/2d.text.align.default.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/offscreen/text/2d.text.align.default.worker.js.ini delete mode 100644 tests/wpt/metadata/html/canvas/offscreen/text/2d.text.align.invalid.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/offscreen/text/2d.text.align.invalid.worker.js.ini delete mode 100644 tests/wpt/metadata/html/canvas/offscreen/text/2d.text.baseline.default.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/offscreen/text/2d.text.baseline.default.worker.js.ini delete mode 100644 tests/wpt/metadata/html/canvas/offscreen/text/2d.text.baseline.invalid.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/offscreen/text/2d.text.baseline.invalid.worker.js.ini delete mode 100644 tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.default.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.default.worker.js.ini delete mode 100644 tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.parse.system.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.parse.system.worker.js.ini create mode 100644 tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.parse.tiny.html.ini create mode 100644 tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.parse.tiny.worker.js.ini delete mode 100644 tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.relative_size.html.ini delete mode 100644 tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.relative_size.worker.js.ini delete mode 100644 tests/wpt/metadata/html/semantics/embedded-content/the-canvas-element/initial.reset.2dstate.html.ini diff --git a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.align.default.html.ini b/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.align.default.html.ini deleted file mode 100644 index 2b8793d28be..00000000000 --- a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.align.default.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.align.default.html] - [Canvas test: 2d.text.align.default] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.align.invalid.html.ini b/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.align.invalid.html.ini deleted file mode 100644 index 100c6551344..00000000000 --- a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.align.invalid.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.align.invalid.html] - [Canvas test: 2d.text.align.invalid] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.baseline.default.html.ini b/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.baseline.default.html.ini deleted file mode 100644 index f09249c129c..00000000000 --- a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.baseline.default.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.baseline.default.html] - [Canvas test: 2d.text.baseline.default] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.baseline.invalid.html.ini b/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.baseline.invalid.html.ini deleted file mode 100644 index 23f93163a60..00000000000 --- a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.baseline.invalid.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.baseline.invalid.html] - [Canvas test: 2d.text.baseline.invalid] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.default.html.ini b/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.default.html.ini deleted file mode 100644 index 71a00e76d9d..00000000000 --- a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.default.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.font.default.html] - [Canvas test: 2d.text.font.default] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.parse.basic.html.ini b/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.parse.basic.html.ini deleted file mode 100644 index cbfa7e019c8..00000000000 --- a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.parse.basic.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.font.parse.basic.html] - [Canvas test: 2d.text.font.parse.basic] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.parse.size.percentage.default.html.ini b/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.parse.size.percentage.default.html.ini deleted file mode 100644 index e38e31fc416..00000000000 --- a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.parse.size.percentage.default.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.font.parse.size.percentage.default.html] - [Canvas test: 2d.text.font.parse.size.percentage.default] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.parse.size.percentage.html.ini b/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.parse.size.percentage.html.ini deleted file mode 100644 index f88a6906139..00000000000 --- a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.parse.size.percentage.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.font.parse.size.percentage.html] - [Canvas test: 2d.text.font.parse.size.percentage] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.parse.system.html.ini b/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.parse.system.html.ini deleted file mode 100644 index 4011e6441bf..00000000000 --- a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.parse.system.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.font.parse.system.html] - [System fonts must be computed to explicit values] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.relative_size.html.ini b/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.relative_size.html.ini deleted file mode 100644 index 5353ddbe2dc..00000000000 --- a/tests/wpt/metadata/html/canvas/element/text-styles/2d.text.font.relative_size.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.font.relative_size.html] - [Canvas test: 2d.text.font.relative_size] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/element/the-canvas-state/2d.state.saverestore.font.html.ini b/tests/wpt/metadata/html/canvas/element/the-canvas-state/2d.state.saverestore.font.html.ini deleted file mode 100644 index afb4feba894..00000000000 --- a/tests/wpt/metadata/html/canvas/element/the-canvas-state/2d.state.saverestore.font.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.state.saverestore.font.html] - [save()/restore() works for font] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/element/the-canvas-state/2d.state.saverestore.textAlign.html.ini b/tests/wpt/metadata/html/canvas/element/the-canvas-state/2d.state.saverestore.textAlign.html.ini deleted file mode 100644 index 0b2a17d1d9d..00000000000 --- a/tests/wpt/metadata/html/canvas/element/the-canvas-state/2d.state.saverestore.textAlign.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.state.saverestore.textAlign.html] - [save()/restore() works for textAlign] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/element/the-canvas-state/2d.state.saverestore.textBaseline.html.ini b/tests/wpt/metadata/html/canvas/element/the-canvas-state/2d.state.saverestore.textBaseline.html.ini deleted file mode 100644 index 4c754240145..00000000000 --- a/tests/wpt/metadata/html/canvas/element/the-canvas-state/2d.state.saverestore.textBaseline.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.state.saverestore.textBaseline.html] - [save()/restore() works for textBaseline] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.align.default.html.ini b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.align.default.html.ini deleted file mode 100644 index cfc8b5f9e74..00000000000 --- a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.align.default.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.align.default.html] - [OffscreenCanvas test: 2d.text.align.default] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.align.default.worker.js.ini b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.align.default.worker.js.ini deleted file mode 100644 index 38fffadbee9..00000000000 --- a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.align.default.worker.js.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.align.default.worker.html] - [2d] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.align.invalid.html.ini b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.align.invalid.html.ini deleted file mode 100644 index 1e8afd14191..00000000000 --- a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.align.invalid.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.align.invalid.html] - [OffscreenCanvas test: 2d.text.align.invalid] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.align.invalid.worker.js.ini b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.align.invalid.worker.js.ini deleted file mode 100644 index fd2751fa4fe..00000000000 --- a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.align.invalid.worker.js.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.align.invalid.worker.html] - [2d] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.baseline.default.html.ini b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.baseline.default.html.ini deleted file mode 100644 index 660b1093d74..00000000000 --- a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.baseline.default.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.baseline.default.html] - [OffscreenCanvas test: 2d.text.baseline.default] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.baseline.default.worker.js.ini b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.baseline.default.worker.js.ini deleted file mode 100644 index 15f63c7a2f4..00000000000 --- a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.baseline.default.worker.js.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.baseline.default.worker.html] - [2d] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.baseline.invalid.html.ini b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.baseline.invalid.html.ini deleted file mode 100644 index fc890c7ba66..00000000000 --- a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.baseline.invalid.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.baseline.invalid.html] - [OffscreenCanvas test: 2d.text.baseline.invalid] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.baseline.invalid.worker.js.ini b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.baseline.invalid.worker.js.ini deleted file mode 100644 index 15d3ef67d1e..00000000000 --- a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.baseline.invalid.worker.js.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.baseline.invalid.worker.html] - [2d] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.default.html.ini b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.default.html.ini deleted file mode 100644 index cbef4aaf060..00000000000 --- a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.default.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.font.default.html] - [OffscreenCanvas test: 2d.text.font.default] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.default.worker.js.ini b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.default.worker.js.ini deleted file mode 100644 index 181525db750..00000000000 --- a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.default.worker.js.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.font.default.worker.html] - [2d] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.parse.system.html.ini b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.parse.system.html.ini deleted file mode 100644 index 4011e6441bf..00000000000 --- a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.parse.system.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.font.parse.system.html] - [System fonts must be computed to explicit values] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.parse.system.worker.js.ini b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.parse.system.worker.js.ini deleted file mode 100644 index 38ba0ec527b..00000000000 --- a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.parse.system.worker.js.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.font.parse.system.worker.html] - [System fonts must be computed to explicit values] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.parse.tiny.html.ini b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.parse.tiny.html.ini new file mode 100644 index 00000000000..f69fc85b936 --- /dev/null +++ b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.parse.tiny.html.ini @@ -0,0 +1,4 @@ +[2d.text.font.parse.tiny.html] + [OffscreenCanvas test: 2d.text.font.parse.tiny] + expected: FAIL + diff --git a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.parse.tiny.worker.js.ini b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.parse.tiny.worker.js.ini new file mode 100644 index 00000000000..c2449239def --- /dev/null +++ b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.parse.tiny.worker.js.ini @@ -0,0 +1,4 @@ +[2d.text.font.parse.tiny.worker.html] + [2d] + expected: FAIL + diff --git a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.relative_size.html.ini b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.relative_size.html.ini deleted file mode 100644 index 3b0a22523af..00000000000 --- a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.relative_size.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.font.relative_size.html] - [OffscreenCanvas test: 2d.text.font.relative_size] - expected: FAIL - diff --git a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.relative_size.worker.js.ini b/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.relative_size.worker.js.ini deleted file mode 100644 index d8ba1ee8817..00000000000 --- a/tests/wpt/metadata/html/canvas/offscreen/text/2d.text.font.relative_size.worker.js.ini +++ /dev/null @@ -1,4 +0,0 @@ -[2d.text.font.relative_size.worker.html] - [2d] - expected: FAIL - diff --git a/tests/wpt/metadata/html/dom/idlharness.https.html.ini b/tests/wpt/metadata/html/dom/idlharness.https.html.ini index b0b337f71d8..bbc23fe7631 100644 --- a/tests/wpt/metadata/html/dom/idlharness.https.html.ini +++ b/tests/wpt/metadata/html/dom/idlharness.https.html.ini @@ -110,9 +110,6 @@ [History interface: window.history must inherit property "scrollRestoration" with the proper type] expected: FAIL - [CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "direction" with the proper type] - expected: FAIL - [ApplicationCache interface: constant UPDATEREADY on interface prototype object] expected: FAIL @@ -152,9 +149,6 @@ [Navigator interface: attribute hardwareConcurrency] expected: FAIL - [CanvasRenderingContext2D interface: attribute textAlign] - expected: FAIL - [OffscreenCanvasRenderingContext2D interface: operation restore()] expected: FAIL @@ -341,9 +335,6 @@ [DataTransfer interface: existence and properties of interface prototype object] expected: FAIL - [CanvasRenderingContext2D interface: attribute direction] - expected: FAIL - [DataTransferItemList interface: existence and properties of interface object] expected: FAIL @@ -419,9 +410,6 @@ [SVGElement interface: attribute onsecuritypolicyviolation] expected: FAIL - [CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "font" with the proper type] - expected: FAIL - [OffscreenCanvasRenderingContext2D interface: operation quadraticCurveTo(unrestricted double, unrestricted double, unrestricted double, unrestricted double)] expected: FAIL @@ -899,9 +887,6 @@ [ApplicationCache interface: existence and properties of interface prototype object's @@unscopables property] expected: FAIL - [CanvasRenderingContext2D interface: attribute font] - expected: FAIL - [OffscreenCanvas interface: existence and properties of interface prototype object's @@unscopables property] expected: FAIL @@ -1076,9 +1061,6 @@ [OffscreenCanvasRenderingContext2D interface: operation closePath()] expected: FAIL - [CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "textAlign" with the proper type] - expected: FAIL - [Path2D interface: operation arcTo(unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double)] expected: FAIL @@ -1136,9 +1118,6 @@ [SVGElement interface: attribute onvolumechange] expected: FAIL - [CanvasRenderingContext2D interface: attribute textBaseline] - expected: FAIL - [ImageBitmapRenderingContext interface object length] expected: FAIL @@ -1259,9 +1238,6 @@ [ElementInternals interface: attribute validity] expected: FAIL - [CanvasRenderingContext2D interface: document.createElement("canvas").getContext("2d") must inherit property "textBaseline" with the proper type] - expected: FAIL - [SVGElement interface: attribute autofocus] expected: FAIL diff --git a/tests/wpt/metadata/html/semantics/embedded-content/the-canvas-element/initial.reset.2dstate.html.ini b/tests/wpt/metadata/html/semantics/embedded-content/the-canvas-element/initial.reset.2dstate.html.ini deleted file mode 100644 index 2f98ac94dff..00000000000 --- a/tests/wpt/metadata/html/semantics/embedded-content/the-canvas-element/initial.reset.2dstate.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[initial.reset.2dstate.html] - type: testharness - [Resetting the canvas state resets 2D state variables] - expected: FAIL - From 8372cf1bd8946ccb41d6d0aa851d977e8de33b1b Mon Sep 17 00:00:00 2001 From: Utsav Oza Date: Fri, 12 Jun 2020 18:43:09 +0530 Subject: [PATCH 12/13] Derive Serialize and Deserialize traits for font styles for #[cfg_attr(feature = "servo")] --- components/canvas/canvas_data.rs | 4 +--- components/canvas/canvas_paint_thread.rs | 1 - components/style/properties/properties.mako.rs | 3 ++- components/style/values/computed/font.rs | 17 +++-------------- components/style/values/specified/font.rs | 6 ++---- 5 files changed, 8 insertions(+), 23 deletions(-) diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index ec59074582a..23f23e4252c 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -409,7 +409,6 @@ pub struct CanvasData<'a> { /// An old webrender image key that can be deleted when the current epoch ends. very_old_image_key: Option, font_cache_thread: Mutex, - _canvas_id: CanvasId, } fn create_backend() -> Box { @@ -421,7 +420,6 @@ impl<'a> CanvasData<'a> { size: Size2D, webrender_api: Box, antialias: AntialiasMode, - canvas_id: CanvasId, font_cache_thread: FontCacheThread, ) -> CanvasData<'a> { let backend = create_backend(); @@ -437,7 +435,6 @@ impl<'a> CanvasData<'a> { old_image_key: None, very_old_image_key: None, font_cache_thread: Mutex::new(font_cache_thread), - _canvas_id: canvas_id, } } @@ -495,6 +492,7 @@ impl<'a> CanvasData<'a> { } } + // https://html.spec.whatwg.org/multipage/#text-preparation-algorithm pub fn fill_text( &mut self, text: String, diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index 763e7d04a18..836a1bc6a6d 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -134,7 +134,6 @@ impl<'a> CanvasPaintThread<'a> { size, self.webrender_api.clone(), antialias, - canvas_id.clone(), font_cache_thread, ); self.canvases.insert(canvas_id.clone(), canvas_data); diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 7bf6813994b..1c51b6d55b0 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -2601,7 +2601,8 @@ pub mod style_structs { % for style_struct in data.active_style_structs(): % if style_struct.name == "Font": - #[derive(Clone, Debug, MallocSizeOf, Serialize, Deserialize)] + #[derive(Clone, Debug, MallocSizeOf)] + #[cfg_attr(feature = "servo", derive(Serialize, Deserialize))] % else: #[derive(Clone, Debug, MallocSizeOf, PartialEq)] % endif diff --git a/components/style/values/computed/font.rs b/components/style/values/computed/font.rs index 68f60c02e14..25a93d68348 100644 --- a/components/style/values/computed/font.rs +++ b/components/style/values/computed/font.rs @@ -93,9 +93,8 @@ impl From for FontKitFontWeight { ToAnimatedZero, ToCss, ToResolvedValue, - Serialize, - Deserialize, )] +#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))] /// The computed value of font-size pub struct FontSize { /// The size. @@ -478,20 +477,10 @@ impl From<&SingleFontFamily> for FontKitFamilyName { } } -#[cfg(feature = "servo")] #[derive( - Clone, - Debug, - Eq, - Hash, - MallocSizeOf, - PartialEq, - ToComputedValue, - ToResolvedValue, - ToShmem, - Serialize, - Deserialize, + Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, )] +#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))] /// A list of SingleFontFamily pub struct FontFamilyList(Box<[SingleFontFamily]>); diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index 9d70648ce71..1a77a3864ef 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -496,9 +496,8 @@ impl ToComputedValue for FontStretch { ToCss, ToResolvedValue, ToShmem, - Serialize, - Deserialize, )] +#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))] #[allow(missing_docs)] pub enum KeywordSize { #[css(keyword = "xx-small")] @@ -542,9 +541,8 @@ impl Default for KeywordSize { ToCss, ToResolvedValue, ToShmem, - Serialize, - Deserialize, )] +#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))] /// Additional information for keyword-derived font sizes. pub struct KeywordInfo { /// The keyword used From 502f34a9db36202cd89f7a1b48bd138d2ce6f46e Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Fri, 12 Jun 2020 11:51:12 -0400 Subject: [PATCH 13/13] Add canvas font test failure. --- .../element/manual/text-styles/canvas_text_font_001.htm.ini | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/wpt/metadata/html/canvas/element/manual/text-styles/canvas_text_font_001.htm.ini diff --git a/tests/wpt/metadata/html/canvas/element/manual/text-styles/canvas_text_font_001.htm.ini b/tests/wpt/metadata/html/canvas/element/manual/text-styles/canvas_text_font_001.htm.ini new file mode 100644 index 00000000000..7b5158316c0 --- /dev/null +++ b/tests/wpt/metadata/html/canvas/element/manual/text-styles/canvas_text_font_001.htm.ini @@ -0,0 +1,2 @@ +[canvas_text_font_001.htm] + expected: FAIL